├── .gitignore ├── LICENSE.md ├── README.md ├── assets └── sdf.png ├── examples ├── basic │ ├── .gitignore │ ├── blend.js │ ├── elongate.js │ ├── map.js │ ├── operations.js │ ├── package-lock.json │ ├── package.json │ └── shapes.js └── obj-export │ ├── .gitignore │ ├── export-cpu-threaded.js │ ├── export-cpu-threaded.sh │ ├── export-cpu.js │ ├── export-cpu.sh │ ├── export-gpu.js │ ├── export-gpu.sh │ ├── package-lock.json │ ├── package.json │ └── shared.js ├── lerna.json ├── package-lock.json ├── package.json └── packages ├── display-sdf ├── .gitignore ├── LICENSE.md ├── README.md ├── assets │ └── screen.png ├── display-mesh.js ├── display-raw.js ├── implicit-mesh.js ├── index.js ├── package-lock.json └── package.json ├── hiccup-sdf-to-obj ├── .gitignore ├── LICENSE.md ├── README.md ├── assets │ └── screen.png ├── gpu.js ├── gpu │ ├── electron.js │ ├── index.html │ ├── index.js │ ├── spawn.js │ └── test.js ├── index.js ├── package-lock.json ├── package.json ├── test │ ├── map-gpu.js │ ├── map.js │ ├── sphere.js │ └── union.js ├── threaded.js └── threaded │ ├── implicit-mesh.js │ └── worker.js └── hiccup-sdf ├── .gitignore ├── LICENSE.md ├── README.md ├── assets └── screen.png ├── dsl.js ├── glsl-helpers.js ├── glsl-stl.js ├── index.js ├── package-lock.json ├── package.json └── test └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | *.tgz 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Szymon Kaliski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hiccup-sdf 2 | 3 | Monorepository holding tools for modeling with signed distance functions using hiccup-like language, works both on CPU and GPU. 4 | Provides multiple primitives, basic operations, and custom `map` function for working over large amounts of data efficiently. 5 | 6 |

7 | 8 |

9 | 10 | ## Projects 11 | 12 | Each project contains detailed README file. 13 | 14 | - [`hiccup-sdf`](./packages/hiccup-sdf) - main library 15 | - [`display-sdf`](./packages/display-sdf) - utility for displaying SDFs 16 | - [`hiccup-sdf-to-obj`](./packages/hiccup-sdf-to-obj) - utility for exporting `hiccup-sdf` models to OBJs 17 | 18 | ## Examples 19 | 20 | - [`basic`](./examples/basic) - example usage of `hiccup-sdf` and `display-sdf` 21 | - [`obj-export`](./examples/obj-export) - examples of exporting `hiccup-sdf` models to OBJs 22 | 23 | ## Future Work 24 | 25 | PRs welcome! 26 | 27 | - [ ] consistent `map` function for CPU and GPU 28 | - [ ] threaded meshing for model trees containing functions 29 | - [ ] support for custom user-defined functions 30 | 31 | ## Acknowledgements 32 | 33 | This project was developed in part at Laboratory, an artist residency for interactive arts: [https://laboratoryspokane.com](https://laboratoryspokane.com). 34 | 35 | -------------------------------------------------------------------------------- /assets/sdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szymonkaliski/hiccup-sdf/715089e16fbfcfbacd85ca5249709cf6d1ca2a9e/assets/sdf.png -------------------------------------------------------------------------------- /examples/basic/.gitignore: -------------------------------------------------------------------------------- 1 | .sketchbook_cli 2 | -------------------------------------------------------------------------------- /examples/basic/blend.js: -------------------------------------------------------------------------------- 1 | const { displayRaw } = require("display-sdf"); 2 | const { compileShader, glslHelpers } = require("hiccup-sdf"); 3 | 4 | const range = length => Array.from({ length }, (_, i) => i); 5 | 6 | const steps = range(10).map(i => { 7 | return [ 8 | "translate", 9 | { t: [i / 10 - 0.5, 0, 0] }, 10 | [ 11 | [ 12 | "blend", 13 | { k: i / 10 }, 14 | [["torus", { r1: 0.01, r2: 0.03 }], ["box", { s: [0.03, 0.03, 0.03] }]] 15 | ] 16 | ] 17 | ]; 18 | }); 19 | 20 | const tree = [ 21 | "scale", 22 | { s: 2.0 }, 23 | [["rotate", { r: [0.1, 0.25, 0.5] }, [["union", steps]]]] 24 | ]; 25 | 26 | const { inject, model } = compileShader(tree); 27 | const shader = glslHelpers.createShaderFull(model, inject); 28 | 29 | displayRaw(shader); 30 | -------------------------------------------------------------------------------- /examples/basic/elongate.js: -------------------------------------------------------------------------------- 1 | const { displayRaw } = require("display-sdf"); 2 | const { compileShader, glslHelpers } = require("hiccup-sdf"); 3 | 4 | const range = length => Array.from({ length }, (_, i) => i); 5 | 6 | const steps = range(10).map(i => { 7 | return [ 8 | "translate", 9 | { t: [i / 10 - 0.5, 0, 0] }, 10 | [ 11 | [ 12 | "elongate", 13 | { s: [0.0, (i / 10) * 0.5, 0.0] }, 14 | [["torus", { r1: 0.001, r2: 0.03 }]] 15 | ] 16 | ] 17 | ]; 18 | }); 19 | 20 | const tree = [ 21 | "scale", 22 | { s: 1.5 }, 23 | [["rotate", { r: [0.1, 0, -0.25] }, [["union", steps]]]] 24 | ]; 25 | 26 | const { inject, model } = compileShader(tree); 27 | const shader = glslHelpers.createShaderFull(model, inject); 28 | 29 | displayRaw(shader); 30 | -------------------------------------------------------------------------------- /examples/basic/map.js: -------------------------------------------------------------------------------- 1 | const { displayMesh } = require("display-sdf"); 2 | const { compileShader, glslHelpers } = require("hiccup-sdf"); 3 | const SimplexNoise = require("simplex-noise"); 4 | const simplex = new SimplexNoise(); 5 | 6 | const position = []; 7 | 8 | const size = 32; 9 | 10 | for (let x = -size / 2; x < size / 2; x = ++x) { 11 | for (let z = -size / 2; z < size / 2; z = ++z) { 12 | for (let y = -size / 2; y < size / 2; y = ++y) { 13 | const nMod = 0.05; 14 | const n = simplex.noise3D(x * nMod, y * nMod, z * nMod); 15 | const d = Math.sqrt(x * x + y * y + z * z); 16 | 17 | if (n > 0.5 && d < size * 0.49) { 18 | position.push([x / size, y / size, z / size]); 19 | } 20 | } 21 | } 22 | } 23 | 24 | const tree = [ 25 | "map", 26 | { 27 | data: { position }, 28 | map: props => [ 29 | "translate", 30 | { t: `${props.position}.xyz` }, 31 | [["box", { s: [0.02, 0.02, 0.02] }]] 32 | ], 33 | reduce: ["union", { r: 0.0 }] 34 | } 35 | ]; 36 | 37 | const { inject, uniforms, model } = compileShader(tree); 38 | const shader = glslHelpers.createShaderModel(model, inject); 39 | 40 | displayMesh(shader, { textures: uniforms, size: 128, refine: false }); 41 | -------------------------------------------------------------------------------- /examples/basic/operations.js: -------------------------------------------------------------------------------- 1 | const { displayRaw } = require("display-sdf"); 2 | const { compileShader, glslHelpers } = require("hiccup-sdf"); 3 | 4 | const t = ([x, y], children) => ["translate", { t: [x, 0, y] }, [children]]; 5 | const b = ["box", { s: [0.05, 0.05, 0.05] }]; 6 | 7 | const b2 = [ 8 | t([-0.04, 0], b), 9 | t([0.04, 0], ["rotate", { r: [0, 0, 0.25] }, [b]]) 10 | ]; 11 | 12 | const shapes = [ 13 | t([-0.6, -0.6], ["union", b2]), 14 | t([-0.6, -0.3], ["union", { r: 0.05 }, b2]), 15 | t([-0.6, 0.0], ["intersection", b2]), 16 | t([-0.6, 0.3], ["difference", b2]), 17 | 18 | t([-0.2, -0.3], ["scale", { s: 0.5 }, [b]]), 19 | t([-0.2, 0.0], ["rotate", { r: [0.25, 0.25, 0] }, [b]]), 20 | t([-0.2, 0.3], ["mirror", { m: [0.075, 0, 0] }, [b]]), 21 | 22 | t([0.3, 0.0], ["repeat", { r: [0.0, 0.0, 0.25] }, [b]]), 23 | t([0.7, 0.0], ["repeatPolar", { r: 6 }, [t([0.25, 0], b)]]) 24 | ]; 25 | 26 | const tree = ["rotate", { r: [0, 0, 0.25] }, [["union", shapes]]]; 27 | 28 | const { inject, model } = compileShader(tree); 29 | const shader = glslHelpers.createShaderFull(model, inject); 30 | 31 | displayRaw(shader); 32 | -------------------------------------------------------------------------------- /examples/basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-basic", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "sketchbook-cli . --port 3000" 7 | }, 8 | "author": "Szymon Kaliski (http://szymonkaliski.com)", 9 | "license": "MIT", 10 | "dependencies": { 11 | "display-sdf": "latest", 12 | "hiccup-sdf": "0.0.9", 13 | "simplex-noise": "^2.4.0" 14 | }, 15 | "devDependencies": { 16 | "sketchbook-cli": "^1.4.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/basic/shapes.js: -------------------------------------------------------------------------------- 1 | const { displayRaw } = require("display-sdf"); 2 | const { compileShader, glslHelpers } = require("hiccup-sdf"); 3 | 4 | const t = ([x, y], children) => ["translate", { t: [x, 0, y] }, [children]]; 5 | 6 | const shapes = [ 7 | t([0, -0.2], ["box", { s: [0.05, 0.05, 0.05] }]), 8 | t([0, 0], ["sphere", { r: 0.05 }]), 9 | t([0, 0.2], ["torus", { r1: 0.025, r2: 0.05 }]), 10 | t([0.2, -0.2], ["hex", { r: 0.05, h: 0.025 }]), 11 | t([0.2, 0], ["triangle", { r: 0.05, h: 0.025 }]), 12 | t([0.2, 0.2], ["capsule", { a: [-0.025, 0, 0], b: [0.025, 0, 0], r: 0.025 }]), 13 | t([-0.2, -0.2], ["cylinder", { r: 0.05, h: 0.025 }]) 14 | ]; 15 | 16 | const tree = [ 17 | "scale", 18 | { s: 2.0 }, 19 | [["rotate", { r: [0, 0, 0.25] }, [["union", shapes]]]] 20 | ]; 21 | 22 | const { inject, model } = compileShader(tree); 23 | const shader = glslHelpers.createShaderFull(model, inject); 24 | 25 | displayRaw(shader); 26 | -------------------------------------------------------------------------------- /examples/obj-export/.gitignore: -------------------------------------------------------------------------------- 1 | *.obj 2 | -------------------------------------------------------------------------------- /examples/obj-export/export-cpu-threaded.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const sdfToObj = require("hiccup-sdf-to-obj/threaded"); 4 | 5 | const { points } = require("./shared"); 6 | 7 | // map function is currently not supported for threaded cpu export, 8 | // use gpu export, or flatten the tree: 9 | 10 | const tree = [ 11 | "difference", 12 | {}, 13 | [ 14 | ["sphere", { r: 0.4 }], 15 | [ 16 | "union", 17 | { r: 0.05 }, 18 | points.map(t => ["translate", { t }, [["sphere", { r: 0.1 }]]]) 19 | ] 20 | ] 21 | ]; 22 | 23 | sdfToObj(tree, { size: 256 }, objStr => { 24 | fs.writeFileSync(path.join(__dirname, "export-cpu-threaded.obj"), objStr); 25 | }); 26 | -------------------------------------------------------------------------------- /examples/obj-export/export-cpu-threaded.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | time node --experimental-worker ./export-cpu-threaded.js 4 | -------------------------------------------------------------------------------- /examples/obj-export/export-cpu.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const sdfToObj = require("hiccup-sdf-to-obj"); 4 | 5 | const { points } = require("./shared"); 6 | 7 | const tree = [ 8 | "difference", 9 | {}, 10 | [ 11 | ["sphere", { r: 0.4 }], 12 | [ 13 | "map", 14 | { 15 | data: { points }, 16 | map: props => [ 17 | "translate", 18 | { t: props.points }, 19 | [["sphere", { r: 0.1 }]] 20 | ], 21 | reduce: ["union", { r: 0.05 }] 22 | } 23 | ] 24 | ] 25 | ]; 26 | 27 | sdfToObj(tree, { size: 64 }, objStr => { 28 | fs.writeFileSync(path.join(__dirname, "export-cpu.obj"), objStr); 29 | }); 30 | -------------------------------------------------------------------------------- /examples/obj-export/export-cpu.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | time node ./export-cpu.js 4 | -------------------------------------------------------------------------------- /examples/obj-export/export-gpu.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const sdfToObj = require("hiccup-sdf-to-obj/gpu"); 4 | 5 | const { points } = require("./shared"); 6 | 7 | const tree = [ 8 | "difference", 9 | {}, 10 | [ 11 | ["sphere", { r: 0.4 }], 12 | [ 13 | "map", 14 | { 15 | data: { points }, 16 | map: props => [ 17 | "translate", 18 | { t: `${props.points}.xyz` }, 19 | [["sphere", { r: 0.1 }]] 20 | ], 21 | reduce: ["union", { r: 0.05 }] 22 | } 23 | ] 24 | ] 25 | ]; 26 | 27 | sdfToObj(tree, { size: 256 }, objStr => { 28 | fs.writeFileSync(path.join(__dirname, "export-gpu.obj"), objStr); 29 | }); 30 | -------------------------------------------------------------------------------- /examples/obj-export/export-gpu.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | time node ./export-gpu.js 4 | 5 | -------------------------------------------------------------------------------- /examples/obj-export/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exammple-obj", 3 | "version": "1.0.0", 4 | "private": true, 5 | "author": "Szymon Kaliski (http://szymonkaliski.com)", 6 | "license": "MIT", 7 | "dependencies": { 8 | "display-sdf": "latest", 9 | "hiccup-sdf": "latest", 10 | "hiccup-sdf-to-obj": "latest", 11 | "simplex-noise": "^2.4.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/obj-export/shared.js: -------------------------------------------------------------------------------- 1 | const randomSpherePosition = r => { 2 | const u = Math.random(); 3 | const v = Math.random(); 4 | 5 | const theta = 2 * Math.PI * u; 6 | const phi = Math.acos(2 * v - 1); 7 | 8 | const x = r * Math.sin(phi) * Math.cos(theta); 9 | const y = r * Math.sin(phi) * Math.sin(theta); 10 | const z = r * Math.cos(phi); 11 | 12 | return [x, y, z]; 13 | }; 14 | 15 | const range = length => Array.from({ length }, (_, i) => i); 16 | 17 | const points = range(100).map(() => randomSpherePosition(0.4)); 18 | 19 | module.exports = { points }; 20 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "independent" 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "bootstrap": "lerna bootstrap", 5 | "publish:init": "lerna exec 'npm publish --access public'", 6 | "publish": "lerna publish" 7 | }, 8 | "dependencies": { 9 | "lerna": "^3.4.3" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/display-sdf/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /packages/display-sdf/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Szymon Kaliski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/display-sdf/README.md: -------------------------------------------------------------------------------- 1 | # display-sdf 2 | 3 | Helper library for displaying SDFs, supports rendering "straight" from GPU, or meshing with surface-nets and displaying as a mesh. 4 | 5 |

6 | 7 |

