├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── example ├── index.js ├── package-lock.json └── package.json ├── index.js ├── package-lock.json ├── package.json └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | test 7 | test.js 8 | demo/ 9 | screenshot.png 10 | .npmignore 11 | LICENSE.md 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2017 Marcin Ignac 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](screenshot.png) 2 | 3 | # spline-points 4 | 5 | Subdivides a 3d path into spline points 6 | 7 | # Example 8 | 9 | ```sh 10 | npm install spline-points --save 11 | ``` 12 | 13 | ```javascript 14 | const splinePoints = require('spline-points') 15 | 16 | const points = [[0, 0, 0], [1, 0, 0], [2, 1, 0]] 17 | const smoothPoints = splinePoints(points, { segmentLength: 0.1 }) 18 | // smoothPoints = [[0, 0, 0], [0.09, -0.01, 0], [0.19, -0.02, 0], ...] 19 | ``` 20 | 21 | ### Running the example 22 | 23 | ```sh 24 | cd spline-points/example 25 | npm install 26 | budo index.js --open 27 | ``` 28 | 29 | # API 30 | 31 | ```javascript 32 | var splinePoints = require('spline-points') 33 | ``` 34 | 35 | ### `smoothPoints = splinePoints(points, opts)` 36 | 37 | Subdivides a 3d path into spline points 38 | 39 | - `points` : Array of vec3 - path of 3d points [x, y, z] to subdivide 40 | - `opts` 41 | - `closed` : Bool - is the path closed?, `false` 42 | - `segmentLength` : Number - distance between generated points 43 | 44 | ## License 45 | 46 | MIT, see [LICENSE.md](http://github.com/vorg/geom-builder/blob/master/LICENSE.md) for details. 47 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | // const gl = require('pex-gl')(1280, 720) 2 | // const regl = require('regl')(gl) 3 | const random = require('pex-random') 4 | const createContext = require('pex-context') 5 | const createCube = require('primitive-cube') 6 | const mat4 = require('pex-math/mat4') 7 | const vec3 = require('pex-math/vec3') 8 | 9 | const splinePoints = require('..') 10 | 11 | const p = [[0, 0, 0], [1, 0, 0], [2, 1, 0]] 12 | const sp = splinePoints(p, { segmentLength: 0.1 }) 13 | const ctx = createContext({ width: 1280, height: 720 }) 14 | const cube = createCube(0.1, 0.02, 0.02) 15 | document.body.style.background = 'black' 16 | 17 | const camera = require('pex-cam/perspective')({ 18 | fov: Math.PI / 4, 19 | aspect: ctx.gl.drawingBufferWidth / ctx.gl.drawingBufferHeight 20 | }) 21 | const orbiter = require('pex-cam/orbiter')({ camera: camera }) 22 | 23 | 24 | random.seed(0) 25 | 26 | const minR = 0.2 27 | const maxR = 1.2 28 | const numbers = [] 29 | const points = [] 30 | const numPoints = 9 31 | 32 | let sum = 0 33 | for (let i = 0; i < numPoints; i++) { 34 | var f = random.float(minR, maxR) 35 | sum += f 36 | numbers.push(f) 37 | } 38 | 39 | let angle = 0 40 | for (let i = 0; i < numPoints; i++) { 41 | var x = numbers[i] * Math.cos(angle) 42 | var y = numbers[i] * Math.sin(angle) 43 | points.push([x, y, 0]) 44 | angle += Math.PI * 2 * numbers[i] / sum 45 | } 46 | 47 | const smoothPoints = splinePoints(points, { segmentLength: 0.05, closed: true }) 48 | 49 | // close the line strip 50 | smoothPoints.push(smoothPoints[0]) 51 | 52 | const clearCmd = { 53 | pass: ctx.pass({ 54 | clearColor: [1, 1, 1, 1], 55 | clearDepth: 1 56 | }) 57 | } 58 | 59 | const drawLineStripCmd = { 60 | pipeline: ctx.pipeline({ 61 | depthTest: true, 62 | vert: ` 63 | attribute vec3 aPosition; 64 | uniform mat4 uProjectionMatrix; 65 | uniform mat4 uViewMatrix; 66 | uniform mat4 uModelMatrix; 67 | void main () { 68 | gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aPosition, 1.0); 69 | } 70 | `, 71 | frag: ` 72 | precision mediump float; 73 | uniform vec4 uColor; 74 | void main () { 75 | gl_FragColor.rgb = uColor.rgb; 76 | gl_FragColor.a = 1.0; 77 | } 78 | `, 79 | primitive: ctx.Primitive.LineStrip 80 | }), 81 | attributes: { 82 | }, 83 | uniforms: { 84 | uColor: [0.3, 0.3, 0.3, 1], 85 | uProjectionMatrix: camera.projectionMatrix, //mat4.perspective(mat4.create(), Math.PI / 4, window.innerWidth / window.innerHeight, 0.1, 100), 86 | uViewMatrix: camera.viewMatrix,//mat4.lookAt(mat4.create(), [2, 2, 5], [0, 0, 0], [0, 1, 0]), 87 | uModelMatrix: mat4.translate(mat4.create(), [0, 0, 0]) 88 | } 89 | } 90 | 91 | const drawLinesCmd = { 92 | pipeline: ctx.pipeline({ 93 | depthTest: true, 94 | vert: ` 95 | attribute vec3 aPosition; 96 | uniform mat4 uProjectionMatrix; 97 | uniform mat4 uViewMatrix; 98 | uniform mat4 uModelMatrix; 99 | void main () { 100 | gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aPosition, 1.0); 101 | } 102 | `, 103 | frag: ` 104 | precision mediump float; 105 | uniform vec4 uColor; 106 | void main () { 107 | gl_FragColor.rgb = uColor.rgb; 108 | gl_FragColor.a = 1.0; 109 | } 110 | `, 111 | primitive: ctx.Primitive.Lines 112 | }), 113 | attributes: { 114 | }, 115 | uniforms: { 116 | uColor: [0.3, 0.3, 0.3, 1], 117 | uProjectionMatrix: camera.projectionMatrix, //mat4.perspective(mat4.create(), Math.PI / 4, window.innerWidth / window.innerHeight, 0.1, 100), 118 | uViewMatrix: camera.viewMatrix,//mat4.lookAt(mat4.create(), [2, 2, 5], [0, 0, 0], [0, 1, 0]), 119 | uModelMatrix: mat4.translate(mat4.create(), [0, 0, 0]) 120 | } 121 | } 122 | 123 | var pointRects = [] 124 | smoothPoints.forEach((p) => { 125 | var x = p[0] 126 | var y = p[1] 127 | var r = 0.01 128 | pointRects.push([x - r, y - r, 0], [x + r, y - r, 0]) 129 | pointRects.push([x + r, y - r, 0], [x + r, y + r, 0]) 130 | pointRects.push([x + r, y + r, 0], [x - r, y + r, 0]) 131 | pointRects.push([x - r, y + r, 0], [x - r, y - r, 0]) 132 | }) 133 | 134 | var positionsBuf = ctx.vertexBuffer(points) 135 | var smoothPositionsBuf = ctx.vertexBuffer(smoothPoints) 136 | var pointRectsBuf = ctx.vertexBuffer(pointRects) 137 | 138 | var debugOnce = false 139 | 140 | var green = [39 / 255, 174 / 255, 96 / 255, 1] 141 | 142 | ctx.frame(() => { 143 | ctx.debug(debugOnce) 144 | debugOnce = false 145 | ctx.submit(clearCmd) 146 | ctx.submit(drawLineStripCmd, { 147 | attributes: { aPosition: positionsBuf }, 148 | count: points.length 149 | }) 150 | ctx.submit(drawLineStripCmd, { 151 | attributes: { aPosition: smoothPositionsBuf }, 152 | count: smoothPoints.length, 153 | uniforms: { 154 | uColor: green 155 | } 156 | }) 157 | ctx.submit(drawLinesCmd, { 158 | attributes: { aPosition: pointRectsBuf }, 159 | count: pointRects.length, 160 | uniforms: { 161 | uColor: green 162 | } 163 | }) 164 | }) 165 | -------------------------------------------------------------------------------- /example/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "balanced-match": { 8 | "version": "1.0.0", 9 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 10 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 11 | }, 12 | "brace-expansion": { 13 | "version": "1.1.8", 14 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", 15 | "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", 16 | "requires": { 17 | "balanced-match": "1.0.0", 18 | "concat-map": "0.0.1" 19 | } 20 | }, 21 | "concat-map": { 22 | "version": "0.0.1", 23 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 24 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 25 | }, 26 | "deep-equal": { 27 | "version": "1.0.1", 28 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", 29 | "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" 30 | }, 31 | "define-properties": { 32 | "version": "1.1.2", 33 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", 34 | "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", 35 | "requires": { 36 | "foreach": "2.0.5", 37 | "object-keys": "1.0.11" 38 | } 39 | }, 40 | "defined": { 41 | "version": "1.0.0", 42 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", 43 | "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" 44 | }, 45 | "es-abstract": { 46 | "version": "1.10.0", 47 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.10.0.tgz", 48 | "integrity": "sha512-/uh/DhdqIOSkAWifU+8nG78vlQxdLckUdI/sPgy0VhuXi2qJ7T8czBmqIYtLQVpCIFYafChnsRsB5pyb1JdmCQ==", 49 | "requires": { 50 | "es-to-primitive": "1.1.1", 51 | "function-bind": "1.1.1", 52 | "has": "1.0.1", 53 | "is-callable": "1.1.3", 54 | "is-regex": "1.0.4" 55 | } 56 | }, 57 | "es-to-primitive": { 58 | "version": "1.1.1", 59 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", 60 | "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", 61 | "requires": { 62 | "is-callable": "1.1.3", 63 | "is-date-object": "1.0.1", 64 | "is-symbol": "1.0.1" 65 | } 66 | }, 67 | "for-each": { 68 | "version": "0.3.2", 69 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.2.tgz", 70 | "integrity": "sha1-LEBFC5NI6X8oEyJZO6lnBLmr1NQ=", 71 | "requires": { 72 | "is-function": "1.0.1" 73 | } 74 | }, 75 | "foreach": { 76 | "version": "2.0.5", 77 | "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", 78 | "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" 79 | }, 80 | "fs.realpath": { 81 | "version": "1.0.0", 82 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 83 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 84 | }, 85 | "function-bind": { 86 | "version": "1.1.1", 87 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 88 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 89 | }, 90 | "glob": { 91 | "version": "7.1.2", 92 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 93 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 94 | "requires": { 95 | "fs.realpath": "1.0.0", 96 | "inflight": "1.0.6", 97 | "inherits": "2.0.3", 98 | "minimatch": "3.0.4", 99 | "once": "1.4.0", 100 | "path-is-absolute": "1.0.1" 101 | } 102 | }, 103 | "has": { 104 | "version": "1.0.1", 105 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", 106 | "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", 107 | "requires": { 108 | "function-bind": "1.1.1" 109 | } 110 | }, 111 | "inflight": { 112 | "version": "1.0.6", 113 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 114 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 115 | "requires": { 116 | "once": "1.4.0", 117 | "wrappy": "1.0.2" 118 | } 119 | }, 120 | "inherits": { 121 | "version": "2.0.3", 122 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 123 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 124 | }, 125 | "interpolate-angle": { 126 | "version": "1.0.2", 127 | "resolved": "https://registry.npmjs.org/interpolate-angle/-/interpolate-angle-1.0.2.tgz", 128 | "integrity": "sha1-q6mSFyJy4ucJOMlTC0HcNcV+5do=", 129 | "requires": { 130 | "lerp": "1.0.3" 131 | } 132 | }, 133 | "is-browser": { 134 | "version": "2.0.1", 135 | "resolved": "https://registry.npmjs.org/is-browser/-/is-browser-2.0.1.tgz", 136 | "integrity": "sha1-i/C695mpxi/Z3lvO5M8zl8PnUpo=" 137 | }, 138 | "is-callable": { 139 | "version": "1.1.3", 140 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", 141 | "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=" 142 | }, 143 | "is-date-object": { 144 | "version": "1.0.1", 145 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", 146 | "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" 147 | }, 148 | "is-function": { 149 | "version": "1.0.1", 150 | "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", 151 | "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=" 152 | }, 153 | "is-plask": { 154 | "version": "1.1.1", 155 | "resolved": "https://registry.npmjs.org/is-plask/-/is-plask-1.1.1.tgz", 156 | "integrity": "sha1-3mOSZXYfKK939JtBI4tnp/R8sgg=", 157 | "requires": { 158 | "plask-wrap": "1.0.1" 159 | } 160 | }, 161 | "is-regex": { 162 | "version": "1.0.4", 163 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", 164 | "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", 165 | "requires": { 166 | "has": "1.0.1" 167 | } 168 | }, 169 | "is-symbol": { 170 | "version": "1.0.1", 171 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", 172 | "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=" 173 | }, 174 | "latlon-to-xyz": { 175 | "version": "1.0.1", 176 | "resolved": "https://registry.npmjs.org/latlon-to-xyz/-/latlon-to-xyz-1.0.1.tgz", 177 | "integrity": "sha512-OF65aE60Y9aMOibxWFC2Vl7EN2BOtMKabm0t97+VaVgR9LoStZHbNNMc8dS/LG6iE1BpEXmnoH85wWr1drp/Tw==" 178 | }, 179 | "lerp": { 180 | "version": "1.0.3", 181 | "resolved": "https://registry.npmjs.org/lerp/-/lerp-1.0.3.tgz", 182 | "integrity": "sha1-oYyJaPkXiW3hXM/MKNVaa3Med24=" 183 | }, 184 | "minimatch": { 185 | "version": "3.0.4", 186 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 187 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 188 | "requires": { 189 | "brace-expansion": "1.1.8" 190 | } 191 | }, 192 | "minimist": { 193 | "version": "1.2.0", 194 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 195 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 196 | }, 197 | "object-inspect": { 198 | "version": "1.3.0", 199 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.3.0.tgz", 200 | "integrity": "sha512-OHHnLgLNXpM++GnJRyyhbr2bwl3pPVm4YvaraHrRvDt/N3r+s/gDVHciA7EJBTkijKXj61ssgSAikq1fb0IBRg==" 201 | }, 202 | "object-keys": { 203 | "version": "1.0.11", 204 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", 205 | "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" 206 | }, 207 | "once": { 208 | "version": "1.4.0", 209 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 210 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 211 | "requires": { 212 | "wrappy": "1.0.2" 213 | } 214 | }, 215 | "path-is-absolute": { 216 | "version": "1.0.1", 217 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 218 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 219 | }, 220 | "path-parse": { 221 | "version": "1.0.5", 222 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", 223 | "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" 224 | }, 225 | "performance-now": { 226 | "version": "2.1.0", 227 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 228 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 229 | }, 230 | "pex-cam": { 231 | "version": "2.4.3", 232 | "resolved": "https://registry.npmjs.org/pex-cam/-/pex-cam-2.4.3.tgz", 233 | "integrity": "sha512-K9LbIKAz6fc12Wy9z+SH68U610VjbbU+GPmWAj/+7/XGCp8cd5HgECJ80hkl4Yho91Kyi++PiPJhzzwx0RWR3Q==", 234 | "requires": { 235 | "interpolate-angle": "1.0.2", 236 | "latlon-to-xyz": "1.0.1", 237 | "pex-geom": "2.0.2", 238 | "pex-math": "2.1.0", 239 | "raf": "3.4.0", 240 | "xyz-to-latlon": "1.0.2" 241 | }, 242 | "dependencies": { 243 | "pex-math": { 244 | "version": "2.1.0", 245 | "resolved": "https://registry.npmjs.org/pex-math/-/pex-math-2.1.0.tgz", 246 | "integrity": "sha512-jieN3PH9i1Gtb8XMiAaj5XLq7YJr3/sbhBXhvnzmuQKO1uqRdzWCcW+cZsAXhvBHrGnqxm0vElKekxpKWmyICg==" 247 | } 248 | } 249 | }, 250 | "pex-context": { 251 | "version": "2.1.2", 252 | "resolved": "https://registry.npmjs.org/pex-context/-/pex-context-2.1.2.tgz", 253 | "integrity": "sha512-iUxtQnLdXwQrZ9NkYqKljU3m7u5dTenTfX/+nV1BJk1r5Vfhawr5hRZZ72KLpp2oEAGBYYxwjHZnjjN4iToCNQ==", 254 | "requires": { 255 | "debug": "2.6.9", 256 | "is-browser": "2.0.1", 257 | "pex-gl": "2.4.1", 258 | "raf": "3.4.0", 259 | "ramda": "0.23.0" 260 | }, 261 | "dependencies": { 262 | "debug": { 263 | "version": "2.6.9", 264 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 265 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 266 | "requires": { 267 | "ms": "2.0.0" 268 | } 269 | }, 270 | "ms": { 271 | "version": "2.0.0", 272 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 273 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 274 | }, 275 | "pex-gl": { 276 | "version": "2.4.1", 277 | "resolved": "https://registry.npmjs.org/pex-gl/-/pex-gl-2.4.1.tgz", 278 | "integrity": "sha1-MqZNLqathNo31KvrcfvHq5Wn0W4=", 279 | "requires": { 280 | "is-plask": "1.1.1" 281 | } 282 | } 283 | } 284 | }, 285 | "pex-geom": { 286 | "version": "2.0.2", 287 | "resolved": "https://registry.npmjs.org/pex-geom/-/pex-geom-2.0.2.tgz", 288 | "integrity": "sha512-DRdxdD1LF64RRTTX6/VEGhXxNuMFXdKSmLg7Lh5TvhvkMTXJxO4myRBqIj7yljnPntJ/TdAyUSaYoHRWk4R32w==", 289 | "requires": { 290 | "pex-math": "2.1.0", 291 | "tape": "4.8.0" 292 | }, 293 | "dependencies": { 294 | "pex-math": { 295 | "version": "2.1.0", 296 | "resolved": "https://registry.npmjs.org/pex-math/-/pex-math-2.1.0.tgz", 297 | "integrity": "sha512-jieN3PH9i1Gtb8XMiAaj5XLq7YJr3/sbhBXhvnzmuQKO1uqRdzWCcW+cZsAXhvBHrGnqxm0vElKekxpKWmyICg==" 298 | } 299 | } 300 | }, 301 | "pex-math": { 302 | "version": "1.0.1", 303 | "resolved": "https://registry.npmjs.org/pex-math/-/pex-math-1.0.1.tgz", 304 | "integrity": "sha1-NRvf2t+Cqnksfq08XSyGO68zJHg=" 305 | }, 306 | "pex-random": { 307 | "version": "1.0.1", 308 | "resolved": "https://registry.npmjs.org/pex-random/-/pex-random-1.0.1.tgz", 309 | "integrity": "sha1-Cwp9kOLgV9V3d+2ROjm3IlUxm+U=", 310 | "requires": { 311 | "seedrandom": "2.4.3", 312 | "simplex-noise": "2.1.1" 313 | } 314 | }, 315 | "plask-wrap": { 316 | "version": "1.0.1", 317 | "resolved": "https://registry.npmjs.org/plask-wrap/-/plask-wrap-1.0.1.tgz", 318 | "integrity": "sha1-xfXBtdh0wcfZtxf3chJvJauzHd8=" 319 | }, 320 | "raf": { 321 | "version": "3.4.0", 322 | "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.0.tgz", 323 | "integrity": "sha512-pDP/NMRAXoTfrhCfyfSEwJAKLaxBU9eApMeBPB1TkDouZmvPerIClV8lTAd+uF8ZiTaVl69e1FCxQrAd/VTjGw==", 324 | "requires": { 325 | "performance-now": "2.1.0" 326 | } 327 | }, 328 | "ramda": { 329 | "version": "0.23.0", 330 | "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.23.0.tgz", 331 | "integrity": "sha1-zNE//3NJepOXTj6GMnv9h71ujis=" 332 | }, 333 | "resolve": { 334 | "version": "1.4.0", 335 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", 336 | "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==", 337 | "requires": { 338 | "path-parse": "1.0.5" 339 | } 340 | }, 341 | "resumer": { 342 | "version": "0.0.0", 343 | "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", 344 | "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", 345 | "requires": { 346 | "through": "2.3.8" 347 | } 348 | }, 349 | "seedrandom": { 350 | "version": "2.4.3", 351 | "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz", 352 | "integrity": "sha1-JDhQTa0zkXMUv/GKxNeU8W1qrsw=" 353 | }, 354 | "simplex-noise": { 355 | "version": "2.1.1", 356 | "resolved": "https://registry.npmjs.org/simplex-noise/-/simplex-noise-2.1.1.tgz", 357 | "integrity": "sha1-kuZ4XxaptanxMdHplsM3q/X0KF0=" 358 | }, 359 | "string.prototype.trim": { 360 | "version": "1.1.2", 361 | "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", 362 | "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", 363 | "requires": { 364 | "define-properties": "1.1.2", 365 | "es-abstract": "1.10.0", 366 | "function-bind": "1.1.1" 367 | } 368 | }, 369 | "tape": { 370 | "version": "4.8.0", 371 | "resolved": "https://registry.npmjs.org/tape/-/tape-4.8.0.tgz", 372 | "integrity": "sha512-TWILfEnvO7I8mFe35d98F6T5fbLaEtbFTG/lxWvid8qDfFTxt19EBijWmB4j3+Hoh5TfHE2faWs73ua+EphuBA==", 373 | "requires": { 374 | "deep-equal": "1.0.1", 375 | "defined": "1.0.0", 376 | "for-each": "0.3.2", 377 | "function-bind": "1.1.1", 378 | "glob": "7.1.2", 379 | "has": "1.0.1", 380 | "inherits": "2.0.3", 381 | "minimist": "1.2.0", 382 | "object-inspect": "1.3.0", 383 | "resolve": "1.4.0", 384 | "resumer": "0.0.0", 385 | "string.prototype.trim": "1.1.2", 386 | "through": "2.3.8" 387 | } 388 | }, 389 | "through": { 390 | "version": "2.3.8", 391 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 392 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 393 | }, 394 | "wrappy": { 395 | "version": "1.0.2", 396 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 397 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 398 | }, 399 | "xyz-to-latlon": { 400 | "version": "1.0.2", 401 | "resolved": "https://registry.npmjs.org/xyz-to-latlon/-/xyz-to-latlon-1.0.2.tgz", 402 | "integrity": "sha512-iTQe5HIzAE0r2L2u2abs17s/SekCCUAlIHzYnHHo10aR0B1iKRSGye33z7a52IMJoUUMTpPwNUTNIy+OhZVRIg==" 403 | } 404 | } 405 | } 406 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Marcin Ignac (http://marcinignac.com/)", 10 | "license": "MIT", 11 | "dependencies": { 12 | "budo": "^9.2.1", 13 | "glslify": "^6.0.1", 14 | "pex-cam": "^2.4.3", 15 | "pex-context": "^2.1.2", 16 | "pex-gl": "^0.2.1", 17 | "pex-math": "^1.0.1", 18 | "pex-random": "^1.0.1", 19 | "regl": "^1.3.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | function distance (a, b) { 2 | return distance3(a, b[0], b[1], b[2]) 3 | } 4 | 5 | function distance3 (a, x, y, z) { 6 | var dx = x - a[0] 7 | var dy = y - a[1] 8 | var dz = z - a[2] 9 | return Math.sqrt(dx * dx + dy * dy + dz * dz) 10 | } 11 | 12 | function interpolate (p0, p1, p2, p3, t) { 13 | var v0 = (p2 - p0) * 0.5 14 | var v1 = (p3 - p1) * 0.5 15 | var t2 = t * t 16 | var t3 = t * t2 17 | return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1 18 | } 19 | 20 | function splinePoints (points, options) { 21 | var isClosedPath = options && options.closed 22 | var segmentLength = (options && options.segmentLength) ? options.segmentLength : 0 23 | 24 | var subpoints = [] 25 | var numPoints = isClosedPath ? points.length : points.length - 1 26 | for (let i = 0; i < numPoints; i++) { 27 | var c0, c1, c2, c3 28 | if (isClosedPath) { 29 | c0 = (i - 1 + points.length) % points.length 30 | c1 = i % points.length 31 | c2 = (i + 1) % points.length 32 | c3 = (i + 2) % points.length 33 | } else { 34 | c0 = i === 0 ? i : i - 1 35 | c1 = i 36 | c2 = i > points.length - 2 ? i : i + 1 37 | c3 = i > points.length - 3 ? i : i + 2 38 | } 39 | var numSteps = 3 40 | if (segmentLength) { 41 | var dist = distance(points[c1], points[c2]) 42 | numSteps = Math.max(1, dist / segmentLength) 43 | } 44 | if (segmentLength) { 45 | numSteps *= 100 // generate 10x more points than necessary 46 | } 47 | var step = 1 / numSteps 48 | for (var t = 0; t < 1; t += step) { 49 | var x = interpolate(points[c0][0], points[c1][0], points[c2][0], points[c3][0], t) 50 | var y = interpolate(points[c0][1], points[c1][1], points[c2][1], points[c3][1], t) 51 | var z = interpolate(points[c0][2], points[c1][2], points[c2][2], points[c3][2], t) 52 | subpoints.push([x, y, z]) 53 | } 54 | } 55 | 56 | var finalPoints = [] 57 | var travelledDist = 0 58 | var prevPoint = points[0] 59 | finalPoints.push(prevPoint) 60 | for (let i = 0; i < subpoints.length; i++) { 61 | var p = subpoints[i] 62 | travelledDist += distance(prevPoint, p) 63 | if (travelledDist >= segmentLength) { 64 | finalPoints.push(p) 65 | travelledDist -= segmentLength 66 | } 67 | prevPoint = p 68 | } 69 | 70 | if (!isClosedPath && distance(finalPoints[finalPoints.length - 1], subpoints[subpoints.length - 1]) > segmentLength / 2) { 71 | finalPoints.push(subpoints[subpoints.length - 1]) 72 | } 73 | return finalPoints 74 | } 75 | 76 | module.exports = splinePoints 77 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spline-points", 3 | "version": "0.2.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "primitive-cube": { 8 | "version": "2.0.1", 9 | "resolved": "https://registry.npmjs.org/primitive-cube/-/primitive-cube-2.0.1.tgz", 10 | "integrity": "sha1-iqW8PL/y7+6DzOdM/KuKybW0awY=" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spline-points", 3 | "version": "0.2.1", 4 | "description": "Subdivides path into spline points", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Marcin Ignac", 9 | "email": "marcin.ignac@gmail.com", 10 | "url": "https://github.com/vorg" 11 | }, 12 | "dependencies": { 13 | "primitive-cube": "^2.0.1" 14 | }, 15 | "devDependencies": {}, 16 | "scripts": { 17 | "test": "node test.js" 18 | }, 19 | "keywords": [], 20 | "repository": { 21 | "type": "git", 22 | "url": "git://github.com/vorg/spline-points.git" 23 | }, 24 | "homepage": "https://github.com/vorg/spline-points", 25 | "bugs": { 26 | "url": "https://github.com/vorg/spline-points/issues" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vorg/spline-points/96ca464f543c609fa680947e6e2a2030d199890e/screenshot.png --------------------------------------------------------------------------------