8 | 9 | ## Installation 10 | 11 | `npm install display-sdf --save` 12 | 13 | ## Basic Usage 14 | 15 | ```js 16 | const { displayRaw } = require("display-sdf"); 17 | 18 | // generate shaderCode 19 | 20 | displayRaw(shaderCode); 21 | ``` 22 | 23 | In combination with `hiccup-sdf`: 24 | 25 | ```js 26 | const { displayRaw } = require("display-sdf"); 27 | const { compileShader, glslHelpers } = require("hiccup-sdf"); 28 | 29 | const tree = ["box"] 30 | 31 | const { inject, model } = compileShader(tree); 32 | const shader = glslHelpers.createShaderFull(model, inject); 33 | 34 | displayRaw(shader); 35 | ``` 36 | 37 | ## Meshing 38 | 39 | Sometimes working with "straight" GPU rendering is not ideal, so `display-sdf` provides an option to mesh the SDF using surface-nets, and display afterwards: 40 | 41 | ```js 42 | const { displayRaw } = require("display-sdf"); 43 | 44 | // generate shaderCode 45 | 46 | displayMesh(shader, { size: 128 }); 47 | ``` 48 | 49 | ## API 50 | 51 | All options are optional. 52 | 53 | ### `displayRaw(shaderCode, [options])` 54 | 55 | - `shaderCode` - stringified SDF GLSL code 56 | - `options.textures` - custom data textures to pass to the shader, used by `hiccup-sdf` `map` function to work over large amounts of data 57 | 58 | ### `displayMesh(shaderCode, [options])` 59 | 60 | - `shaderCode` - stringified SDF GLSL code 61 | - `options.size` - size of the surface-nets field (uniform box, so `128` becomes `[128, 128, 128]`) 62 | - `options.textures` - custom data textures to pass to the shader, used by `hiccup-sdf` `map` function to work over large amounts of data 63 | - `options.refine` - should [`refine-mesh`](https://github.com/mikolalysenko/refine-mesh) be executed after meshing, this usually makes the output look nicer (`true`/`false`) 64 | - `options.refineOptions` - [custom options to pass to `refine-mesh`](https://github.com/mikolalysenko/refine-mesh#api) 65 | 66 | ## Acknowledgements 67 | 68 | This project was developed in part at Laboratory, an artist residency for interactive arts: [https://laboratoryspokane.com](https://laboratoryspokane.com). 69 | 70 | -------------------------------------------------------------------------------- /packages/display-sdf/assets/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szymonkaliski/hiccup-sdf/715089e16fbfcfbacd85ca5249709cf6d1ca2a9e/packages/display-sdf/assets/screen.png -------------------------------------------------------------------------------- /packages/display-sdf/display-mesh.js: -------------------------------------------------------------------------------- 1 | const angleNormals = require("angle-normals"); 2 | const createCamera = require("regl-camera"); 3 | const createRegl = require("regl"); 4 | const memoSync = require("persistent-memo/memo-sync"); 5 | const refineMesh = require("refine-mesh"); 6 | 7 | const implicitMesh = require("./implicit-mesh"); 8 | 9 | module.exports = ( 10 | sdfShader, 11 | { 12 | size = 128, 13 | textures = {}, 14 | memoize = false, 15 | refine = false, 16 | refineOptions = {} 17 | } = {} 18 | ) => { 19 | const memo = memoize ? memoSync : fn => (...args) => fn(...args); 20 | 21 | const generateMesh = memo((size, sdfShader, textures) => { 22 | const makeUniforms = regl => 23 | Object.keys(textures).reduce( 24 | (memo, key) => 25 | Object.assign({}, memo, { 26 | [key]: regl.texture({ 27 | data: textures[key], 28 | type: "float" 29 | }) 30 | }), 31 | {} 32 | ); 33 | 34 | let mesh = implicitMesh(size, sdfShader, makeUniforms); 35 | mesh.normals = angleNormals(mesh.cells, mesh.positions); 36 | 37 | if (refine) { 38 | mesh = refineMesh( 39 | mesh.cells, 40 | mesh.positions, 41 | mesh.normals, 42 | refineOptions 43 | ); 44 | mesh.normals = angleNormals(mesh.cells, mesh.positions); 45 | } 46 | 47 | return mesh; 48 | }); 49 | 50 | const mesh = generateMesh(size, sdfShader, textures); 51 | 52 | const regl = createRegl(); 53 | const camera = createCamera(regl, { distance: 3 }); 54 | 55 | const vert = ` 56 | precision highp float; 57 | 58 | uniform mat4 projection, view; 59 | attribute vec3 position, normal; 60 | varying vec3 vNormal, vPosition; 61 | varying mat4 vView; 62 | 63 | void main () { 64 | vNormal = normal; 65 | vPosition = position; 66 | vView = view; 67 | 68 | gl_Position = projection * view * vec4(position, 1.0); 69 | } 70 | `; 71 | 72 | const frag = ` 73 | precision highp float; 74 | 75 | varying vec3 vNormal, vPosition; 76 | uniform vec3 lightPosition; 77 | varying mat4 vView; 78 | 79 | float lambert(vec3 lightDirection, vec3 surfaceNormal) { 80 | return max(0.1, dot(lightDirection, surfaceNormal)); 81 | } 82 | 83 | void main() { 84 | vec3 lightDirection = normalize(lightPosition - vPosition); 85 | vec3 normal = (vView * vec4(normalize(vNormal), 0.0)).xyz; 86 | float power = lambert(lightDirection, normal); 87 | 88 | gl_FragColor = vec4(power, power, power, 1.0); 89 | } 90 | `; 91 | 92 | const draw = regl({ 93 | vert, 94 | frag, 95 | attributes: { 96 | position: mesh.positions, 97 | normal: mesh.normals 98 | }, 99 | uniforms: { 100 | lightPosition: [0, 10, 10] 101 | }, 102 | elements: mesh.cells, 103 | primitive: "triangles" 104 | }); 105 | 106 | regl.frame(() => { 107 | camera(({ dirty }) => { 108 | if (!dirty) return; 109 | 110 | regl.clear({ 111 | color: [0.2, 0.2, 0.2, 1.0] 112 | }); 113 | 114 | draw(); 115 | }); 116 | }); 117 | 118 | return regl; 119 | }; 120 | -------------------------------------------------------------------------------- /packages/display-sdf/display-raw.js: -------------------------------------------------------------------------------- 1 | const regl = require("regl")({ 2 | extensions: ["OES_texture_float"] 3 | }); 4 | const camera = createCamera(regl); 5 | 6 | function createCamera(regl) { 7 | const element = regl._gl.canvas; 8 | 9 | const state = { 10 | theta: 0, 11 | phi: 0, 12 | distance: 2 13 | }; 14 | 15 | element.addEventListener("mousedown", e => { 16 | const clickX = e.clientX; 17 | const clickY = e.clientY; 18 | 19 | const startTheta = state.theta; 20 | const startPhi = state.phi; 21 | 22 | const onDrag = e => { 23 | const dx = clickX - e.clientX; 24 | const dy = clickY - e.clientY; 25 | 26 | const theta = startTheta + dx; 27 | const phi = Math.min(179.9, Math.max(-179.9, startPhi - dy)); 28 | 29 | state.theta = theta; 30 | state.phi = phi; 31 | }; 32 | 33 | const onUp = () => { 34 | element.removeEventListener("mousemove", onDrag); 35 | element.removeEventListener("mouseup", onUp); 36 | }; 37 | 38 | element.addEventListener("mousemove", onDrag); 39 | element.addEventListener("mouseup", onUp); 40 | }); 41 | 42 | const onScroll = e => { 43 | e.preventDefault(); 44 | 45 | state.distance = Math.max(0, state.distance + e.deltaY / 100); 46 | }; 47 | 48 | element.addEventListener("mousewheel", onScroll); 49 | 50 | return { state }; 51 | } 52 | 53 | module.exports = (sdfShader, { textures = {} } = {}) => { 54 | const textureUniforms = Object.keys(textures).reduce( 55 | (memo, key) => 56 | Object.assign({}, memo, { 57 | [key]: regl.texture({ 58 | data: textures[key], 59 | type: "float" 60 | }) 61 | }), 62 | {} 63 | ); 64 | 65 | const uniforms = Object.assign( 66 | { 67 | width: regl.context("viewportWidth"), 68 | height: regl.context("viewportHeight"), 69 | camTheta: regl.prop("theta"), 70 | camPhi: regl.prop("phi"), 71 | camDistance: regl.prop("distance") 72 | }, 73 | textureUniforms 74 | ); 75 | 76 | const draw = regl({ 77 | vert: ` 78 | precision highp float; 79 | attribute vec2 position; 80 | void main () { 81 | gl_Position = vec4(position, 0.0, 1.0); 82 | } 83 | `, 84 | frag: sdfShader, 85 | attributes: { 86 | position: [-4, -4, 4, -4, 0, 4] 87 | }, 88 | count: 3, 89 | depth: { 90 | enable: false 91 | }, 92 | uniforms 93 | }); 94 | 95 | regl.frame(() => { 96 | regl.clear({ 97 | color: [0.2, 0.2, 0.2, 1.0] 98 | }); 99 | 100 | draw(camera.state); 101 | }); 102 | 103 | return regl; 104 | }; 105 | -------------------------------------------------------------------------------- /packages/display-sdf/implicit-mesh.js: -------------------------------------------------------------------------------- 1 | // adapted from https://github.com/substack/implicit-mesh 2 | 3 | const createRegl = require("regl"); 4 | const ndarray = require("ndarray"); 5 | const surfaceNets = require("surface-nets"); 6 | 7 | const scale = (size, mesh) => { 8 | const sx = 2 / size[0]; 9 | const sy = 2 / size[1]; 10 | const sz = 2 / size[2]; 11 | 12 | let p = mesh.positions; 13 | 14 | for (let i = 0; i < p.length; i++) { 15 | p[i][0] = p[i][0] * sx - 1; 16 | p[i][1] = p[i][1] * sy - 1; 17 | p[i][2] = p[i][2] * sz - 1; 18 | } 19 | 20 | return mesh; 21 | }; 22 | 23 | const st = n => String(n).replace(/^(\d+)$/, "$1.0"); 24 | 25 | module.exports = (s, src, makeUniforms) => { 26 | const size = Array.isArray(s) ? s : [s, s, s]; 27 | const len = size[0] * size[1] * size[2]; 28 | 29 | const sx = st(size[0]); 30 | const sy = st(size[1]); 31 | const sz = st(size[2]); 32 | 33 | const isx = st(2 / (size[0] - 1)); 34 | const isy = st(2 / (size[1] - 1)); 35 | const isz = st(2 / (size[2] - 1)); 36 | 37 | const isxy = st(1 / (size[0] * size[1])); 38 | 39 | const sq = Math.ceil(Math.sqrt(len)); 40 | 41 | const canvas = document.createElement("canvas"); 42 | const regl = createRegl({ 43 | canvas, 44 | extensions: ["oes_texture_float"] 45 | }); 46 | 47 | const magic = { 48 | "64,64,64": (sq + size[0] * 4) * 4, 49 | "128,128,128": (sq + size[0] * 16) * 4, 50 | "100,100,100": (sq + size[0] * 10) * 4, 51 | "50,50,50": (sq + size[0] * 2 + 18) * 4 52 | }; 53 | 54 | const draw = regl({ 55 | framebuffer: regl.prop("framebuffer"), 56 | frag: ` 57 | precision highp float; 58 | 59 | ${src} 60 | 61 | float isurface(float i) { 62 | float x = mod(i, ${sx}) * ${isx} - 1.0; 63 | float y = mod(i / ${sx}, ${sy}) * ${isy} - 1.0; 64 | float z = mod(i * ${isxy}, ${sz}) * ${isz} - 1.0; 65 | 66 | return clamp(0.5 + doModel(vec3(x, y, z)).x, 0.0, 1.0); 67 | } 68 | 69 | void main() { 70 | float i = (gl_FragCoord.x + gl_FragCoord.y * ${st(sq)}) 71 | * 4.0 + ${st(magic[size] || 0)}; 72 | 73 | gl_FragColor = vec4( 74 | isurface(i + 0.0), 75 | isurface(i + 1.0), 76 | isurface(i + 2.0), 77 | isurface(i + 3.0) 78 | ); 79 | } 80 | `, 81 | vert: ` 82 | precision highp float; 83 | attribute vec2 position; 84 | 85 | void main () { 86 | gl_Position = vec4(position, 0.0, 1.0); 87 | } 88 | `, 89 | attributes: { 90 | position: [-4, 4, 4, 4, 0, -4] 91 | }, 92 | uniforms: makeUniforms ? makeUniforms(regl) : {}, 93 | count: 3, 94 | depth: { 95 | enable: false 96 | } 97 | }); 98 | 99 | regl.clear({ color: [0, 0, 0, 1], depth: true }); 100 | 101 | const framebuffer = regl.framebuffer({ 102 | width: sq, 103 | height: sq, 104 | colorFormat: "rgba", 105 | colorType: "uint8" 106 | }); 107 | 108 | let mesh = {}; 109 | 110 | draw({ framebuffer }, function() { 111 | regl.draw(); 112 | 113 | const data = regl.read(); 114 | const iv = 1 / 127.5; 115 | const ndata = new Float32Array(len); 116 | 117 | for (let i = 0; i < data.length; i++) { 118 | ndata[i] = (data[i] - 127.5) * iv; 119 | } 120 | 121 | mesh = scale(size, surfaceNets(ndarray(ndata, size))); 122 | }); 123 | 124 | regl.destroy(); 125 | 126 | return mesh; 127 | }; 128 | -------------------------------------------------------------------------------- /packages/display-sdf/index.js: -------------------------------------------------------------------------------- 1 | const displayRaw = require("./display-raw") 2 | const displayMesh = require("./display-mesh") 3 | 4 | module.exports = { displayRaw, displayMesh } 5 | -------------------------------------------------------------------------------- /packages/display-sdf/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "display-sdf", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "angle-normals": { 8 | "version": "1.0.0", 9 | "resolved": "https://registry.npmjs.org/angle-normals/-/angle-normals-1.0.0.tgz", 10 | "integrity": "sha1-lYV0mGqj8ClGpFzRCMsqO4kHyaw=" 11 | }, 12 | "bit-twiddle": { 13 | "version": "1.0.2", 14 | "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz", 15 | "integrity": "sha1-DGwfq+KyPRcXPZpht7cJPrnhdp4=" 16 | }, 17 | "cwise-compiler": { 18 | "version": "1.1.3", 19 | "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", 20 | "integrity": "sha1-9NZnQQ6FDToxOn0tt7HlBbsDTMU=", 21 | "requires": { 22 | "uniq": "^1.0.0" 23 | } 24 | }, 25 | "dup": { 26 | "version": "1.0.0", 27 | "resolved": "https://registry.npmjs.org/dup/-/dup-1.0.0.tgz", 28 | "integrity": "sha1-UfxaxoX4GWRp3wuQXpNLIK9bQCk=" 29 | }, 30 | "gamma": { 31 | "version": "0.1.0", 32 | "resolved": "https://registry.npmjs.org/gamma/-/gamma-0.1.0.tgz", 33 | "integrity": "sha1-MxVkNAO/J5BsqAqzfDbs6UQO8zA=" 34 | }, 35 | "gl-mat4": { 36 | "version": "1.2.0", 37 | "resolved": "https://registry.npmjs.org/gl-mat4/-/gl-mat4-1.2.0.tgz", 38 | "integrity": "sha512-sT5C0pwB1/e9G9AvAoLsoaJtbMGjfd/jfxo8jMCKqYYEnjZuFvqV5rehqar0538EmssjdDeiEWnKyBSTw7quoA==" 39 | }, 40 | "invert-permutation": { 41 | "version": "1.0.0", 42 | "resolved": "https://registry.npmjs.org/invert-permutation/-/invert-permutation-1.0.0.tgz", 43 | "integrity": "sha1-oKeAQurbNrwXVR54fv0UOa3VSTM=" 44 | }, 45 | "iota-array": { 46 | "version": "1.0.0", 47 | "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", 48 | "integrity": "sha1-ge9X/l0FgUzVjCSDYyqZwwoOgIc=" 49 | }, 50 | "is-buffer": { 51 | "version": "1.1.6", 52 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 53 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 54 | }, 55 | "json-fn": { 56 | "version": "1.1.1", 57 | "resolved": "https://registry.npmjs.org/json-fn/-/json-fn-1.1.1.tgz", 58 | "integrity": "sha1-QpPJGYpILWaX0zSm4yzQ0iESHoA=" 59 | }, 60 | "mouse-change": { 61 | "version": "1.4.0", 62 | "resolved": "https://registry.npmjs.org/mouse-change/-/mouse-change-1.4.0.tgz", 63 | "integrity": "sha1-wrd+W/o0pDzhRFyBV6Tk3JiVwU8=", 64 | "requires": { 65 | "mouse-event": "^1.0.0" 66 | } 67 | }, 68 | "mouse-event": { 69 | "version": "1.0.5", 70 | "resolved": "https://registry.npmjs.org/mouse-event/-/mouse-event-1.0.5.tgz", 71 | "integrity": "sha1-s3ie23EJmX1aky0dAdqhVDpQFzI=" 72 | }, 73 | "mouse-wheel": { 74 | "version": "1.2.0", 75 | "resolved": "https://registry.npmjs.org/mouse-wheel/-/mouse-wheel-1.2.0.tgz", 76 | "integrity": "sha1-bSkDseqPtI5h8bU7kDZ3PwQs21w=", 77 | "requires": { 78 | "right-now": "^1.0.0", 79 | "signum": "^1.0.0", 80 | "to-px": "^1.0.1" 81 | } 82 | }, 83 | "ndarray": { 84 | "version": "1.0.18", 85 | "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.18.tgz", 86 | "integrity": "sha1-tg06cyJOxVXQ+qeXEeUCRI/T95M=", 87 | "requires": { 88 | "iota-array": "^1.0.0", 89 | "is-buffer": "^1.0.2" 90 | } 91 | }, 92 | "ndarray-extract-contour": { 93 | "version": "1.0.1", 94 | "resolved": "https://registry.npmjs.org/ndarray-extract-contour/-/ndarray-extract-contour-1.0.1.tgz", 95 | "integrity": "sha1-Cu4ROjozsia5DEiIz4d79HUTBeQ=", 96 | "requires": { 97 | "typedarray-pool": "^1.0.0" 98 | } 99 | }, 100 | "ndarray-sort": { 101 | "version": "1.0.1", 102 | "resolved": "https://registry.npmjs.org/ndarray-sort/-/ndarray-sort-1.0.1.tgz", 103 | "integrity": "sha1-/qBbTLg0x/TgIWo1TzynUTAN/Wo=", 104 | "requires": { 105 | "typedarray-pool": "^1.0.0" 106 | } 107 | }, 108 | "next-pow-2": { 109 | "version": "1.0.0", 110 | "resolved": "https://registry.npmjs.org/next-pow-2/-/next-pow-2-1.0.0.tgz", 111 | "integrity": "sha1-y1wvHa4EDFbN1c2h3FxqOjOPQ2c=" 112 | }, 113 | "parse-unit": { 114 | "version": "1.0.1", 115 | "resolved": "https://registry.npmjs.org/parse-unit/-/parse-unit-1.0.1.tgz", 116 | "integrity": "sha1-fhu21b7zh0wo45JSaiVBFwKR7s8=" 117 | }, 118 | "permutation-parity": { 119 | "version": "1.0.0", 120 | "resolved": "https://registry.npmjs.org/permutation-parity/-/permutation-parity-1.0.0.tgz", 121 | "integrity": "sha1-AXTVH8pwSxG5pLFSsj1Tf9xrXvQ=", 122 | "requires": { 123 | "typedarray-pool": "^1.0.0" 124 | } 125 | }, 126 | "permutation-rank": { 127 | "version": "1.0.0", 128 | "resolved": "https://registry.npmjs.org/permutation-rank/-/permutation-rank-1.0.0.tgz", 129 | "integrity": "sha1-n9mLvOzwj79ZlLXq3JSmLmeUg7U=", 130 | "requires": { 131 | "invert-permutation": "^1.0.0", 132 | "typedarray-pool": "^1.0.0" 133 | } 134 | }, 135 | "persistent-memo": { 136 | "version": "0.0.1", 137 | "resolved": "https://registry.npmjs.org/persistent-memo/-/persistent-memo-0.0.1.tgz", 138 | "integrity": "sha512-6dzU8xOZzve4Wq1FV5yVlcz9pztuRFh5q2wTsA4DYWxnzAXaoKynB64krrR+z6OydFG9DngWBrUOd3Rzuiqqyg==", 139 | "requires": { 140 | "json-fn": "^1.1.1" 141 | } 142 | }, 143 | "refine-mesh": { 144 | "version": "1.0.1", 145 | "resolved": "https://registry.npmjs.org/refine-mesh/-/refine-mesh-1.0.1.tgz", 146 | "integrity": "sha1-u8FfSPevGwLAFLFrmqxipNXT4EA=", 147 | "requires": { 148 | "ndarray": "^1.0.18", 149 | "ndarray-sort": "^1.0.1", 150 | "next-pow-2": "^1.0.0", 151 | "typedarray-pool": "^1.1.0" 152 | } 153 | }, 154 | "regl": { 155 | "version": "1.3.9", 156 | "resolved": "https://registry.npmjs.org/regl/-/regl-1.3.9.tgz", 157 | "integrity": "sha512-CungQSUBsZNYZJWJlb2sPe4iwBjmxrgl1Yxt91HN3VuuEL7lJ5k03O3T1xEXVOCMN1q8wncddwJsxozuyzzmrA==" 158 | }, 159 | "regl-camera": { 160 | "version": "2.1.1", 161 | "resolved": "https://registry.npmjs.org/regl-camera/-/regl-camera-2.1.1.tgz", 162 | "integrity": "sha1-aXmm0cm4DF2Ri4CfoFdmSDzM5UY=", 163 | "requires": { 164 | "gl-mat4": "^1.1.4", 165 | "mouse-change": "^1.3.0", 166 | "mouse-wheel": "^1.2.0" 167 | } 168 | }, 169 | "right-now": { 170 | "version": "1.0.0", 171 | "resolved": "https://registry.npmjs.org/right-now/-/right-now-1.0.0.tgz", 172 | "integrity": "sha1-bolgne69fc2vja7Mmuo5z1haCRg=" 173 | }, 174 | "signum": { 175 | "version": "1.0.0", 176 | "resolved": "https://registry.npmjs.org/signum/-/signum-1.0.0.tgz", 177 | "integrity": "sha1-dKfSvyogtA66FqkrFSEk8dVZ+nc=" 178 | }, 179 | "surface-nets": { 180 | "version": "1.0.2", 181 | "resolved": "https://registry.npmjs.org/surface-nets/-/surface-nets-1.0.2.tgz", 182 | "integrity": "sha1-5DPIy7qUpydMb0yZVStGG/H8eks=", 183 | "requires": { 184 | "ndarray-extract-contour": "^1.0.0", 185 | "triangulate-hypercube": "^1.0.0", 186 | "zero-crossings": "^1.0.0" 187 | } 188 | }, 189 | "to-px": { 190 | "version": "1.0.1", 191 | "resolved": "https://registry.npmjs.org/to-px/-/to-px-1.0.1.tgz", 192 | "integrity": "sha1-W7rtXl1PdkRbzJA8KTojB90yRkY=", 193 | "requires": { 194 | "parse-unit": "^1.0.1" 195 | } 196 | }, 197 | "triangulate-hypercube": { 198 | "version": "1.0.1", 199 | "resolved": "https://registry.npmjs.org/triangulate-hypercube/-/triangulate-hypercube-1.0.1.tgz", 200 | "integrity": "sha1-2Acdsuv8/VHzCNC88qXEils20Tc=", 201 | "requires": { 202 | "gamma": "^0.1.0", 203 | "permutation-parity": "^1.0.0", 204 | "permutation-rank": "^1.0.0" 205 | } 206 | }, 207 | "typedarray-pool": { 208 | "version": "1.1.0", 209 | "resolved": "https://registry.npmjs.org/typedarray-pool/-/typedarray-pool-1.1.0.tgz", 210 | "integrity": "sha1-0RT0hIAUifU+yrXoCIqiMET0mNk=", 211 | "requires": { 212 | "bit-twiddle": "^1.0.0", 213 | "dup": "^1.0.0" 214 | } 215 | }, 216 | "uniq": { 217 | "version": "1.0.1", 218 | "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", 219 | "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" 220 | }, 221 | "zero-crossings": { 222 | "version": "1.0.1", 223 | "resolved": "https://registry.npmjs.org/zero-crossings/-/zero-crossings-1.0.1.tgz", 224 | "integrity": "sha1-xWK9MRNkPzRDokXRJAa4i2m5qf8=", 225 | "requires": { 226 | "cwise-compiler": "^1.0.0" 227 | } 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /packages/display-sdf/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "display-sdf", 3 | "version": "0.0.9", 4 | "description": "simple fullscreen SDF shader visualizer", 5 | "main": "index.js", 6 | "author": "Szymon Kaliski (http://szymonkaliski.com)", 7 | "license": "MIT", 8 | "dependencies": { 9 | "angle-normals": "^1.0.0", 10 | "ndarray": "^1.0.18", 11 | "persistent-memo": "^0.0.1", 12 | "refine-mesh": "^1.0.1", 13 | "regl": "^1.3.7", 14 | "regl-camera": "^2.1.1", 15 | "surface-nets": "^1.0.2" 16 | }, 17 | "gitHead": "e7f441600f1b472fcf0ef1396399c465ea258d0e" 18 | } 19 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .git 3 | *.obj 4 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Szymon Kaliski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/README.md: -------------------------------------------------------------------------------- 1 | # hiccup-sdf-to-obj 2 | 3 | Tools for exporting `hiccup-sdf` models to OBJs. Supports single CPU, threaded CPU and GPU mashing. Designed for offline export in node.js. 4 | 5 |

6 | 7 |

8 | 9 | ## Installation 10 | 11 | `npm install hiccup-sdf-to-obj --save` 12 | 13 | ## Usage 14 | 15 | ### CPU meshing 16 | 17 | ```js 18 | const fs = require("fs") 19 | const sdfToObj = require("hiccup-sdf-to-obj") 20 | 21 | const tree = ["sphere"]; 22 | 23 | sdfToObj(tree, obj => { 24 | fs.writeFileSync(path.join(__dirname, "sphere.obj"), obj); 25 | }); 26 | ``` 27 | 28 | `$ node export-obj.js` 29 | 30 | ### Threaded CPU meshing 31 | 32 | Uses [worker threads](https://nodejs.org/api/worker_threads.html): 33 | 34 | ```js 35 | const fs = require("fs") 36 | const sdfToObj = require("hiccup-sdf-to-obj/threaded") 37 | 38 | const tree = ["sphere"]; 39 | 40 | sdfToObj(tree, obj => { 41 | fs.writeFileSync(path.join(__dirname, "sphere.obj"), obj); 42 | }); 43 | ``` 44 | 45 | `$ node --experimental-worker export-obj.js` 46 | 47 | **Warning:** currently `map` from `hiccup-sdf` is not supported in threaded export! 48 | 49 | ### GPU meshing 50 | 51 | Uses offscreen Electron to render on GPU and mesh to OBJ. 52 | 53 | ```js 54 | const fs = require("fs") 55 | const sdfToObj = require("hiccup-sdf-to-obj/gpu") 56 | 57 | const tree = ["sphere"]; 58 | 59 | sdfToObj(tree, obj => { 60 | fs.writeFileSync(path.join(__dirname, "sphere.obj"), obj); 61 | }); 62 | ``` 63 | 64 | `$ node export-obj.js` 65 | 66 | ## API 67 | 68 | All options are optional. 69 | 70 | ### `sdfToObj(tree, [options], callback)` 71 | 72 | - `tree` - `hiccup-sdf` model tree 73 | - `options.size` - resolution for meshing, `128` is the default, for GPU meshing `256` usually gives nicer results, for single CPU `64` is recommended for speed (but ugly exports) 74 | - `options.threads` - number of threads to spawn, works only with `threaded` exports, defaults to `4` 75 | - `callback(obj)` - callback called with obj string if succeeded, otherwise `null` 76 | 77 | ## Acknowledgements 78 | 79 | This project was developed in part at Laboratory, an artist residency for interactive arts: [https://laboratoryspokane.com](https://laboratoryspokane.com). 80 | 81 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/assets/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szymonkaliski/hiccup-sdf/715089e16fbfcfbacd85ca5249709cf6d1ca2a9e/packages/hiccup-sdf-to-obj/assets/screen.png -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/gpu.js: -------------------------------------------------------------------------------- 1 | const serializeObj = require("serialize-wavefront-obj"); 2 | const spawnGPU = require("./gpu/spawn"); 3 | const { compileShader, glslHelpers } = require("hiccup-sdf"); 4 | 5 | module.exports = (tree, options, callback) => { 6 | if (!callback) { 7 | callback = options; 8 | options = { size: 128 }; 9 | } 10 | 11 | const { inject, uniforms, model } = compileShader(tree); 12 | const shader = glslHelpers.createShaderModel(model, inject); 13 | 14 | spawnGPU({ shader, uniforms }, options, data => { 15 | if (data) { 16 | const { cells, positions } = data; 17 | const str = serializeObj(cells, positions); 18 | return callback(str); 19 | } 20 | 21 | callback(); 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/gpu/electron.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const { app, BrowserWindow, ipcMain } = require("electron"); 3 | const ipc = require("node-ipc"); 4 | 5 | ipc.config.id = "child"; 6 | ipc.config.retry = 2000; 7 | ipc.config.silent = true; 8 | ipc.config.sync = true; 9 | 10 | ipc.connectTo("server", () => { 11 | ipc.of.server.on("connect", () => { 12 | app.dock.hide(); 13 | 14 | app.on("ready", () => { 15 | const win = new BrowserWindow({ 16 | show: false, 17 | webPreferences: { 18 | webgl: true, 19 | offscreen: true 20 | } 21 | }); 22 | 23 | win.loadURL("file://" + path.join(__dirname, "index.html")); 24 | 25 | ipcMain.on("mesh", (_, mesh) => { 26 | ipc.of.server.emit("mesh", mesh); 27 | }); 28 | 29 | ipc.of.server.on("close", () => { 30 | ipc.disconnect("server"); 31 | app.exit(); 32 | }); 33 | 34 | win.webContents.on("did-finish-load", () => { 35 | ipc.of.server.emit("did-finish-load"); 36 | }); 37 | 38 | ipc.of.server.on("shader", data => { 39 | win.webContents.send("shader", data); 40 | }); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/gpu/index.html: -------------------------------------------------------------------------------- 1 | 27 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/gpu/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szymonkaliski/hiccup-sdf/715089e16fbfcfbacd85ca5249709cf6d1ca2a9e/packages/hiccup-sdf-to-obj/gpu/index.js -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/gpu/spawn.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const { spawn } = require("child_process"); 3 | const ipc = require("node-ipc"); 4 | 5 | ipc.config.id = "server"; 6 | ipc.config.retry = 2000; 7 | ipc.config.silent = true; 8 | ipc.config.sync = true; 9 | 10 | module.exports = ({ shader, uniforms }, options, callback) => { 11 | ipc.serve(() => { 12 | const spawned = spawn( 13 | path.join(__dirname, "../node_modules/.bin/electron"), 14 | [path.join(__dirname, "electron.js")], 15 | { 16 | env: Object.assign( 17 | { 18 | ELECTRON_DISABLE_SECURITY_WARNINGS: true 19 | }, 20 | process.env 21 | ) 22 | } 23 | ); 24 | 25 | spawned.stderr.on("data", data => { 26 | console.log("[gpu err]", data.toString()); 27 | }); 28 | 29 | spawned.stdout.on("data", data => { 30 | console.log("[gpu]", data.toString()); 31 | }); 32 | 33 | ipc.server.on("mesh", (mesh, socket) => { 34 | ipc.server.emit(socket, "close"); 35 | ipc.server.stop(); 36 | callback(mesh); 37 | }); 38 | 39 | ipc.server.on("did-finish-load", (_, socket) => { 40 | ipc.server.emit(socket, "shader", { shader, uniforms, options }); 41 | }); 42 | }); 43 | 44 | ipc.server.start(); 45 | }; 46 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/gpu/test.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const spawnMesher = require("./spawn"); 3 | 4 | console.time("mesher"); 5 | spawnMesher(path.join(__dirname, "../objects/01.js"), (err, data) => { 6 | console.timeEnd("mesher"); 7 | 8 | // if (err) { 9 | // console.error("err", err); 10 | // } 11 | 12 | // if (data) { 13 | // console.log(data); 14 | // } 15 | }); 16 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/index.js: -------------------------------------------------------------------------------- 1 | const implicitMesh = require("implicit-mesh"); 2 | const serializeObj = require("serialize-wavefront-obj"); 3 | const { compileFunction } = require("hiccup-sdf"); 4 | 5 | module.exports = (tree, options, callback) => { 6 | if (!callback) { 7 | callback = options; 8 | options = { size: 128 }; 9 | } 10 | 11 | const compiled = compileFunction(tree); 12 | 13 | const { cells, positions } = implicitMesh(options.size, (x, y, z) => 14 | compiled([x, y, z]) 15 | ); 16 | 17 | const str = serializeObj(cells, positions); 18 | 19 | callback(str); 20 | }; 21 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hiccup-sdf-to-obj", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "8.10.37", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.37.tgz", 10 | "integrity": "sha512-Jp39foY8Euv/PG4OGPyzxis82mnjcUtXLEMA8oFMCE4ilmuJgZPdV2nZNV1moz+99EJTtcpOSgDCgATUwABKig==" 11 | }, 12 | "acorn": { 13 | "version": "5.7.3", 14 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", 15 | "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" 16 | }, 17 | "ajv": { 18 | "version": "5.5.2", 19 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", 20 | "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", 21 | "requires": { 22 | "co": "^4.6.0", 23 | "fast-deep-equal": "^1.0.0", 24 | "fast-json-stable-stringify": "^2.0.0", 25 | "json-schema-traverse": "^0.3.0" 26 | } 27 | }, 28 | "align-text": { 29 | "version": "0.1.4", 30 | "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", 31 | "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", 32 | "requires": { 33 | "kind-of": "^3.0.2", 34 | "longest": "^1.0.1", 35 | "repeat-string": "^1.5.2" 36 | } 37 | }, 38 | "amdefine": { 39 | "version": "1.0.1", 40 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", 41 | "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", 42 | "optional": true 43 | }, 44 | "ansi-regex": { 45 | "version": "2.1.1", 46 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 47 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 48 | }, 49 | "array-find-index": { 50 | "version": "1.0.2", 51 | "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", 52 | "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" 53 | }, 54 | "asn1": { 55 | "version": "0.2.4", 56 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 57 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 58 | "requires": { 59 | "safer-buffer": "~2.1.0" 60 | } 61 | }, 62 | "assert-plus": { 63 | "version": "1.0.0", 64 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 65 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 66 | }, 67 | "asynckit": { 68 | "version": "0.4.0", 69 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 70 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 71 | }, 72 | "aws-sign2": { 73 | "version": "0.7.0", 74 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 75 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 76 | }, 77 | "aws4": { 78 | "version": "1.8.0", 79 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", 80 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" 81 | }, 82 | "balanced-match": { 83 | "version": "1.0.0", 84 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 85 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 86 | }, 87 | "bcrypt-pbkdf": { 88 | "version": "1.0.2", 89 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 90 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 91 | "requires": { 92 | "tweetnacl": "^0.14.3" 93 | } 94 | }, 95 | "bit-twiddle": { 96 | "version": "1.0.2", 97 | "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz", 98 | "integrity": "sha1-DGwfq+KyPRcXPZpht7cJPrnhdp4=" 99 | }, 100 | "brace-expansion": { 101 | "version": "1.1.11", 102 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 103 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 104 | "requires": { 105 | "balanced-match": "^1.0.0", 106 | "concat-map": "0.0.1" 107 | } 108 | }, 109 | "buffer-from": { 110 | "version": "1.1.1", 111 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 112 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" 113 | }, 114 | "builtin-modules": { 115 | "version": "1.1.1", 116 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 117 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" 118 | }, 119 | "camelcase": { 120 | "version": "2.1.1", 121 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", 122 | "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" 123 | }, 124 | "camelcase-keys": { 125 | "version": "2.1.0", 126 | "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", 127 | "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", 128 | "requires": { 129 | "camelcase": "^2.0.0", 130 | "map-obj": "^1.0.0" 131 | } 132 | }, 133 | "caseless": { 134 | "version": "0.12.0", 135 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 136 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 137 | }, 138 | "center-align": { 139 | "version": "0.1.3", 140 | "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", 141 | "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", 142 | "requires": { 143 | "align-text": "^0.1.3", 144 | "lazy-cache": "^1.0.3" 145 | } 146 | }, 147 | "cliui": { 148 | "version": "2.1.0", 149 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", 150 | "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", 151 | "requires": { 152 | "center-align": "^0.1.1", 153 | "right-align": "^0.1.1", 154 | "wordwrap": "0.0.2" 155 | } 156 | }, 157 | "co": { 158 | "version": "4.6.0", 159 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 160 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" 161 | }, 162 | "code-point-at": { 163 | "version": "1.1.0", 164 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 165 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 166 | }, 167 | "combined-stream": { 168 | "version": "1.0.7", 169 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", 170 | "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", 171 | "requires": { 172 | "delayed-stream": "~1.0.0" 173 | } 174 | }, 175 | "concat-map": { 176 | "version": "0.0.1", 177 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 178 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 179 | }, 180 | "concat-stream": { 181 | "version": "1.6.2", 182 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", 183 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", 184 | "requires": { 185 | "buffer-from": "^1.0.0", 186 | "inherits": "^2.0.3", 187 | "readable-stream": "^2.2.2", 188 | "typedarray": "^0.0.6" 189 | }, 190 | "dependencies": { 191 | "isarray": { 192 | "version": "1.0.0", 193 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 194 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 195 | }, 196 | "readable-stream": { 197 | "version": "2.3.6", 198 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 199 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 200 | "requires": { 201 | "core-util-is": "~1.0.0", 202 | "inherits": "~2.0.3", 203 | "isarray": "~1.0.0", 204 | "process-nextick-args": "~2.0.0", 205 | "safe-buffer": "~5.1.1", 206 | "string_decoder": "~1.1.1", 207 | "util-deprecate": "~1.0.1" 208 | } 209 | }, 210 | "string_decoder": { 211 | "version": "1.1.1", 212 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 213 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 214 | "requires": { 215 | "safe-buffer": "~5.1.0" 216 | } 217 | } 218 | } 219 | }, 220 | "core-util-is": { 221 | "version": "1.0.2", 222 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 223 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 224 | }, 225 | "currently-unhandled": { 226 | "version": "0.4.1", 227 | "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", 228 | "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", 229 | "requires": { 230 | "array-find-index": "^1.0.1" 231 | } 232 | }, 233 | "cwise": { 234 | "version": "1.0.10", 235 | "resolved": "https://registry.npmjs.org/cwise/-/cwise-1.0.10.tgz", 236 | "integrity": "sha1-JO7mBy69/WuMb12tsXCQtkmxK+8=", 237 | "requires": { 238 | "cwise-compiler": "^1.1.1", 239 | "cwise-parser": "^1.0.0", 240 | "static-module": "^1.0.0", 241 | "uglify-js": "^2.6.0" 242 | } 243 | }, 244 | "cwise-compiler": { 245 | "version": "1.1.3", 246 | "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", 247 | "integrity": "sha1-9NZnQQ6FDToxOn0tt7HlBbsDTMU=", 248 | "requires": { 249 | "uniq": "^1.0.0" 250 | } 251 | }, 252 | "cwise-parser": { 253 | "version": "1.0.3", 254 | "resolved": "https://registry.npmjs.org/cwise-parser/-/cwise-parser-1.0.3.tgz", 255 | "integrity": "sha1-jkk8F9VPl8sDCp6YVLyGyd+zVP4=", 256 | "requires": { 257 | "esprima": "^1.0.3", 258 | "uniq": "^1.0.0" 259 | } 260 | }, 261 | "dashdash": { 262 | "version": "1.14.1", 263 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 264 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 265 | "requires": { 266 | "assert-plus": "^1.0.0" 267 | } 268 | }, 269 | "debug": { 270 | "version": "2.6.9", 271 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 272 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 273 | "requires": { 274 | "ms": "2.0.0" 275 | } 276 | }, 277 | "decamelize": { 278 | "version": "1.2.0", 279 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 280 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 281 | }, 282 | "deep-extend": { 283 | "version": "0.6.0", 284 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 285 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" 286 | }, 287 | "delayed-stream": { 288 | "version": "1.0.0", 289 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 290 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 291 | }, 292 | "dup": { 293 | "version": "1.0.0", 294 | "resolved": "https://registry.npmjs.org/dup/-/dup-1.0.0.tgz", 295 | "integrity": "sha1-UfxaxoX4GWRp3wuQXpNLIK9bQCk=" 296 | }, 297 | "duplexer2": { 298 | "version": "0.0.2", 299 | "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", 300 | "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", 301 | "requires": { 302 | "readable-stream": "~1.1.9" 303 | } 304 | }, 305 | "easy-stack": { 306 | "version": "1.0.0", 307 | "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", 308 | "integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=" 309 | }, 310 | "ecc-jsbn": { 311 | "version": "0.1.2", 312 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 313 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 314 | "requires": { 315 | "jsbn": "~0.1.0", 316 | "safer-buffer": "^2.1.0" 317 | } 318 | }, 319 | "electron": { 320 | "version": "2.0.13", 321 | "resolved": "https://registry.npmjs.org/electron/-/electron-2.0.13.tgz", 322 | "integrity": "sha512-8ouYaLsp0F4sPI7QKgJkkJhrwj1JPSnBwbz6HHA9l6u7WofEt94lV+gHw71KJrDl7UaIkFwlSjyhIjG8lIZqxw==", 323 | "requires": { 324 | "@types/node": "^8.0.24", 325 | "electron-download": "^3.0.1", 326 | "extract-zip": "^1.0.3" 327 | } 328 | }, 329 | "electron-download": { 330 | "version": "3.3.0", 331 | "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-3.3.0.tgz", 332 | "integrity": "sha1-LP1U1pZsAZxNSa1l++Zcyc3vaMg=", 333 | "requires": { 334 | "debug": "^2.2.0", 335 | "fs-extra": "^0.30.0", 336 | "home-path": "^1.0.1", 337 | "minimist": "^1.2.0", 338 | "nugget": "^2.0.0", 339 | "path-exists": "^2.1.0", 340 | "rc": "^1.1.2", 341 | "semver": "^5.3.0", 342 | "sumchecker": "^1.2.0" 343 | } 344 | }, 345 | "error-ex": { 346 | "version": "1.3.2", 347 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 348 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 349 | "requires": { 350 | "is-arrayish": "^0.2.1" 351 | } 352 | }, 353 | "es6-promise": { 354 | "version": "4.2.5", 355 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", 356 | "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==" 357 | }, 358 | "escodegen": { 359 | "version": "1.3.3", 360 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz", 361 | "integrity": "sha1-8CQBb1qI4Eb9EgBQVek5gC5sXyM=", 362 | "requires": { 363 | "esprima": "~1.1.1", 364 | "estraverse": "~1.5.0", 365 | "esutils": "~1.0.0", 366 | "source-map": "~0.1.33" 367 | }, 368 | "dependencies": { 369 | "esprima": { 370 | "version": "1.1.1", 371 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz", 372 | "integrity": "sha1-W28VR/TRAuZw4UDFCb5ncdautUk=" 373 | } 374 | } 375 | }, 376 | "esprima": { 377 | "version": "1.2.5", 378 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz", 379 | "integrity": "sha1-CZNQL+r2aBODJXVvMPmlH+7sEek=" 380 | }, 381 | "estraverse": { 382 | "version": "1.5.1", 383 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", 384 | "integrity": "sha1-hno+jlip+EYYr7bC3bzZFrfLr3E=" 385 | }, 386 | "esutils": { 387 | "version": "1.0.0", 388 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz", 389 | "integrity": "sha1-gVHTWOIMisx/t0XnRywAJf5JZXA=" 390 | }, 391 | "event-pubsub": { 392 | "version": "4.3.0", 393 | "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", 394 | "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==" 395 | }, 396 | "extend": { 397 | "version": "3.0.2", 398 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 399 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 400 | }, 401 | "extract-zip": { 402 | "version": "1.6.7", 403 | "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", 404 | "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", 405 | "requires": { 406 | "concat-stream": "1.6.2", 407 | "debug": "2.6.9", 408 | "mkdirp": "0.5.1", 409 | "yauzl": "2.4.1" 410 | } 411 | }, 412 | "extsprintf": { 413 | "version": "1.3.0", 414 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 415 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 416 | }, 417 | "falafel": { 418 | "version": "2.1.0", 419 | "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.1.0.tgz", 420 | "integrity": "sha1-lrsXdh2rqU9G0AFzizzt86Z/4Gw=", 421 | "requires": { 422 | "acorn": "^5.0.0", 423 | "foreach": "^2.0.5", 424 | "isarray": "0.0.1", 425 | "object-keys": "^1.0.6" 426 | }, 427 | "dependencies": { 428 | "object-keys": { 429 | "version": "1.0.12", 430 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", 431 | "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==" 432 | } 433 | } 434 | }, 435 | "fast-deep-equal": { 436 | "version": "1.1.0", 437 | "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", 438 | "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" 439 | }, 440 | "fast-json-stable-stringify": { 441 | "version": "2.0.0", 442 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 443 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 444 | }, 445 | "fd-slicer": { 446 | "version": "1.0.1", 447 | "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", 448 | "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", 449 | "requires": { 450 | "pend": "~1.2.0" 451 | } 452 | }, 453 | "find-up": { 454 | "version": "1.1.2", 455 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", 456 | "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", 457 | "requires": { 458 | "path-exists": "^2.0.0", 459 | "pinkie-promise": "^2.0.0" 460 | } 461 | }, 462 | "foreach": { 463 | "version": "2.0.5", 464 | "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", 465 | "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" 466 | }, 467 | "forever-agent": { 468 | "version": "0.6.1", 469 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 470 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 471 | }, 472 | "form-data": { 473 | "version": "2.3.3", 474 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 475 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 476 | "requires": { 477 | "asynckit": "^0.4.0", 478 | "combined-stream": "^1.0.6", 479 | "mime-types": "^2.1.12" 480 | } 481 | }, 482 | "fs-extra": { 483 | "version": "0.30.0", 484 | "resolved": "http://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", 485 | "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", 486 | "requires": { 487 | "graceful-fs": "^4.1.2", 488 | "jsonfile": "^2.1.0", 489 | "klaw": "^1.0.0", 490 | "path-is-absolute": "^1.0.0", 491 | "rimraf": "^2.2.8" 492 | } 493 | }, 494 | "fs.realpath": { 495 | "version": "1.0.0", 496 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 497 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 498 | }, 499 | "function-bind": { 500 | "version": "1.1.1", 501 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 502 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 503 | }, 504 | "gamma": { 505 | "version": "0.1.0", 506 | "resolved": "https://registry.npmjs.org/gamma/-/gamma-0.1.0.tgz", 507 | "integrity": "sha1-MxVkNAO/J5BsqAqzfDbs6UQO8zA=" 508 | }, 509 | "get-stdin": { 510 | "version": "4.0.1", 511 | "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", 512 | "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" 513 | }, 514 | "getpass": { 515 | "version": "0.1.7", 516 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 517 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 518 | "requires": { 519 | "assert-plus": "^1.0.0" 520 | } 521 | }, 522 | "glob": { 523 | "version": "7.1.3", 524 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 525 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 526 | "requires": { 527 | "fs.realpath": "^1.0.0", 528 | "inflight": "^1.0.4", 529 | "inherits": "2", 530 | "minimatch": "^3.0.4", 531 | "once": "^1.3.0", 532 | "path-is-absolute": "^1.0.0" 533 | } 534 | }, 535 | "graceful-fs": { 536 | "version": "4.1.15", 537 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", 538 | "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" 539 | }, 540 | "har-schema": { 541 | "version": "2.0.0", 542 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 543 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 544 | }, 545 | "har-validator": { 546 | "version": "5.1.0", 547 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", 548 | "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", 549 | "requires": { 550 | "ajv": "^5.3.0", 551 | "har-schema": "^2.0.0" 552 | } 553 | }, 554 | "has": { 555 | "version": "1.0.3", 556 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 557 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 558 | "requires": { 559 | "function-bind": "^1.1.1" 560 | } 561 | }, 562 | "home-path": { 563 | "version": "1.0.6", 564 | "resolved": "https://registry.npmjs.org/home-path/-/home-path-1.0.6.tgz", 565 | "integrity": "sha512-wo+yjrdAtoXt43Vy92a+0IPCYViiyLAHyp0QVS4xL/tfvVz5sXIW1ubLZk3nhVkD92fQpUMKX+fzMjr5F489vw==" 566 | }, 567 | "hosted-git-info": { 568 | "version": "2.7.1", 569 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", 570 | "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" 571 | }, 572 | "http-signature": { 573 | "version": "1.2.0", 574 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 575 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 576 | "requires": { 577 | "assert-plus": "^1.0.0", 578 | "jsprim": "^1.2.2", 579 | "sshpk": "^1.7.0" 580 | } 581 | }, 582 | "implicit-mesh": { 583 | "version": "1.1.1", 584 | "resolved": "https://registry.npmjs.org/implicit-mesh/-/implicit-mesh-1.1.1.tgz", 585 | "integrity": "sha1-yv7qHUF1LPwMIVxr+5bIUoGWtXo=", 586 | "requires": { 587 | "ndarray": "^1.0.18", 588 | "ndarray-fill": "^1.0.1", 589 | "regl": "^1.2.1", 590 | "surface-nets": "^1.0.2" 591 | } 592 | }, 593 | "indent-string": { 594 | "version": "2.1.0", 595 | "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", 596 | "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", 597 | "requires": { 598 | "repeating": "^2.0.0" 599 | } 600 | }, 601 | "inflight": { 602 | "version": "1.0.6", 603 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 604 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 605 | "requires": { 606 | "once": "^1.3.0", 607 | "wrappy": "1" 608 | } 609 | }, 610 | "inherits": { 611 | "version": "2.0.3", 612 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 613 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 614 | }, 615 | "ini": { 616 | "version": "1.3.5", 617 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", 618 | "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" 619 | }, 620 | "invert-permutation": { 621 | "version": "1.0.0", 622 | "resolved": "https://registry.npmjs.org/invert-permutation/-/invert-permutation-1.0.0.tgz", 623 | "integrity": "sha1-oKeAQurbNrwXVR54fv0UOa3VSTM=" 624 | }, 625 | "iota-array": { 626 | "version": "1.0.0", 627 | "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", 628 | "integrity": "sha1-ge9X/l0FgUzVjCSDYyqZwwoOgIc=" 629 | }, 630 | "is-arrayish": { 631 | "version": "0.2.1", 632 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 633 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" 634 | }, 635 | "is-buffer": { 636 | "version": "1.1.6", 637 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 638 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 639 | }, 640 | "is-builtin-module": { 641 | "version": "1.0.0", 642 | "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", 643 | "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", 644 | "requires": { 645 | "builtin-modules": "^1.0.0" 646 | } 647 | }, 648 | "is-finite": { 649 | "version": "1.0.2", 650 | "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", 651 | "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", 652 | "requires": { 653 | "number-is-nan": "^1.0.0" 654 | } 655 | }, 656 | "is-fullwidth-code-point": { 657 | "version": "1.0.0", 658 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 659 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 660 | "requires": { 661 | "number-is-nan": "^1.0.0" 662 | } 663 | }, 664 | "is-typedarray": { 665 | "version": "1.0.0", 666 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 667 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 668 | }, 669 | "is-utf8": { 670 | "version": "0.2.1", 671 | "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", 672 | "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" 673 | }, 674 | "isarray": { 675 | "version": "0.0.1", 676 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 677 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 678 | }, 679 | "isstream": { 680 | "version": "0.1.2", 681 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 682 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 683 | }, 684 | "js-message": { 685 | "version": "1.0.5", 686 | "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", 687 | "integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=" 688 | }, 689 | "js-queue": { 690 | "version": "2.0.0", 691 | "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", 692 | "integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=", 693 | "requires": { 694 | "easy-stack": "^1.0.0" 695 | } 696 | }, 697 | "jsbn": { 698 | "version": "0.1.1", 699 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 700 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 701 | }, 702 | "json-schema": { 703 | "version": "0.2.3", 704 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 705 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 706 | }, 707 | "json-schema-traverse": { 708 | "version": "0.3.1", 709 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", 710 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" 711 | }, 712 | "json-stringify-safe": { 713 | "version": "5.0.1", 714 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 715 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 716 | }, 717 | "jsonfile": { 718 | "version": "2.4.0", 719 | "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", 720 | "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", 721 | "requires": { 722 | "graceful-fs": "^4.1.6" 723 | } 724 | }, 725 | "jsprim": { 726 | "version": "1.4.1", 727 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 728 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 729 | "requires": { 730 | "assert-plus": "1.0.0", 731 | "extsprintf": "1.3.0", 732 | "json-schema": "0.2.3", 733 | "verror": "1.10.0" 734 | } 735 | }, 736 | "kind-of": { 737 | "version": "3.2.2", 738 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 739 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 740 | "requires": { 741 | "is-buffer": "^1.1.5" 742 | } 743 | }, 744 | "klaw": { 745 | "version": "1.3.1", 746 | "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", 747 | "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", 748 | "requires": { 749 | "graceful-fs": "^4.1.9" 750 | } 751 | }, 752 | "lazy-cache": { 753 | "version": "1.0.4", 754 | "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", 755 | "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" 756 | }, 757 | "load-json-file": { 758 | "version": "1.1.0", 759 | "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", 760 | "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", 761 | "requires": { 762 | "graceful-fs": "^4.1.2", 763 | "parse-json": "^2.2.0", 764 | "pify": "^2.0.0", 765 | "pinkie-promise": "^2.0.0", 766 | "strip-bom": "^2.0.0" 767 | } 768 | }, 769 | "longest": { 770 | "version": "1.0.1", 771 | "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", 772 | "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" 773 | }, 774 | "loud-rejection": { 775 | "version": "1.6.0", 776 | "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", 777 | "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", 778 | "requires": { 779 | "currently-unhandled": "^0.4.1", 780 | "signal-exit": "^3.0.0" 781 | } 782 | }, 783 | "map-obj": { 784 | "version": "1.0.1", 785 | "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", 786 | "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" 787 | }, 788 | "meow": { 789 | "version": "3.7.0", 790 | "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", 791 | "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", 792 | "requires": { 793 | "camelcase-keys": "^2.0.0", 794 | "decamelize": "^1.1.2", 795 | "loud-rejection": "^1.0.0", 796 | "map-obj": "^1.0.1", 797 | "minimist": "^1.1.3", 798 | "normalize-package-data": "^2.3.4", 799 | "object-assign": "^4.0.1", 800 | "read-pkg-up": "^1.0.1", 801 | "redent": "^1.0.0", 802 | "trim-newlines": "^1.0.0" 803 | } 804 | }, 805 | "mime-db": { 806 | "version": "1.37.0", 807 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", 808 | "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" 809 | }, 810 | "mime-types": { 811 | "version": "2.1.21", 812 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", 813 | "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", 814 | "requires": { 815 | "mime-db": "~1.37.0" 816 | } 817 | }, 818 | "minimatch": { 819 | "version": "3.0.4", 820 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 821 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 822 | "requires": { 823 | "brace-expansion": "^1.1.7" 824 | } 825 | }, 826 | "minimist": { 827 | "version": "1.2.0", 828 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 829 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 830 | }, 831 | "mkdirp": { 832 | "version": "0.5.1", 833 | "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 834 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 835 | "requires": { 836 | "minimist": "0.0.8" 837 | }, 838 | "dependencies": { 839 | "minimist": { 840 | "version": "0.0.8", 841 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 842 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 843 | } 844 | } 845 | }, 846 | "ms": { 847 | "version": "2.0.0", 848 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 849 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 850 | }, 851 | "ndarray": { 852 | "version": "1.0.18", 853 | "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.18.tgz", 854 | "integrity": "sha1-tg06cyJOxVXQ+qeXEeUCRI/T95M=", 855 | "requires": { 856 | "iota-array": "^1.0.0", 857 | "is-buffer": "^1.0.2" 858 | } 859 | }, 860 | "ndarray-extract-contour": { 861 | "version": "1.0.1", 862 | "resolved": "https://registry.npmjs.org/ndarray-extract-contour/-/ndarray-extract-contour-1.0.1.tgz", 863 | "integrity": "sha1-Cu4ROjozsia5DEiIz4d79HUTBeQ=", 864 | "requires": { 865 | "typedarray-pool": "^1.0.0" 866 | } 867 | }, 868 | "ndarray-fill": { 869 | "version": "1.0.2", 870 | "resolved": "https://registry.npmjs.org/ndarray-fill/-/ndarray-fill-1.0.2.tgz", 871 | "integrity": "sha1-owpg9xiODJWC/N1YiWrNy1IqHtY=", 872 | "requires": { 873 | "cwise": "^1.0.10" 874 | } 875 | }, 876 | "node-ipc": { 877 | "version": "9.1.1", 878 | "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz", 879 | "integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==", 880 | "requires": { 881 | "event-pubsub": "4.3.0", 882 | "js-message": "1.0.5", 883 | "js-queue": "2.0.0" 884 | } 885 | }, 886 | "normalize-package-data": { 887 | "version": "2.4.0", 888 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", 889 | "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", 890 | "requires": { 891 | "hosted-git-info": "^2.1.4", 892 | "is-builtin-module": "^1.0.0", 893 | "semver": "2 || 3 || 4 || 5", 894 | "validate-npm-package-license": "^3.0.1" 895 | } 896 | }, 897 | "nugget": { 898 | "version": "2.0.1", 899 | "resolved": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz", 900 | "integrity": "sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA=", 901 | "requires": { 902 | "debug": "^2.1.3", 903 | "minimist": "^1.1.0", 904 | "pretty-bytes": "^1.0.2", 905 | "progress-stream": "^1.1.0", 906 | "request": "^2.45.0", 907 | "single-line-log": "^1.1.2", 908 | "throttleit": "0.0.2" 909 | } 910 | }, 911 | "number-is-nan": { 912 | "version": "1.0.1", 913 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 914 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 915 | }, 916 | "oauth-sign": { 917 | "version": "0.9.0", 918 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 919 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 920 | }, 921 | "object-assign": { 922 | "version": "4.1.1", 923 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 924 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 925 | }, 926 | "object-inspect": { 927 | "version": "0.4.0", 928 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-0.4.0.tgz", 929 | "integrity": "sha1-9RV8EWwUVbJDsG7pdwM5LFrYn+w=" 930 | }, 931 | "object-keys": { 932 | "version": "0.4.0", 933 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", 934 | "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" 935 | }, 936 | "once": { 937 | "version": "1.4.0", 938 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 939 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 940 | "requires": { 941 | "wrappy": "1" 942 | } 943 | }, 944 | "parse-json": { 945 | "version": "2.2.0", 946 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", 947 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", 948 | "requires": { 949 | "error-ex": "^1.2.0" 950 | } 951 | }, 952 | "path-exists": { 953 | "version": "2.1.0", 954 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", 955 | "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", 956 | "requires": { 957 | "pinkie-promise": "^2.0.0" 958 | } 959 | }, 960 | "path-is-absolute": { 961 | "version": "1.0.1", 962 | "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 963 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 964 | }, 965 | "path-type": { 966 | "version": "1.1.0", 967 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", 968 | "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", 969 | "requires": { 970 | "graceful-fs": "^4.1.2", 971 | "pify": "^2.0.0", 972 | "pinkie-promise": "^2.0.0" 973 | } 974 | }, 975 | "pend": { 976 | "version": "1.2.0", 977 | "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", 978 | "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" 979 | }, 980 | "performance-now": { 981 | "version": "2.1.0", 982 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 983 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 984 | }, 985 | "permutation-parity": { 986 | "version": "1.0.0", 987 | "resolved": "https://registry.npmjs.org/permutation-parity/-/permutation-parity-1.0.0.tgz", 988 | "integrity": "sha1-AXTVH8pwSxG5pLFSsj1Tf9xrXvQ=", 989 | "requires": { 990 | "typedarray-pool": "^1.0.0" 991 | } 992 | }, 993 | "permutation-rank": { 994 | "version": "1.0.0", 995 | "resolved": "https://registry.npmjs.org/permutation-rank/-/permutation-rank-1.0.0.tgz", 996 | "integrity": "sha1-n9mLvOzwj79ZlLXq3JSmLmeUg7U=", 997 | "requires": { 998 | "invert-permutation": "^1.0.0", 999 | "typedarray-pool": "^1.0.0" 1000 | } 1001 | }, 1002 | "pify": { 1003 | "version": "2.3.0", 1004 | "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1005 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" 1006 | }, 1007 | "pinkie": { 1008 | "version": "2.0.4", 1009 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 1010 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" 1011 | }, 1012 | "pinkie-promise": { 1013 | "version": "2.0.1", 1014 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 1015 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 1016 | "requires": { 1017 | "pinkie": "^2.0.0" 1018 | } 1019 | }, 1020 | "pretty-bytes": { 1021 | "version": "1.0.4", 1022 | "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", 1023 | "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", 1024 | "requires": { 1025 | "get-stdin": "^4.0.1", 1026 | "meow": "^3.1.0" 1027 | } 1028 | }, 1029 | "process-nextick-args": { 1030 | "version": "2.0.0", 1031 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 1032 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" 1033 | }, 1034 | "progress-stream": { 1035 | "version": "1.2.0", 1036 | "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-1.2.0.tgz", 1037 | "integrity": "sha1-LNPP6jO6OonJwSHsM0er6asSX3c=", 1038 | "requires": { 1039 | "speedometer": "~0.1.2", 1040 | "through2": "~0.2.3" 1041 | } 1042 | }, 1043 | "psl": { 1044 | "version": "1.1.29", 1045 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", 1046 | "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" 1047 | }, 1048 | "punycode": { 1049 | "version": "1.4.1", 1050 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 1051 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 1052 | }, 1053 | "qs": { 1054 | "version": "6.5.2", 1055 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 1056 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 1057 | }, 1058 | "quote-stream": { 1059 | "version": "0.0.0", 1060 | "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-0.0.0.tgz", 1061 | "integrity": "sha1-zeKelMQJsW4Z3HCYuJtmWPlyHTs=", 1062 | "requires": { 1063 | "minimist": "0.0.8", 1064 | "through2": "~0.4.1" 1065 | }, 1066 | "dependencies": { 1067 | "minimist": { 1068 | "version": "0.0.8", 1069 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 1070 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 1071 | }, 1072 | "readable-stream": { 1073 | "version": "1.0.34", 1074 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 1075 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", 1076 | "requires": { 1077 | "core-util-is": "~1.0.0", 1078 | "inherits": "~2.0.1", 1079 | "isarray": "0.0.1", 1080 | "string_decoder": "~0.10.x" 1081 | } 1082 | }, 1083 | "through2": { 1084 | "version": "0.4.2", 1085 | "resolved": "http://registry.npmjs.org/through2/-/through2-0.4.2.tgz", 1086 | "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", 1087 | "requires": { 1088 | "readable-stream": "~1.0.17", 1089 | "xtend": "~2.1.1" 1090 | } 1091 | } 1092 | } 1093 | }, 1094 | "rc": { 1095 | "version": "1.2.8", 1096 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 1097 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 1098 | "requires": { 1099 | "deep-extend": "^0.6.0", 1100 | "ini": "~1.3.0", 1101 | "minimist": "^1.2.0", 1102 | "strip-json-comments": "~2.0.1" 1103 | } 1104 | }, 1105 | "read-pkg": { 1106 | "version": "1.1.0", 1107 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", 1108 | "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", 1109 | "requires": { 1110 | "load-json-file": "^1.0.0", 1111 | "normalize-package-data": "^2.3.2", 1112 | "path-type": "^1.0.0" 1113 | } 1114 | }, 1115 | "read-pkg-up": { 1116 | "version": "1.0.1", 1117 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", 1118 | "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", 1119 | "requires": { 1120 | "find-up": "^1.0.0", 1121 | "read-pkg": "^1.0.0" 1122 | } 1123 | }, 1124 | "readable-stream": { 1125 | "version": "1.1.14", 1126 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 1127 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 1128 | "requires": { 1129 | "core-util-is": "~1.0.0", 1130 | "inherits": "~2.0.1", 1131 | "isarray": "0.0.1", 1132 | "string_decoder": "~0.10.x" 1133 | } 1134 | }, 1135 | "redent": { 1136 | "version": "1.0.0", 1137 | "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", 1138 | "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", 1139 | "requires": { 1140 | "indent-string": "^2.1.0", 1141 | "strip-indent": "^1.0.1" 1142 | } 1143 | }, 1144 | "regl": { 1145 | "version": "1.3.9", 1146 | "resolved": "https://registry.npmjs.org/regl/-/regl-1.3.9.tgz", 1147 | "integrity": "sha512-CungQSUBsZNYZJWJlb2sPe4iwBjmxrgl1Yxt91HN3VuuEL7lJ5k03O3T1xEXVOCMN1q8wncddwJsxozuyzzmrA==" 1148 | }, 1149 | "repeat-string": { 1150 | "version": "1.6.1", 1151 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", 1152 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" 1153 | }, 1154 | "repeating": { 1155 | "version": "2.0.1", 1156 | "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", 1157 | "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", 1158 | "requires": { 1159 | "is-finite": "^1.0.0" 1160 | } 1161 | }, 1162 | "request": { 1163 | "version": "2.88.0", 1164 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", 1165 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", 1166 | "requires": { 1167 | "aws-sign2": "~0.7.0", 1168 | "aws4": "^1.8.0", 1169 | "caseless": "~0.12.0", 1170 | "combined-stream": "~1.0.6", 1171 | "extend": "~3.0.2", 1172 | "forever-agent": "~0.6.1", 1173 | "form-data": "~2.3.2", 1174 | "har-validator": "~5.1.0", 1175 | "http-signature": "~1.2.0", 1176 | "is-typedarray": "~1.0.0", 1177 | "isstream": "~0.1.2", 1178 | "json-stringify-safe": "~5.0.1", 1179 | "mime-types": "~2.1.19", 1180 | "oauth-sign": "~0.9.0", 1181 | "performance-now": "^2.1.0", 1182 | "qs": "~6.5.2", 1183 | "safe-buffer": "^5.1.2", 1184 | "tough-cookie": "~2.4.3", 1185 | "tunnel-agent": "^0.6.0", 1186 | "uuid": "^3.3.2" 1187 | } 1188 | }, 1189 | "right-align": { 1190 | "version": "0.1.3", 1191 | "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", 1192 | "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", 1193 | "requires": { 1194 | "align-text": "^0.1.1" 1195 | } 1196 | }, 1197 | "rimraf": { 1198 | "version": "2.6.2", 1199 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 1200 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 1201 | "requires": { 1202 | "glob": "^7.0.5" 1203 | } 1204 | }, 1205 | "safe-buffer": { 1206 | "version": "5.1.2", 1207 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1208 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1209 | }, 1210 | "safer-buffer": { 1211 | "version": "2.1.2", 1212 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1213 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1214 | }, 1215 | "semver": { 1216 | "version": "5.6.0", 1217 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", 1218 | "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" 1219 | }, 1220 | "serialize-wavefront-obj": { 1221 | "version": "1.0.0", 1222 | "resolved": "https://registry.npmjs.org/serialize-wavefront-obj/-/serialize-wavefront-obj-1.0.0.tgz", 1223 | "integrity": "sha1-fhyq6CeCqDae45sK1CPwavdkH6c=" 1224 | }, 1225 | "shallow-copy": { 1226 | "version": "0.0.1", 1227 | "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", 1228 | "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=" 1229 | }, 1230 | "signal-exit": { 1231 | "version": "3.0.2", 1232 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1233 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 1234 | }, 1235 | "single-line-log": { 1236 | "version": "1.1.2", 1237 | "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz", 1238 | "integrity": "sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q=", 1239 | "requires": { 1240 | "string-width": "^1.0.1" 1241 | } 1242 | }, 1243 | "source-map": { 1244 | "version": "0.1.43", 1245 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", 1246 | "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", 1247 | "optional": true, 1248 | "requires": { 1249 | "amdefine": ">=0.0.4" 1250 | } 1251 | }, 1252 | "spdx-correct": { 1253 | "version": "3.0.2", 1254 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz", 1255 | "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==", 1256 | "requires": { 1257 | "spdx-expression-parse": "^3.0.0", 1258 | "spdx-license-ids": "^3.0.0" 1259 | } 1260 | }, 1261 | "spdx-exceptions": { 1262 | "version": "2.2.0", 1263 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", 1264 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" 1265 | }, 1266 | "spdx-expression-parse": { 1267 | "version": "3.0.0", 1268 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 1269 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 1270 | "requires": { 1271 | "spdx-exceptions": "^2.1.0", 1272 | "spdx-license-ids": "^3.0.0" 1273 | } 1274 | }, 1275 | "spdx-license-ids": { 1276 | "version": "3.0.2", 1277 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz", 1278 | "integrity": "sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg==" 1279 | }, 1280 | "speedometer": { 1281 | "version": "0.1.4", 1282 | "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-0.1.4.tgz", 1283 | "integrity": "sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0=" 1284 | }, 1285 | "sshpk": { 1286 | "version": "1.15.2", 1287 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz", 1288 | "integrity": "sha512-Ra/OXQtuh0/enyl4ETZAfTaeksa6BXks5ZcjpSUNrjBr0DvrJKX+1fsKDPpT9TBXgHAFsa4510aNVgI8g/+SzA==", 1289 | "requires": { 1290 | "asn1": "~0.2.3", 1291 | "assert-plus": "^1.0.0", 1292 | "bcrypt-pbkdf": "^1.0.0", 1293 | "dashdash": "^1.12.0", 1294 | "ecc-jsbn": "~0.1.1", 1295 | "getpass": "^0.1.1", 1296 | "jsbn": "~0.1.0", 1297 | "safer-buffer": "^2.0.2", 1298 | "tweetnacl": "~0.14.0" 1299 | } 1300 | }, 1301 | "static-eval": { 1302 | "version": "0.2.4", 1303 | "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-0.2.4.tgz", 1304 | "integrity": "sha1-t9NNg4k3uWn5ZBygfUj47eJj6ns=", 1305 | "requires": { 1306 | "escodegen": "~0.0.24" 1307 | }, 1308 | "dependencies": { 1309 | "escodegen": { 1310 | "version": "0.0.28", 1311 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-0.0.28.tgz", 1312 | "integrity": "sha1-Dk/xcV8yh3XWyrUaxEpAbNer/9M=", 1313 | "requires": { 1314 | "esprima": "~1.0.2", 1315 | "estraverse": "~1.3.0", 1316 | "source-map": ">= 0.1.2" 1317 | } 1318 | }, 1319 | "esprima": { 1320 | "version": "1.0.4", 1321 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", 1322 | "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=" 1323 | }, 1324 | "estraverse": { 1325 | "version": "1.3.2", 1326 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.3.2.tgz", 1327 | "integrity": "sha1-N8K4k+8T1yPydth41g2FNRUqbEI=" 1328 | } 1329 | } 1330 | }, 1331 | "static-module": { 1332 | "version": "1.5.0", 1333 | "resolved": "https://registry.npmjs.org/static-module/-/static-module-1.5.0.tgz", 1334 | "integrity": "sha1-J9qYg8QajNCSNvhC8MHrxu32PYY=", 1335 | "requires": { 1336 | "concat-stream": "~1.6.0", 1337 | "duplexer2": "~0.0.2", 1338 | "escodegen": "~1.3.2", 1339 | "falafel": "^2.1.0", 1340 | "has": "^1.0.0", 1341 | "object-inspect": "~0.4.0", 1342 | "quote-stream": "~0.0.0", 1343 | "readable-stream": "~1.0.27-1", 1344 | "shallow-copy": "~0.0.1", 1345 | "static-eval": "~0.2.0", 1346 | "through2": "~0.4.1" 1347 | }, 1348 | "dependencies": { 1349 | "readable-stream": { 1350 | "version": "1.0.34", 1351 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 1352 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", 1353 | "requires": { 1354 | "core-util-is": "~1.0.0", 1355 | "inherits": "~2.0.1", 1356 | "isarray": "0.0.1", 1357 | "string_decoder": "~0.10.x" 1358 | } 1359 | }, 1360 | "through2": { 1361 | "version": "0.4.2", 1362 | "resolved": "http://registry.npmjs.org/through2/-/through2-0.4.2.tgz", 1363 | "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", 1364 | "requires": { 1365 | "readable-stream": "~1.0.17", 1366 | "xtend": "~2.1.1" 1367 | } 1368 | } 1369 | } 1370 | }, 1371 | "string-width": { 1372 | "version": "1.0.2", 1373 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1374 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1375 | "requires": { 1376 | "code-point-at": "^1.0.0", 1377 | "is-fullwidth-code-point": "^1.0.0", 1378 | "strip-ansi": "^3.0.0" 1379 | } 1380 | }, 1381 | "string_decoder": { 1382 | "version": "0.10.31", 1383 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1384 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 1385 | }, 1386 | "strip-ansi": { 1387 | "version": "3.0.1", 1388 | "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1389 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1390 | "requires": { 1391 | "ansi-regex": "^2.0.0" 1392 | } 1393 | }, 1394 | "strip-bom": { 1395 | "version": "2.0.0", 1396 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", 1397 | "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", 1398 | "requires": { 1399 | "is-utf8": "^0.2.0" 1400 | } 1401 | }, 1402 | "strip-indent": { 1403 | "version": "1.0.1", 1404 | "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", 1405 | "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", 1406 | "requires": { 1407 | "get-stdin": "^4.0.1" 1408 | } 1409 | }, 1410 | "strip-json-comments": { 1411 | "version": "2.0.1", 1412 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1413 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" 1414 | }, 1415 | "sumchecker": { 1416 | "version": "1.3.1", 1417 | "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-1.3.1.tgz", 1418 | "integrity": "sha1-ebs7RFbdBPGOvbwNcDodHa7FEF0=", 1419 | "requires": { 1420 | "debug": "^2.2.0", 1421 | "es6-promise": "^4.0.5" 1422 | } 1423 | }, 1424 | "surface-nets": { 1425 | "version": "1.0.2", 1426 | "resolved": "https://registry.npmjs.org/surface-nets/-/surface-nets-1.0.2.tgz", 1427 | "integrity": "sha1-5DPIy7qUpydMb0yZVStGG/H8eks=", 1428 | "requires": { 1429 | "ndarray-extract-contour": "^1.0.0", 1430 | "triangulate-hypercube": "^1.0.0", 1431 | "zero-crossings": "^1.0.0" 1432 | } 1433 | }, 1434 | "throttleit": { 1435 | "version": "0.0.2", 1436 | "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", 1437 | "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=" 1438 | }, 1439 | "through2": { 1440 | "version": "0.2.3", 1441 | "resolved": "http://registry.npmjs.org/through2/-/through2-0.2.3.tgz", 1442 | "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", 1443 | "requires": { 1444 | "readable-stream": "~1.1.9", 1445 | "xtend": "~2.1.1" 1446 | } 1447 | }, 1448 | "tough-cookie": { 1449 | "version": "2.4.3", 1450 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 1451 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 1452 | "requires": { 1453 | "psl": "^1.1.24", 1454 | "punycode": "^1.4.1" 1455 | } 1456 | }, 1457 | "triangulate-hypercube": { 1458 | "version": "1.0.1", 1459 | "resolved": "https://registry.npmjs.org/triangulate-hypercube/-/triangulate-hypercube-1.0.1.tgz", 1460 | "integrity": "sha1-2Acdsuv8/VHzCNC88qXEils20Tc=", 1461 | "requires": { 1462 | "gamma": "^0.1.0", 1463 | "permutation-parity": "^1.0.0", 1464 | "permutation-rank": "^1.0.0" 1465 | } 1466 | }, 1467 | "trim-newlines": { 1468 | "version": "1.0.0", 1469 | "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", 1470 | "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" 1471 | }, 1472 | "tunnel-agent": { 1473 | "version": "0.6.0", 1474 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1475 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1476 | "requires": { 1477 | "safe-buffer": "^5.0.1" 1478 | } 1479 | }, 1480 | "tweetnacl": { 1481 | "version": "0.14.5", 1482 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1483 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 1484 | }, 1485 | "typedarray": { 1486 | "version": "0.0.6", 1487 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 1488 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" 1489 | }, 1490 | "typedarray-pool": { 1491 | "version": "1.1.0", 1492 | "resolved": "https://registry.npmjs.org/typedarray-pool/-/typedarray-pool-1.1.0.tgz", 1493 | "integrity": "sha1-0RT0hIAUifU+yrXoCIqiMET0mNk=", 1494 | "requires": { 1495 | "bit-twiddle": "^1.0.0", 1496 | "dup": "^1.0.0" 1497 | } 1498 | }, 1499 | "uglify-js": { 1500 | "version": "2.8.29", 1501 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", 1502 | "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", 1503 | "requires": { 1504 | "source-map": "~0.5.1", 1505 | "uglify-to-browserify": "~1.0.0", 1506 | "yargs": "~3.10.0" 1507 | }, 1508 | "dependencies": { 1509 | "source-map": { 1510 | "version": "0.5.7", 1511 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 1512 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" 1513 | } 1514 | } 1515 | }, 1516 | "uglify-to-browserify": { 1517 | "version": "1.0.2", 1518 | "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", 1519 | "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", 1520 | "optional": true 1521 | }, 1522 | "uniq": { 1523 | "version": "1.0.1", 1524 | "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", 1525 | "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" 1526 | }, 1527 | "util-deprecate": { 1528 | "version": "1.0.2", 1529 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1530 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1531 | }, 1532 | "uuid": { 1533 | "version": "3.3.2", 1534 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 1535 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 1536 | }, 1537 | "validate-npm-package-license": { 1538 | "version": "3.0.4", 1539 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 1540 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 1541 | "requires": { 1542 | "spdx-correct": "^3.0.0", 1543 | "spdx-expression-parse": "^3.0.0" 1544 | } 1545 | }, 1546 | "verror": { 1547 | "version": "1.10.0", 1548 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 1549 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 1550 | "requires": { 1551 | "assert-plus": "^1.0.0", 1552 | "core-util-is": "1.0.2", 1553 | "extsprintf": "^1.2.0" 1554 | } 1555 | }, 1556 | "window-size": { 1557 | "version": "0.1.0", 1558 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", 1559 | "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" 1560 | }, 1561 | "wordwrap": { 1562 | "version": "0.0.2", 1563 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", 1564 | "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" 1565 | }, 1566 | "wrappy": { 1567 | "version": "1.0.2", 1568 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1569 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1570 | }, 1571 | "xtend": { 1572 | "version": "2.1.2", 1573 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", 1574 | "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", 1575 | "requires": { 1576 | "object-keys": "~0.4.0" 1577 | } 1578 | }, 1579 | "yargs": { 1580 | "version": "3.10.0", 1581 | "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", 1582 | "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", 1583 | "requires": { 1584 | "camelcase": "^1.0.2", 1585 | "cliui": "^2.1.0", 1586 | "decamelize": "^1.0.0", 1587 | "window-size": "0.1.0" 1588 | }, 1589 | "dependencies": { 1590 | "camelcase": { 1591 | "version": "1.2.1", 1592 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", 1593 | "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" 1594 | } 1595 | } 1596 | }, 1597 | "yauzl": { 1598 | "version": "2.4.1", 1599 | "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", 1600 | "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", 1601 | "requires": { 1602 | "fd-slicer": "~1.0.1" 1603 | } 1604 | }, 1605 | "zero-crossings": { 1606 | "version": "1.0.1", 1607 | "resolved": "https://registry.npmjs.org/zero-crossings/-/zero-crossings-1.0.1.tgz", 1608 | "integrity": "sha1-xWK9MRNkPzRDokXRJAa4i2m5qf8=", 1609 | "requires": { 1610 | "cwise-compiler": "^1.0.0" 1611 | } 1612 | } 1613 | } 1614 | } 1615 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hiccup-sdf-to-obj", 3 | "version": "0.0.10", 4 | "description": "export hiccup-sdf models to OBJ", 5 | "main": "index.js", 6 | "author": "Szymon Kaliski (http://szymonkaliski.com)", 7 | "license": "MIT", 8 | "dependencies": { 9 | "display-sdf": "^0.0.9", 10 | "electron": "^2.0.8", 11 | "hiccup-sdf": "^0.0.10", 12 | "implicit-mesh": "^1.1.1", 13 | "ndarray": "^1.0.18", 14 | "node-ipc": "^9.1.1", 15 | "serialize-wavefront-obj": "^1.0.0", 16 | "surface-nets": "^1.0.2" 17 | }, 18 | "gitHead": "e7f441600f1b472fcf0ef1396399c465ea258d0e" 19 | } 20 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/test/map-gpu.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | 4 | const sdfToObj = require("../gpu"); 5 | 6 | const randomSpherePosition = r => { 7 | const u = Math.random(); 8 | const v = Math.random(); 9 | 10 | const theta = 2 * Math.PI * u; 11 | const phi = Math.acos(2 * v - 1); 12 | 13 | const x = r * Math.sin(phi) * Math.cos(theta); 14 | const y = r * Math.sin(phi) * Math.sin(theta); 15 | const z = r * Math.cos(phi); 16 | 17 | return [x, y, z]; 18 | }; 19 | 20 | const range = length => Array.from({ length }, (_, i) => i); 21 | 22 | const points = range(100).map(() => randomSpherePosition(0.4)); 23 | 24 | const tree = [ 25 | "map", 26 | { 27 | data: { points }, 28 | map: props => [ 29 | "translate", 30 | { t: `${props.points}.xyz` }, 31 | [["sphere", { r: 0.05 }]] 32 | ], 33 | reduce: ["union", { r: 0.01 }] 34 | } 35 | ]; 36 | 37 | console.time("hiccup-sdf-to-obj"); 38 | sdfToObj(tree, { size: 256 }, objStr => { 39 | console.timeEnd("hiccup-sdf-to-obj"); 40 | 41 | fs.writeFileSync(path.join(__dirname, "map-gpu.obj"), objStr); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/test/map.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | 4 | const sdfToObj = require("../"); 5 | 6 | const randomSpherePosition = r => { 7 | const u = Math.random(); 8 | const v = Math.random(); 9 | 10 | const theta = 2 * Math.PI * u; 11 | const phi = Math.acos(2 * v - 1); 12 | 13 | const x = r * Math.sin(phi) * Math.cos(theta); 14 | const y = r * Math.sin(phi) * Math.sin(theta); 15 | const z = r * Math.cos(phi); 16 | 17 | return [x, y, z]; 18 | }; 19 | 20 | const range = length => Array.from({ length }, (_, i) => i); 21 | 22 | const points = range(100).map(() => randomSpherePosition(0.4)); 23 | 24 | const tree = [ 25 | "map", 26 | { 27 | data: { points }, 28 | map: props => ["translate", { t: props.points }, [["sphere", { r: 0.05 }]]], 29 | reduce: ["union", { r: 0.01 }] 30 | } 31 | ]; 32 | 33 | console.time("hiccup-sdf-to-obj"); 34 | sdfToObj(tree, { size: 128 }, objStr => { 35 | console.timeEnd("hiccup-sdf-to-obj"); 36 | 37 | fs.writeFileSync(path.join(__dirname, "map.obj"), objStr); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/test/sphere.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | 4 | const sdfToObj = require("../threaded"); 5 | 6 | const tree = ["sphere"]; 7 | 8 | sdfToObj(tree, objStr => { 9 | fs.writeFileSync(path.join(__dirname, "sphere.obj"), objStr); 10 | }); 11 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/test/union.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | 4 | const sdfToObj = require("../threaded"); 5 | 6 | const translatedSphere = (t, r) => ["translate", { t }, [["sphere", { r }]]]; 7 | 8 | // prettier-ignore 9 | const tree = [ 10 | "union", 11 | { r: 0.01 }, 12 | [ 13 | translatedSphere([-0.1, 0, 0], 0.2), 14 | translatedSphere([0.1, 0, 0], 0.2) 15 | ] 16 | ]; 17 | 18 | sdfToObj(tree, { size: 256 }, objStr => { 19 | fs.writeFileSync(path.join(__dirname, "union.obj"), objStr); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/threaded.js: -------------------------------------------------------------------------------- 1 | const serializeObj = require("serialize-wavefront-obj"); 2 | const threadedImplicitMesh = require("./threaded/implicit-mesh"); 3 | 4 | module.exports = (tree, options, callback) => { 5 | if (!callback) { 6 | callback = options; 7 | options = { size: 128, threads: 4 }; 8 | } 9 | 10 | threadedImplicitMesh(tree, options, ({ cells, positions }) => { 11 | const str = serializeObj(cells, positions); 12 | callback(str); 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/threaded/implicit-mesh.js: -------------------------------------------------------------------------------- 1 | const ndarray = require("ndarray"); 2 | const path = require("path"); 3 | const surfaceNets = require("surface-nets"); 4 | 5 | const { Worker } = require("worker_threads"); 6 | 7 | const range = length => Array.from({ length }).map((_, i) => i); 8 | 9 | const scale = (size, mesh) => { 10 | const sx = 2 / size[0]; 11 | const sy = 2 / size[1]; 12 | const sz = 2 / size[2]; 13 | 14 | const p = mesh.positions; 15 | 16 | for (let i = 0; i < p.length; i++) { 17 | p[i][0] = p[i][0] * sx - 1; 18 | p[i][1] = p[i][1] * sy - 1; 19 | p[i][2] = p[i][2] * sz - 1; 20 | } 21 | 22 | return mesh; 23 | }; 24 | 25 | module.exports = (tree, { size = 64, threads = 4 }, callback) => { 26 | const data = ndarray(new Float64Array(size * size * size), [ 27 | size, 28 | size, 29 | size 30 | ]); 31 | 32 | const doneStatus = range(threads).map(() => false); 33 | const allDone = () => doneStatus.every(done => done); 34 | 35 | range(threads).map(i => { 36 | console.time(`worker[${i}]`); 37 | 38 | const worker = new Worker(path.join(__dirname, "worker.js"), { 39 | workerData: { 40 | id: i, 41 | size, 42 | xs: [ 43 | Math.floor((i * size) / threads), 44 | Math.floor(((i + 1) * size) / threads) 45 | ], 46 | tree 47 | } 48 | }); 49 | 50 | worker.on("message", msg => { 51 | console.timeEnd(`worker[${i}]`); 52 | 53 | worker.unref(); 54 | 55 | doneStatus[i] = true; 56 | 57 | const { xs } = msg; 58 | 59 | let ii = 0; 60 | 61 | for (let x = xs[0]; x < xs[1]; x++) { 62 | for (let y = 0; y < size; y++) { 63 | for (let z = 0; z < size; z++) { 64 | data.set(x, y, z, msg.data[ii][y][z]); 65 | } 66 | } 67 | 68 | ii++; 69 | } 70 | 71 | if (allDone()) { 72 | console.time("surfaceNets"); 73 | const mesh = scale([size, size, size], surfaceNets(data)); 74 | console.timeEnd("surfaceNets"); 75 | callback(mesh); 76 | } 77 | }); 78 | }); 79 | }; 80 | -------------------------------------------------------------------------------- /packages/hiccup-sdf-to-obj/threaded/worker.js: -------------------------------------------------------------------------------- 1 | const { workerData, parentPort } = require("worker_threads"); 2 | const dsl = require("hiccup-sdf"); 3 | 4 | const { id, size, xs, tree } = workerData; 5 | 6 | console.log(`worker[${id}]`, size, xs); 7 | 8 | let data = []; 9 | 10 | const sdfFunction = dsl.compileFunction(tree); 11 | 12 | let x = 0; 13 | for (let i = xs[0]; i < xs[1]; i++) { 14 | if (!data[x]) { 15 | data[x] = []; 16 | } 17 | 18 | for (let y = 0; y < size; y++) { 19 | if (!data[x][y]) { 20 | data[x][y] = []; 21 | } 22 | 23 | for (let z = 0; z < size; z++) { 24 | const j = y; 25 | const k = z; 26 | 27 | const value = sdfFunction([ 28 | (i / size) * 2 - 1, 29 | (j / size) * 2 - 1, 30 | (k / size) * 2 - 1 31 | ]); 32 | 33 | data[x][y][z] = value; 34 | } 35 | } 36 | 37 | x++; 38 | } 39 | 40 | parentPort.postMessage({ data, xs, id }); 41 | parentPort.unref(); 42 | -------------------------------------------------------------------------------- /packages/hiccup-sdf/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szymonkaliski/hiccup-sdf/715089e16fbfcfbacd85ca5249709cf6d1ca2a9e/packages/hiccup-sdf/.gitignore -------------------------------------------------------------------------------- /packages/hiccup-sdf/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Szymon Kaliski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/hiccup-sdf/README.md: -------------------------------------------------------------------------------- 1 | # hiccup-sdf 2 | 3 | Library for building signed distance function models using hiccup-like language. 4 | 5 |

6 | 7 |

8 | 9 | ## Installation 10 | 11 | `npm install hiccup-sdf --save` 12 | 13 | ## Basic usage 14 | 15 | ```js 16 | const { displayRaw } = require("display-sdf"); 17 | const { compileShader, glslHelpers } = require("hiccup-sdf"); 18 | 19 | const tree = [ 20 | "difference", 21 | [ 22 | ["box", { s: [0.5, 0.5, 0.5] }], 23 | ["sphere", { r: 0.4 }] 24 | ] 25 | ]; 26 | 27 | const { inject, model } = compileShader(tree); 28 | const shader = glslHelpers.createShaderFull(model, inject); 29 | 30 | displayRaw(shader); 31 | ``` 32 | 33 | ## Language 34 | 35 | ### Shapes 36 | 37 | - `["sphere", { r: float }]` 38 | - `["box", { s: [float, float, float] }]` 39 | - `["torus", { r1: float, r2: float }]` 40 | - `["hex", { h: float, r: float }]` 41 | - `["triangle", { h: float, r: float }]` 42 | - `["capsule", { a: float, b: float, r: float }]` 43 | - `["cylinder", { h: float, r: float }]` 44 | 45 | ### Operations 46 | 47 | - `["translate", { t: [float, float, float] }, [subtree]]` 48 | - `["scale", { s: float }, [subtree]]` 49 | - `["rotate", { r: [float, float, float] }, [subtree]]` 50 | - `["mirror", { m: [float, float, float] }, [subtree]]` 51 | - `["elongate", { s: [float, float, float] }, [subtree]]` 52 | - `["repeat", { r: [float, float, float] }, [subtree]]` 53 | - `["repeatPolar", { r: float }, [subtree]]` 54 | - `["union", { r: float }, [subtree]]` 55 | - `["intersection", { r: float }, [subtree]]` 56 | - `["difference", { r: float }, [subtree]]` 57 | - `["blend", { k: float }, [subtree]]` 58 | 59 | ### `Map` 60 | 61 | Map is bit more complex operation allowing map-reduce style mapping over huge datasets. This works around instruction/complexity limitations for GPU shaders, and provides nicer API to work with. Unfortunately there are some differences in `map` usage on CPU/GPU (all the other functions/operations should work the same regardless of the backend). 62 | 63 | ```js 64 | const randomPositions = range(1000).map(() => randomPosition()); // random array of [x,y,z] positions 65 | 66 | const tree = [ 67 | "map", 68 | { 69 | // data points to map over, an object mapping key to value, multiple datasets can be supplied 70 | data: { position: randomPositions }, 71 | 72 | // mapping function, runs for each of the data points, `props` contains a single data point for each of `data` keys 73 | map: props => [ 74 | "translate", 75 | { 76 | t: `${props.position}.xyz` // fragment of GLSL code that will be injected when mapping over the texture when ran on GPU, if used on CPU this should be `{ t: props.position }` as we can use the js array directly 77 | }, 78 | [ 79 | ["sphere", { r: 0.1 }] 80 | ] 81 | ], 82 | 83 | // reducing function, generated subtree will be inserted as last parameter, 84 | // usually `union` / `interesction` / `difference` 85 | reduce: ["union", { r: 0.02 }] 86 | } 87 | ] 88 | ``` 89 | 90 | When ran on GPU this operation is turned into efficient texture traversal, and `compileShader()` returns `uniforms` with matching textures. 91 | For full example open [`examples/basic/map.js`](../../examples/basic/map.js). 92 | 93 | ## Public API 94 | 95 | ### `const dsl = require("hiccup-sdf")` 96 | 97 | - `dsl.compileShader(tree)` - generates SDF shader, returns an object: `{ model, uniforms, inject }`: 98 | - `model` - modeling part of SDF shader on GPU 99 | - `inject` - SDF shader code necessary to run the modeling shader 100 | - `uniforms` - data values if the tree is using `map` function 101 | - `dsl.compileFunction(tree)` - generate SDF function to be used on CPU, returns a function: 102 | - `fn([x, y, z])` - returns SDF value for given point 103 | 104 | - `dsl.glslHelpers` - helper utils for generating GLSL code: 105 | - `dsl.glslHelpers.createShaderFull(model, inject)` - creates full SDF shader that can be used without any modifications 106 | - `dsl.glslHelpers.createShaderModel(model, inject)` - creates only the `vec2 doModel(vec3 p)` part of the shader 107 | 108 | ## Acknowledgements 109 | 110 | This project was developed in part at Laboratory, an artist residency for interactive arts: [https://laboratoryspokane.com](https://laboratoryspokane.com). 111 | 112 | -------------------------------------------------------------------------------- /packages/hiccup-sdf/assets/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szymonkaliski/hiccup-sdf/715089e16fbfcfbacd85ca5249709cf6d1ca2a9e/packages/hiccup-sdf/assets/screen.png -------------------------------------------------------------------------------- /packages/hiccup-sdf/dsl.js: -------------------------------------------------------------------------------- 1 | const v = require("@thi.ng/vectors"); 2 | const { uniq, flatten, without } = require("lodash"); 3 | 4 | const GLSL_STL = require("./glsl-stl"); 5 | 6 | const GLSL = "GLSL"; 7 | const CPU = "CPU"; 8 | 9 | const copy = a => a.slice(0); 10 | const clamp = (v, min, max) => Math.max(Math.min(v, max), min); 11 | const length2 = ([x, y]) => Math.sqrt(x * x + y * y); 12 | const length3 = ([x, y, z]) => Math.sqrt(x * x + y * y + z * z); 13 | const range = length => Array.from({ length }).map((_, i) => i); 14 | const flatArray = t => (Array.isArray(t) ? flatten(t) : [t]); 15 | const formatNonString = (t, format) => (typeof t !== "string" ? format(t) : t); 16 | 17 | const GL = { 18 | vec3: v => 19 | formatNonString( 20 | v, 21 | ([x, y, z]) => `vec3(${GL.f(x)}, ${GL.f(y)}, ${GL.f(z)})` 22 | ), 23 | f: n => formatNonString(n, n => n.toFixed(8)), 24 | i: n => formatNonString(n, n => Math.round(n)) 25 | }; 26 | 27 | const SHAPE = { 28 | sphere: { 29 | defaultProps: { 30 | r: 0.5 31 | }, 32 | [CPU]: { generate: props => p => length3(p) - props.r }, 33 | [GLSL]: { 34 | generate: props => p => `sdSphere(${p}, ${GL.f(props.r)})`, 35 | inject: () => GLSL_STL.SD_SPHERE 36 | } 37 | }, 38 | 39 | box: { 40 | defaultProps: { 41 | s: [0.1, 0.1, 0.1] 42 | }, 43 | [CPU]: { 44 | generate: props => p => { 45 | const d = v.sub3(v.abs3(p), props.s); 46 | 47 | return ( 48 | Math.min(Math.max(d[0], Math.max(d[1], d[2])), 0.0) + 49 | length3(v.max3(d, [0, 0, 0])) 50 | ); 51 | } 52 | }, 53 | [GLSL]: { 54 | generate: props => p => `sdBox(${p}, ${GL.vec3(props.s)})`, 55 | inject: () => GLSL_STL.SD_BOX 56 | } 57 | }, 58 | 59 | torus: { 60 | defaultProps: { 61 | r1: 0.1, 62 | r2: 0.2 63 | }, 64 | [CPU]: { 65 | generate: props => p => { 66 | const q = [length2([p[0], p[2]]) - props.r2, p[1]]; 67 | return length2(q) - props.r1; 68 | } 69 | }, 70 | [GLSL]: { 71 | generate: props => p => 72 | `sdTorus(${p}, ${GL.f(props.r1)}, ${GL.f(props.r2)})`, 73 | inject: () => GLSL_STL.SD_TORUS 74 | } 75 | }, 76 | 77 | hex: { 78 | defaultProps: { 79 | h: 0.01, 80 | r: 0.2 81 | }, 82 | [CPU]: { 83 | generate: props => p => { 84 | const q = v.abs3(p); 85 | 86 | return Math.max( 87 | q[2] - props.h, 88 | Math.max(q[0] * 0.866025 + q[1] * 0.5, q[1]) - props.r 89 | ); 90 | } 91 | }, 92 | [GLSL]: { 93 | generate: props => p => `sdHex(${p}, ${GL.f(props.r)}, ${GL.f(props.h)})`, 94 | inject: () => GLSL_STL.SD_HEX 95 | } 96 | }, 97 | 98 | triangle: { 99 | defaultProps: { 100 | h: 0.01, 101 | r: 0.2 102 | }, 103 | [CPU]: { 104 | generate: props => p => { 105 | const q = v.abs3(p); 106 | 107 | // FIXME: not a triangle? 108 | return Math.max( 109 | q[2] - props.h, 110 | Math.max(q[0] * 0.866025 + p[1] * 0.5, -p[1]) - props.r * 0.5 111 | ); 112 | } 113 | }, 114 | [GLSL]: { 115 | generate: props => p => 116 | `sdTriangle(${p}, ${GL.f(props.r)}, ${GL.f(props.h)})`, 117 | inject: () => GLSL_STL.SD_TRIANGLE 118 | } 119 | }, 120 | 121 | capsule: { 122 | defaultProps: { 123 | a: [0, 0, 0], 124 | b: [0, 0, 0.2], 125 | r: 0.1 126 | }, 127 | [CPU]: { 128 | generate: props => p => { 129 | const pa = v.sub3(copy(p), props.a); 130 | const ba = v.sub3(copy(props.b), props.a); 131 | 132 | const h = clamp(v.dot3(pa, ba) / v.dot3(ba, ba), 0.0, 1.0); 133 | 134 | return length3(v.sub3(pa, v.mulN3(ba, h))) - props.r; 135 | } 136 | }, 137 | [GLSL]: { 138 | generate: props => p => 139 | `sdCapsule( 140 | ${p}, 141 | ${GL.vec3(props.a)}, 142 | ${GL.vec3(props.b)}, 143 | ${GL.f(props.r)} 144 | )`, 145 | inject: () => GLSL_STL.SD_CAPSULE 146 | } 147 | }, 148 | 149 | cylinder: { 150 | defaultProps: { 151 | r: 0.1, 152 | h: 0.3 153 | }, 154 | [CPU]: { 155 | generate: props => p => { 156 | const d = v.sub2(v.abs2([length2([p[0], p[2]]), p[1]]), [ 157 | props.r, 158 | props.h 159 | ]); 160 | 161 | return ( 162 | Math.min(Math.max(d[0], d[1]), 0.0) + length2(v.max2(d, [0.0, 0.0])) 163 | ); 164 | } 165 | }, 166 | [GLSL]: { 167 | generate: props => p => 168 | `sdCylinder(${p}, ${GL.f(props.r)}, ${GL.f(props.h)})`, 169 | inject: () => GLSL_STL.SD_CYLINDER 170 | } 171 | } 172 | }; 173 | 174 | const OP = { 175 | translate: { 176 | defaultProps: { 177 | t: [0, 0, 0] 178 | }, 179 | [CPU]: { 180 | generate: (props, children) => 181 | reduceChildrenCPU(children, { process: p => v.sub3(copy(p), props.t) }) 182 | }, 183 | [GLSL]: { 184 | generate: (props, children) => 185 | reduceChildrenGLSL(children, { 186 | process: p => `${p} - ${GL.vec3(props.t)}` 187 | }) 188 | } 189 | }, 190 | 191 | scale: { 192 | defaultProps: { 193 | s: 0.5 194 | }, 195 | [CPU]: { 196 | generate: (props, children) => { 197 | return o => { 198 | const p = v.divN3(o, props.s); 199 | 200 | let d = children[0](p) * props.s; 201 | 202 | for (let i = 1; i < children.length; i++) { 203 | d = Math.min(d, children[i](p)) * props.s; 204 | } 205 | 206 | return d; 207 | }; 208 | } 209 | }, 210 | [GLSL]: { 211 | generate: (props, children) => o => { 212 | const { s } = props; 213 | const p = `${o} / ${GL.f(s)}`; 214 | 215 | return children 216 | .slice(1) 217 | .reduce( 218 | (memo, child) => reducer(memo, `${child(p)} * ${GL.f(s)}`), 219 | `${children[0](p)} * ${GL.f(s)}` 220 | ); 221 | } 222 | } 223 | }, 224 | 225 | rotate: { 226 | defaultProps: { 227 | r: [0.0, 0.0, 0.0] 228 | }, 229 | [CPU]: { 230 | generate: (props, children) => 231 | reduceChildrenCPU(children, { 232 | process: p => { 233 | const { r } = props; 234 | const { sin, cos } = Math; 235 | const TAU = Math.PI * 2; 236 | 237 | const rxz = p => [ 238 | cos(r[0] * TAU) * p[0] + sin(r[0] * TAU) * p[2], 239 | p[1], 240 | cos(r[0] * TAU) * p[2] + sin(r[0] * TAU) * -p[0] 241 | ]; 242 | 243 | const rxy = p => [ 244 | cos(r[1] * TAU) * p[0] + sin(r[1] * TAU) * p[1], 245 | cos(r[1] * TAU) * p[1] + sin(r[1] * TAU) * -p[0], 246 | p[2] 247 | ]; 248 | 249 | const ryz = p => [ 250 | p[0], 251 | cos(r[2] * TAU) * p[1] + sin(r[2] * TAU) * p[2], 252 | cos(r[2] * TAU) * p[2] + sin(r[2] * TAU) * -p[1] 253 | ]; 254 | 255 | return ryz(rxy(rxz(p))); 256 | } 257 | }) 258 | }, 259 | [GLSL]: { 260 | generate: (props, children) => 261 | reduceChildrenGLSL(children, { 262 | process: p => `opRotate(${p}, ${GL.vec3(props.r)})` 263 | }), 264 | inject: () => GLSL_STL.OP_ROTATE 265 | } 266 | }, 267 | 268 | elongate: { 269 | defaultProps: { 270 | s: [0.1, 0.1, 0.1] 271 | }, 272 | [CPU]: { 273 | generate: () => 274 | reduceChildrenCPU(children, { 275 | process: p => v.max3(v.sub3(v.abs3(p), props.s), [0, 0, 0]) 276 | }) 277 | }, 278 | [GLSL]: { 279 | generate: (props, children) => 280 | reduceChildrenGLSL(children, { 281 | process: p => `opElongate(${p}, ${GL.vec3(props.s)})` 282 | }), 283 | inject: () => GLSL_STL.OP_ELONGATE 284 | } 285 | }, 286 | 287 | mirror: { 288 | defaultProps: { 289 | m: [0.5, 0.0, 0] 290 | }, 291 | [CPU]: { 292 | generate: (props, children) => 293 | reduceChildrenCPU(children, { 294 | process: p => [ 295 | props.m[0] > 0 ? Math.abs(p[0]) - props.m[0] : p[0], 296 | props.m[1] > 0 ? Math.abs(p[1]) - props.m[1] : p[1], 297 | props.m[2] > 0 ? Math.abs(p[2]) - props.m[2] : p[2] 298 | ] 299 | }) 300 | }, 301 | [GLSL]: { 302 | generate: (props, children) => 303 | reduceChildrenGLSL(children, { 304 | process: p => `opMirror(${p}, ${GL.vec3(props.m)})` 305 | }), 306 | inject: () => GLSL_STL.OP_MIRROR 307 | } 308 | }, 309 | 310 | repeat: { 311 | defaultProps: { 312 | r: [1.0, 0.0, 0.0] 313 | }, 314 | [CPU]: { 315 | generate: (props, children) => 316 | reduceChildrenCPU(children, { 317 | process: p => [ 318 | props.r[0] > 0 319 | ? ((p[0] + props.r[0] / 2) % props.r[0]) - props.r[0] / 2 320 | : p[0], 321 | props.r[1] > 0 322 | ? ((p[1] + props.r[1] / 2) % props.r[1]) - props.r[1] / 2 323 | : p[1], 324 | props.r[2] > 0 325 | ? ((p[2] + props.r[2] / 2) % props.r[2]) - props.r[2] / 2 326 | : p[2] 327 | ] 328 | }) 329 | }, 330 | [GLSL]: { 331 | generate: (props, children) => 332 | reduceChildrenGLSL(children, { 333 | process: p => `opRepeat(${p}, ${GL.vec3(props.r)})` 334 | }), 335 | inject: () => GLSL_STL.OP_REPEAT 336 | } 337 | }, 338 | 339 | repeatPolar: { 340 | defaultProps: { 341 | r: 6 342 | }, 343 | [CPU]: { 344 | generate: (props, children) => 345 | reduceChildrenCPU(children, { 346 | process: p => { 347 | // FIXME: this is broken 348 | 349 | const angle = (Math.PI * 2.0) / props.r; 350 | const a = Math.atan2(p[1], p[0]) + angle / 2.0; 351 | const l = length2([p[0], p[1]]); 352 | 353 | const aa = (a % angle) - angle / 2.0; 354 | 355 | return [Math.cos(aa) * l, Math.sin(aa) * l, p[2]]; 356 | } 357 | }) 358 | }, 359 | [GLSL]: { 360 | generate: (props, children) => 361 | reduceChildrenGLSL(children, { 362 | process: p => `opRepeatPolar(${p}, ${GL.f(props.r)})` 363 | }), 364 | inject: () => GLSL_STL.OP_REPEAT_POLAR 365 | } 366 | }, 367 | 368 | union: { 369 | defaultProps: { 370 | r: 0 371 | }, 372 | [CPU]: { 373 | generate: (props, children) => { 374 | const unionRound = (a, b) => { 375 | const u = v.max2([props.r - a, props.r - b], [0, 0]); 376 | return Math.max(props.r, Math.min(a, b)) - length2(u); 377 | }; 378 | 379 | const union = props.r > 0 ? unionRound : Math.min; 380 | 381 | return reduceChildrenCPU(children, { reducer: union }); 382 | } 383 | }, 384 | [GLSL]: { 385 | generate: (props, children) => 386 | reduceChildrenGLSL(children, { 387 | reducer: (a, b) => 388 | props.r > 0 389 | ? `opUnionRound(${a}, ${b}, ${GL.f(props.r)})` 390 | : `opUnion(${a}, ${b})` 391 | }), 392 | inject: props => 393 | props.r > 0 ? GLSL_STL.OP_UNION_ROUND : GLSL_STL.OP_UNION 394 | } 395 | }, 396 | 397 | intersection: { 398 | defaultProps: { 399 | r: 0 400 | }, 401 | [CPU]: { 402 | generate: (props, children) => { 403 | const intersectionRound = (a, b) => { 404 | const u = v.max2([props.r + a, props.r + b], [0, 0]); 405 | return Math.min(-props.r, Math.max(a, b)) + length2(u); 406 | }; 407 | 408 | const intersection = props.r > 0 ? intersectionRound : Math.max; 409 | 410 | return reduceChildrenCPU(children, { reducer: intersection }); 411 | } 412 | }, 413 | [GLSL]: { 414 | generate: (props, children) => 415 | reduceChildrenGLSL(children, { 416 | reducer: (a, b) => 417 | props.r > 0 418 | ? `opIntersectionRound(${a}, ${b}, ${GL.f(props.r)})` 419 | : `opIntersection(${a}, ${b})` 420 | }), 421 | inject: props => 422 | props.r > 0 ? GLSL_STL.OP_INTERSECTION_ROUND : GLSL_STL.OP_INTERSECTION 423 | } 424 | }, 425 | 426 | difference: { 427 | defaultProps: { 428 | r: 0 429 | }, 430 | [CPU]: { 431 | generate: (props, children) => { 432 | const differenceRound = (a, b) => { 433 | const u = v.max2([props.r + a, props.r - b], [0, 0]); 434 | return Math.min(-props.r, Math.max(a, -b)) + length2(u); 435 | }; 436 | 437 | const difference = 438 | props.r > 0 ? differenceRound : (a, b) => Math.max(a, -b); 439 | 440 | return reduceChildrenCPU(children, { reducer: difference }); 441 | } 442 | }, 443 | [GLSL]: { 444 | generate: (props, children) => 445 | reduceChildrenGLSL(children, { 446 | reducer: (a, b) => 447 | props.r > 0 448 | ? `opDifferenceRound(${a}, ${b}, ${GL.f(props.r)})` 449 | : `opDifference(${a}, ${b})` 450 | }), 451 | inject: props => 452 | props.r > 0 ? GLSL_STL.OP_DIFFERENCE_ROUND : GLSL_STL.OP_DIFFERENCE 453 | } 454 | }, 455 | 456 | blend: { 457 | defaultProps: { 458 | k: 0.5 459 | }, 460 | [CPU]: { 461 | generate: (props, children) => { 462 | const blend = (a, b) => (1.0 - props.k) * a + props.k * b; 463 | return reduceChildrenCPU(children, { reducer: blend }); 464 | } 465 | }, 466 | [GLSL]: { 467 | generate: (props, children) => 468 | reduceChildrenGLSL(children, { 469 | reducer: (a, b) => `opBlend(${a}, ${b}, ${GL.f(props.k)})` 470 | }), 471 | 472 | inject: () => GLSL_STL.OP_BLEND 473 | } 474 | }, 475 | 476 | map: { 477 | defaultProps: { 478 | reduce: ["union", { r: 0.0 }] 479 | }, 480 | [CPU]: { 481 | generate: props => { 482 | const largestLength = Object.keys(props.data).reduce( 483 | (memo, key) => Math.max(memo, props.data[key].length), 484 | 0 485 | ); 486 | 487 | const reducer = children => 488 | OP[props.reduce[0]][CPU].generate(props.reduce[1], children); 489 | 490 | return p => { 491 | let d; 492 | 493 | for (let i = 0; i < largestLength; i++) { 494 | const currentData = Object.keys(props.data).reduce((memo, key) => { 495 | return Object.assign(memo, { [key]: props.data[key][i] }); 496 | }, {}); 497 | 498 | const mapD = compile(props.map(currentData), { type: CPU }).model; 499 | 500 | if (!d) { 501 | d = mapD(p); 502 | } else { 503 | d = reducer([() => d, p => mapD(p)])(p); 504 | } 505 | } 506 | 507 | return d; 508 | }; 509 | } 510 | }, 511 | [GLSL]: { 512 | generate: props => p => `map${props.key}(${p})`, 513 | 514 | uniforms: props => { 515 | const largestTexLength = Object.keys(props.data).reduce( 516 | (memo, key) => Math.max(memo, props.data[key].length), 517 | 0 518 | ); 519 | 520 | const texSize = Math.ceil(Math.sqrt(largestTexLength)); 521 | 522 | const ensureArraySize = (arr, length, empty = 0) => 523 | range(length).map(i => (arr[i] !== undefined ? arr[i] : empty)); 524 | 525 | const squareArray = (arr, size, empty = 0) => { 526 | return range(size).map(i => 527 | range(size).map(j => { 528 | const idx = i * size + j; 529 | 530 | return ensureArraySize( 531 | idx < arr.length ? arr[idx] : empty, 532 | 4, 533 | empty 534 | ); 535 | }) 536 | ); 537 | }; 538 | 539 | return Object.keys(props.data).reduce( 540 | (memo, key) => 541 | Object.assign(memo, { 542 | [`${key}${props.key}Tex`]: squareArray(props.data[key], texSize) 543 | }), 544 | {} 545 | ); 546 | }, 547 | 548 | inject: props => { 549 | const largestTexLength = Object.keys(props.data).reduce( 550 | (memo, key) => Math.max(memo, props.data[key].length), 551 | 0 552 | ); 553 | 554 | const texSize = Math.ceil(Math.sqrt(largestTexLength)); 555 | 556 | const [reduceOp, reduceProps] = props.reduce; 557 | 558 | const { generate: reduceGenerate, inject: reduceInject } = OP[reduceOp][ 559 | GLSL 560 | ]; 561 | 562 | const childTree = props.map( 563 | Object.keys(props.data).reduce( 564 | (memo, key) => Object.assign(memo, { [key]: key }), 565 | {} 566 | ) 567 | ); 568 | 569 | const { model: childModel, inject: childInject } = compile(childTree, { 570 | type: GLSL 571 | }); 572 | 573 | const reduceChildren = [() => "value", childModel]; 574 | 575 | return [ 576 | reduceInject(reduceProps), 577 | childInject, 578 | ` 579 | ${Object.keys(props.data) 580 | .map(key => `uniform sampler2D ${key}${props.key}Tex;`) 581 | .join("\n")} 582 | 583 | float map${props.key}(vec3 p) { 584 | float value = 0.0; 585 | 586 | for (int i = 0; i < ${GL.i(texSize)}; i++) { 587 | for (int j = 0; j < ${GL.i(texSize)}; j++) { 588 | if (i + j * ${GL.i(texSize)} >= ${GL.i(largestTexLength)}) { 589 | break; 590 | } 591 | 592 | vec2 uv = vec2( 593 | float(i) / ${GL.f(texSize)}, 594 | float(j) / ${GL.f(texSize)} 595 | ); 596 | 597 | ${Object.keys(props.data) 598 | .map(key => ` vec4 ${key} = texture2D(${key}${props.key}Tex, uv);`) 599 | .join("\n")} 600 | 601 | if (i == 0 && j == 0) { 602 | value = ${childModel("p")}; 603 | } 604 | else { 605 | value = ${reduceGenerate(reduceProps, reduceChildren)("p")}; 606 | } 607 | } 608 | } 609 | 610 | return value; 611 | }` 612 | ]; 613 | } 614 | } 615 | } 616 | }; 617 | 618 | const isShape = shape => Object.keys(SHAPE).includes(shape); 619 | const isOp = op => Object.keys(OP).includes(op); 620 | 621 | function reduceChildrenCPU(children, { reducer = Math.min, process = p => p }) { 622 | return o => { 623 | const p = process(o); 624 | 625 | let d = children[0](p); 626 | 627 | for (let i = 1; i < children.length; i++) { 628 | d = reducer(d, children[i](p)); 629 | } 630 | 631 | return d; 632 | }; 633 | } 634 | 635 | function reduceChildrenGLSL( 636 | children, 637 | { reducer = (a, b) => `min(${a}, ${b})`, process = p => p } 638 | ) { 639 | return o => { 640 | const p = process(o); 641 | 642 | if (children.length === 1) { 643 | return children[0](p); 644 | } 645 | 646 | return children 647 | .slice(1) 648 | .reduce((memo, child) => reducer(memo, child(p)), children[0](p)); 649 | }; 650 | } 651 | 652 | function preProcessTree(tree) { 653 | let counter = 0; 654 | 655 | const preProcess = tree => { 656 | counter++; 657 | 658 | if (Array.isArray(tree)) { 659 | if (Array.isArray(tree[1])) { 660 | tree[2] = tree[1]; 661 | tree[1] = undefined; 662 | } 663 | 664 | if (!tree[1]) { 665 | tree[1] = {}; 666 | } 667 | 668 | if (tree[1].key === undefined) { 669 | tree[1].key = counter; 670 | } 671 | 672 | if (tree[2]) { 673 | tree[2].forEach(child => preProcessTree(child)); 674 | } 675 | } 676 | }; 677 | 678 | const output = tree.slice(0); 679 | 680 | preProcess(output); 681 | 682 | return output; 683 | } 684 | 685 | function compile(inputTree, { type = GLSL }) { 686 | tree = preProcessTree(inputTree); 687 | 688 | if (Array.isArray(tree)) { 689 | const id = tree[0]; 690 | 691 | if (isShape(id)) { 692 | const { defaultProps } = SHAPE[id]; 693 | const { generate, inject } = SHAPE[id][type]; 694 | 695 | const props = Object.assign({}, defaultProps, tree[1]); 696 | 697 | return { 698 | model: generate ? generate(props) : undefined, 699 | inject: inject ? inject(props) : undefined 700 | }; 701 | } 702 | 703 | if (isOp(id)) { 704 | const { defaultProps } = OP[id]; 705 | const { generate, inject, uniforms } = OP[id][type]; 706 | 707 | const props = Object.assign({}, defaultProps, tree[1]); 708 | const children = (tree[2] || []).map(child => compile(child, { type })); 709 | 710 | const finalInject = flatArray([ 711 | inject ? inject(props) : undefined, 712 | ...children.map(({ inject }) => inject) 713 | ]); 714 | 715 | const finalUniforms = flatArray([ 716 | uniforms ? uniforms(props) : undefined, 717 | ...children.map(({ uniforms }) => uniforms) 718 | ]); 719 | 720 | return { 721 | model: generate 722 | ? generate(props, children.map(({ model }) => model)) 723 | : undefined, 724 | uniforms: finalUniforms, 725 | inject: finalInject 726 | }; 727 | } 728 | } 729 | 730 | if (typeof args === "function") { 731 | return compile(args(), { type }); 732 | } 733 | 734 | console.warn("tree not recognized", tree); 735 | return; 736 | } 737 | 738 | const compileFunction = tree => { 739 | return compile(tree, { type: CPU }).model; 740 | }; 741 | 742 | const compileShader = tree => { 743 | const result = compile(tree, { type: GLSL }); 744 | 745 | return { 746 | model: result.model("p"), 747 | uniforms: without(result.uniforms, undefined).reduce( 748 | (a, b) => Object.assign(a, b), 749 | {} 750 | ), 751 | inject: without(uniq(flatArray(result.inject)), undefined).join("\n") 752 | }; 753 | }; 754 | 755 | module.exports = { preProcessTree, compile, compileFunction, compileShader }; 756 | -------------------------------------------------------------------------------- /packages/hiccup-sdf/glsl-helpers.js: -------------------------------------------------------------------------------- 1 | const createShaderModel = (sdfFragment, sdfLibrary) => ` 2 | ${sdfLibrary} 3 | 4 | vec2 doModel(vec3 p) { 5 | return vec2(${sdfFragment}, 0.0); 6 | } 7 | `; 8 | 9 | const createShaderFull = (sdfFragment, sdfLibrary) => ` 10 | precision mediump float; 11 | 12 | const int SDF_STEPS = 50; 13 | 14 | uniform float width; 15 | uniform float height; 16 | 17 | uniform float camTheta; 18 | uniform float camPhi; 19 | uniform float camDistance; 20 | 21 | ${createShaderModel(sdfFragment, sdfLibrary)} 22 | 23 | vec2 calcRayIntersection(vec3 rayOrigin, vec3 rayDirection, float maxDistance, float prec) { 24 | float latest = prec * 2.0; 25 | float dist = +0.0; 26 | float type = -1.0; 27 | vec2 res = vec2(-1.0, -1.0); 28 | 29 | for (int i = 0; i < SDF_STEPS; i++) { 30 | if (latest < prec || dist > maxDistance) { 31 | break; 32 | } 33 | 34 | vec2 result = doModel(rayOrigin + rayDirection * dist); 35 | 36 | latest = result.x; 37 | type = result.y; 38 | dist += latest; 39 | } 40 | 41 | if (dist < maxDistance) { 42 | res = vec2(dist, type); 43 | } 44 | 45 | return res; 46 | } 47 | 48 | vec2 raytrace(vec3 rayOrigin, vec3 rayDirection) { 49 | return calcRayIntersection(rayOrigin, rayDirection, 100.0, 0.0001); 50 | } 51 | 52 | vec3 calcNormal(vec3 pos, float eps) { 53 | const vec3 v1 = vec3( 1.0, -1.0, -1.0); 54 | const vec3 v2 = vec3(-1.0, -1.0, 1.0); 55 | const vec3 v3 = vec3(-1.0, 1.0, -1.0); 56 | const vec3 v4 = vec3( 1.0, 1.0, 1.0); 57 | 58 | return normalize( 59 | v1 * doModel(pos + v1 * eps).x + 60 | v2 * doModel(pos + v2 * eps).x + 61 | v3 * doModel(pos + v3 * eps).x + 62 | v4 * doModel(pos + v4 * eps).x 63 | ); 64 | } 65 | 66 | vec3 normal(vec3 pos) { 67 | return calcNormal(pos, 0.001); 68 | } 69 | 70 | mat3 calcLookAtMatrix(vec3 origin, vec3 target, float roll) { 71 | vec3 rr = vec3(sin(roll), cos(roll), 0.0); 72 | vec3 ww = normalize(target - origin); 73 | vec3 uu = normalize(cross(ww, rr)); 74 | vec3 vv = normalize(cross(uu, ww)); 75 | 76 | return mat3(uu, vv, ww); 77 | } 78 | 79 | vec3 getRay(mat3 camMat, vec2 screenPos, float lensLength) { 80 | return normalize(camMat * vec3(screenPos, lensLength)); 81 | } 82 | 83 | vec3 getRay(vec3 origin, vec3 target, vec2 screenPos, float lensLength) { 84 | mat3 camMat = calcLookAtMatrix(origin, target, 0.0); 85 | return getRay(camMat, screenPos, lensLength); 86 | } 87 | 88 | vec2 square(vec2 screenSize) { 89 | vec2 position = 2.0 * (gl_FragCoord.xy / screenSize.xy) - 1.0; 90 | position.x *= screenSize.x / screenSize.y; 91 | return position; 92 | } 93 | 94 | void orbitCamera(out vec3 rayOrigin, out vec3 rayDirection) { 95 | float PI = 3.14159265359; 96 | 97 | vec2 screenPos = square(vec2(width, height)); 98 | vec3 rayTarget = vec3(0.0); 99 | 100 | rayOrigin = vec3( 101 | camDistance * sin(camTheta * PI / 360.0) * cos(camPhi * PI / 360.0), 102 | camDistance * sin(camPhi * PI / 360.0), 103 | camDistance * cos(camTheta * PI / 360.0) * cos(camPhi * PI / 360.0) 104 | ); 105 | 106 | rayDirection = getRay(rayOrigin, rayTarget, screenPos, 2.0); 107 | } 108 | 109 | vec3 lighting(vec3 pos, vec3 nor, vec3 rayOrigin, vec3 rayDirection) { 110 | vec3 dir = normalize(vec3(-10.0, 10.0, 10.0)); 111 | vec3 col = vec3(1.0); 112 | vec3 dif = col * max(0.05, dot(dir, nor)); 113 | 114 | vec3 ambient = vec3(0.1); 115 | 116 | return dif + ambient; 117 | } 118 | 119 | void main () { 120 | vec3 color = vec3(0.2); 121 | vec3 rayOrigin, rayDirection; 122 | 123 | orbitCamera(rayOrigin, rayDirection); 124 | 125 | vec2 t = raytrace(rayOrigin, rayDirection); 126 | 127 | if (t.x > -0.5) { 128 | vec3 pos = rayOrigin + rayDirection * t.x; 129 | vec3 nor = normal(pos); 130 | 131 | color = lighting(pos, nor, rayOrigin, rayDirection); 132 | } 133 | 134 | gl_FragColor = vec4(color, 1.0); 135 | } 136 | `; 137 | 138 | module.exports = { 139 | createShader: createShaderFull, 140 | createShaderFull, 141 | createShaderModel 142 | }; 143 | -------------------------------------------------------------------------------- /packages/hiccup-sdf/glsl-stl.js: -------------------------------------------------------------------------------- 1 | const SD_SPHERE = ` 2 | float sdSphere(vec3 p, float r) { 3 | return length(p) - r; 4 | }`; 5 | 6 | const SD_BOX = ` 7 | float sdBox(vec3 p, vec3 s) { 8 | vec3 d = abs(p) - s; 9 | return min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d, 0.0)); 10 | }`; 11 | 12 | const SD_TORUS = ` 13 | float sdTorus(vec3 p, float r1, float r2) { 14 | vec2 q = vec2(length(p.xz) - r2, p.y); 15 | return length(q) - r1; 16 | }`; 17 | 18 | const SD_HEX = ` 19 | float sdHex(vec3 p, float r, float h) { 20 | vec3 q = abs(p); 21 | return max(q.z - h, max((q.x * 0.866025 + q.y * 0.5), q.y) - r); 22 | }`; 23 | 24 | const SD_TRIANGLE = ` 25 | float sdTriangle(vec3 p, float r, float h) { 26 | vec3 q = abs(p); 27 | return max(q.z - h, max(q.x * 0.866025 + p.y * 0.5, -p.y) - r * 0.5); 28 | }`; 29 | 30 | const SD_CAPSULE = ` 31 | float sdCapsule(vec3 p, vec3 a, vec3 b, float r) { 32 | vec3 pa = p - a; 33 | vec3 ba = b - a; 34 | 35 | float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); 36 | 37 | return length(pa - ba * h) - r; 38 | }`; 39 | 40 | const SD_CYLINDER = ` 41 | float sdCylinder(vec3 p, float r, float h) { 42 | vec2 d = abs(vec2(length(p.xz), p.y)) - vec2(r, h); 43 | return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)); 44 | }`; 45 | 46 | const OP_ROTATE = ` 47 | vec3 opRotate(vec3 p, vec3 r) { 48 | float PI = 3.14159265359; 49 | 50 | p.xz = cos(r.x * PI) * p.xz + sin(r.x * PI) * vec2(p.z, -p.x); 51 | p.xy = cos(r.y * PI) * p.xy + sin(r.y * PI) * vec2(p.y, -p.x); 52 | p.yz = cos(r.z * PI) * p.yz + sin(r.z * PI) * vec2(p.z, -p.y); 53 | 54 | return p; 55 | }`; 56 | 57 | const OP_MIRROR = ` 58 | vec3 opMirror(vec3 p, vec3 m) { 59 | if (abs(m.x) > 0.0) { p.x = abs(p.x) - m.x; } 60 | if (abs(m.y) > 0.0) { p.y = abs(p.y) - m.y; } 61 | if (abs(m.z) > 0.0) { p.z = abs(p.z) - m.z; } 62 | 63 | return p; 64 | }`; 65 | 66 | const OP_REPEAT = ` 67 | vec3 opRepeat(vec3 p, vec3 r) { 68 | vec3 halfR = r * 0.5; 69 | 70 | if (r.x > 0.0) { p.x = mod(p.x + halfR.x, r.x) - halfR.x; } 71 | if (r.y > 0.0) { p.y = mod(p.y + halfR.y, r.y) - halfR.y; } 72 | if (r.z > 0.0) { p.z = mod(p.z + halfR.z, r.z) - halfR.z; } 73 | 74 | return p; 75 | }`; 76 | 77 | const OP_REPEAT_POLAR = ` 78 | vec3 opRepeatPolar(vec3 p, float r) { 79 | float PI = 3.14159265359; 80 | 81 | float angle = 2.0 * PI / r; 82 | 83 | float a = atan(p.y, p.x) + angle / 2.0; 84 | float l = length(p.xy); 85 | 86 | a = mod(a, angle) - angle / 2.0; 87 | 88 | p.xy = vec2(cos(a), sin(a)) * l; 89 | 90 | return p; 91 | }`; 92 | 93 | const OP_UNION = ` 94 | float opUnion(float a, float b) { 95 | return min(a, b); 96 | }`; 97 | 98 | const OP_UNION_ROUND = ` 99 | float opUnionRound(float a, float b, float r) { 100 | vec2 u = max(vec2(r - a, r - b), vec2(0.0)); 101 | return max(r, min(a, b)) - length(u); 102 | }`; 103 | 104 | const OP_INTERSECTION = ` 105 | float opIntersection(float a, float b) { 106 | return max(a, b); 107 | }`; 108 | 109 | const OP_INTERSECTION_ROUND = ` 110 | float opIntersectionRound(float a, float b, float r) { 111 | vec2 u = max(vec2(r + a, r + b), vec2(0.0)); 112 | return min(-r, max(a, b)) + length(u); 113 | }`; 114 | 115 | const OP_DIFFERENCE = ` 116 | float opDifference(float a, float b) { 117 | return max(a, -b); 118 | }`; 119 | 120 | const OP_DIFFERENCE_ROUND = ` 121 | float opDifferenceRound(float a, float b, float r) { 122 | vec2 u = max(vec2(r + a, r - b), vec2(0.0)); 123 | return min(-r, max(a, -b)) + length(u); 124 | }`; 125 | 126 | const OP_BLEND = ` 127 | float opBlend(float a, float b, float k) { 128 | return (1.0 - k) * a + k * b; 129 | }`; 130 | 131 | const OP_ELONGATE = ` 132 | vec3 opElongate(vec3 p, vec3 s) { 133 | vec3 q = abs(p) - s; 134 | return vec3(max(q, 0.0)); 135 | }`; 136 | 137 | module.exports = { 138 | SD_SPHERE, 139 | SD_BOX, 140 | SD_TORUS, 141 | SD_HEX, 142 | SD_TRIANGLE, 143 | SD_CAPSULE, 144 | SD_CYLINDER, 145 | OP_ROTATE, 146 | OP_MIRROR, 147 | OP_REPEAT, 148 | OP_REPEAT_POLAR, 149 | OP_UNION, 150 | OP_UNION_ROUND, 151 | OP_INTERSECTION, 152 | OP_INTERSECTION_ROUND, 153 | OP_DIFFERENCE, 154 | OP_DIFFERENCE_ROUND, 155 | OP_BLEND, 156 | OP_ELONGATE 157 | }; 158 | -------------------------------------------------------------------------------- /packages/hiccup-sdf/index.js: -------------------------------------------------------------------------------- 1 | const dsl = require("./dsl"); 2 | const glslHelpers = require("./glsl-helpers"); 3 | const glslStl = require("./glsl-stl"); 4 | 5 | module.exports = dsl 6 | module.exports.glslHelpers = glslHelpers; 7 | module.exports.glslStl = glslStl; 8 | -------------------------------------------------------------------------------- /packages/hiccup-sdf/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hiccup-sdf", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@thi.ng/api": { 8 | "version": "4.2.3", 9 | "resolved": "https://registry.npmjs.org/@thi.ng/api/-/api-4.2.3.tgz", 10 | "integrity": "sha512-futpLOGn7yL4YTruWdwmmfFm3Rq0Vj8qiZv5imjrL2f8SztY7+73mw16fUSo9XKq45MLZnntMjhAcfHI0kHqHA==", 11 | "requires": { 12 | "@thi.ng/errors": "^0.1.11" 13 | } 14 | }, 15 | "@thi.ng/checks": { 16 | "version": "1.5.13", 17 | "resolved": "https://registry.npmjs.org/@thi.ng/checks/-/checks-1.5.13.tgz", 18 | "integrity": "sha512-RvWQ0gwz8+GC6nifuM1o2bUaWEMfKud3nzBqhMGapks68cuJpmBcLjW1QIubDvR05jLMU+ubgXeypU0+1C7NXA==" 19 | }, 20 | "@thi.ng/errors": { 21 | "version": "0.1.11", 22 | "resolved": "https://registry.npmjs.org/@thi.ng/errors/-/errors-0.1.11.tgz", 23 | "integrity": "sha512-SFV69GvN1Tuvy8x1zHUPkyWn91KfCEVXIEnZSXgJTGdPabopheQjeOo/Yd4ta83lzhLbtopumu0O9gkZYtkgxw==" 24 | }, 25 | "@thi.ng/vectors": { 26 | "version": "0.2.1", 27 | "resolved": "https://registry.npmjs.org/@thi.ng/vectors/-/vectors-0.2.1.tgz", 28 | "integrity": "sha512-/KqSXN1CiB/pWKuJ4u8lafiFfVurpYM6mGIvLy8PrLxt4Riy5F5llAEtEGOgbAIBDs1y6ZBfScWh3AcNQwOwNw==", 29 | "requires": { 30 | "@thi.ng/api": "^4.1.0", 31 | "@thi.ng/checks": "^1.5.7" 32 | } 33 | }, 34 | "lodash": { 35 | "version": "4.17.11", 36 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", 37 | "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/hiccup-sdf/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hiccup-sdf", 3 | "version": "0.0.10", 4 | "description": "hiccup-like syntax for generating SDFs on CPU and GPU", 5 | "main": "index.js", 6 | "author": "Szymon Kaliski (http://szymonkaliski.com)", 7 | "license": "MIT", 8 | "dependencies": { 9 | "@thi.ng/vectors": "^0.2.0", 10 | "lodash": "^4.17.10" 11 | }, 12 | "gitHead": "e7f441600f1b472fcf0ef1396399c465ea258d0e" 13 | } 14 | -------------------------------------------------------------------------------- /packages/hiccup-sdf/test/test.js: -------------------------------------------------------------------------------- 1 | const dsl = require("../"); 2 | 3 | const points = Array.from({ length: 10 }).map(() => [ 4 | Math.random(), 5 | Math.random(), 6 | Math.random() 7 | ]); 8 | 9 | const treeGLSL = [ 10 | "union", 11 | { r: 0.0 }, 12 | [ 13 | [ 14 | "map", 15 | { 16 | data: { points }, 17 | map: props => [ 18 | "translate", 19 | { t: `${props.points}.xyz` }, 20 | [["sphere", { r: 0.2 }]] 21 | ], 22 | reduce: ["union", { r: 0.1 }] 23 | } 24 | ], 25 | ["sphere", { r: 1.0 }] 26 | ] 27 | ]; 28 | 29 | const { inject, uniforms, model } = dsl.compileShader(treeGLSL); 30 | 31 | console.log("- inject\n"); 32 | console.log(inject); 33 | console.log(); 34 | console.log("- uniforms\n"); 35 | console.log(uniforms); 36 | console.log(); 37 | console.log("- model\n"); 38 | console.log(model); 39 | console.log(); 40 | 41 | // prettier-ignore 42 | const treeCPU = [ 43 | "union", 44 | {}, 45 | [ 46 | ["sphere"], 47 | ["box"], 48 | [ 49 | "translate", 50 | { t: [0, 0, -1] }, 51 | [ 52 | ["box"] 53 | ] 54 | ] 55 | ] 56 | ]; 57 | 58 | const cpuFn = dsl.compileFunction(treeCPU); 59 | 60 | console.log("- cpuFn\n"); 61 | console.log(cpuFn.toString()); 62 | console.log("\n- cpuFn([0, 0, 0])\n"); 63 | console.log(cpuFn([0, 0, 0])); 64 | console.log(); 65 | --------------------------------------------------------------------------------