From aa4cd269a6f610cc5bb93e98279edddd1fb56542 Mon Sep 17 00:00:00 2001 From: mattdesl Date: Mon, 2 Jul 2018 15:48:36 -0400 Subject: [PATCH] doing lots of experiments and working on various sketching utilities --- examples/canvas-distributions-2.js | 124 +++ examples/canvas-distributions.js | 120 +++ examples/canvas-random-test.js | 213 +++++ examples/canvas-rough-orange.js | 87 ++ examples/ktx-test.js | 45 - examples/util/geom.js | 160 ++++ examples/util/ktx-idea.js | 27 - examples/util/math.js | 56 +- examples/util/polyline-util.js | 77 ++ examples/util/primitive-polyline.js | 157 ++++ examples/util/random.js | 205 ++++- examples/util/webgl-2d-ideas.js | 56 ++ examples/util/{ktx.js => webgl-2d.js} | 177 +++- examples/webgl-2d-test.js | 135 +++ lib/index.js | 8 + package-lock.json | 1114 ++++++------------------- package.json | 14 +- 17 files changed, 1766 insertions(+), 1009 deletions(-) create mode 100644 examples/canvas-distributions-2.js create mode 100644 examples/canvas-distributions.js create mode 100644 examples/canvas-random-test.js create mode 100644 examples/canvas-rough-orange.js delete mode 100644 examples/ktx-test.js create mode 100644 examples/util/geom.js delete mode 100644 examples/util/ktx-idea.js create mode 100644 examples/util/polyline-util.js create mode 100644 examples/util/primitive-polyline.js create mode 100644 examples/util/webgl-2d-ideas.js rename examples/util/{ktx.js => webgl-2d.js} (56%) create mode 100644 examples/webgl-2d-test.js diff --git a/examples/canvas-distributions-2.js b/examples/canvas-distributions-2.js new file mode 100644 index 0000000..e512608 --- /dev/null +++ b/examples/canvas-distributions-2.js @@ -0,0 +1,124 @@ +const canvasSketch = require('canvas-sketch'); +const Random = require('./util/random'); +const { lerp, clamp, clamp01 } = require('./util/math'); +const { cubicSpline, resampleLineBySpacing, getPolylinePerimeter } = require('./util/geom'); +const { vec2 } = require('gl-matrix'); +const polylineNormals = require('./util/polyline-util'); +const defined = require('defined'); + +const colors = { + background: '#f4d9be', + foreground: '#ff911c', + pen: '#1975ff' +}; + +const settings = { + // animation: true, + // When exporting, use the seed as the suffix + // This way we can reproduce it more easily later + animation: true, + dimensions: [ 1280, 1280 ] + // units: 'in', + // pixelsPerInch: 300 +}; + +const sketch = ({ context, width, height }) => { + const margin = -1; + + const circle = (x, y, radius, steps = 8) => { + const points = []; + for (let i = 0; i < steps; i++) { + const t = i / steps; + const angle = Math.PI * 2 * t + Math.PI / 4; + const px = x + Math.cos(angle) * radius; + const py = y + Math.sin(angle) * radius; + points.push([ px, py ]); + } + return points; + }; + + const rough = (path, opt = {}) => { + const roughness = defined(opt.roughness, 1); + const jitter = defined(opt.jitter, 1); + const spacing = defined(opt.spacing, 0.1); + const closed = opt.closed; + const isSpline = opt.spline; + const tension = defined(opt.tension, 0.5); + const segments = defined(opt.segments, 25); + const dist = opt.dist || ((...args) => Random.gaussian(...args)); + + const perturbed = path.map(point => { + const p = dist() * jitter; + return vec2.scaleAndAdd([], point, Random.onCircle(), p); + }); + const spline = isSpline ? cubicSpline(perturbed, tension, segments, closed) : perturbed; + let points = resampleLineBySpacing(spline, spacing, closed); + points = points.map(point => { + const r = dist() * roughness; + return vec2.scaleAndAdd([], point, Random.onCircle(), r); + }); + return points; + }; + + const path = circle(width / 2, height / 2, width * 0.175, 8); + const lineWidth = 3; + const shapes = [ { + spacing: 15, + jitter: 7, + roughness: 0.5, + dist: () => Random.gaussian(), + spline: true, + segments: 100, + tension: 0.5, + closed: true + }, { + spacing: 15, + jitter: 10, + roughness: 0.75, + dist: () => Random.gaussian(), + spline: true, + segments: 100, + tension: 0.5, + closed: true + } ].map(opts => rough(path, opts)); + + const backgroundShape = rough(circle(width / 2, height / 2, width * 0.575, 4), { + spacing: 10, + jitter: 10, + roughness: 0.5, + spline: true, + segments: 100, + tension: 0.025, + closed: true + }); + + // Render the shapes + return ({ time, frame }) => { + context.fillStyle = 'white'; + context.lineJoin = 'round'; + context.fillRect(0, 0, width, height); + + context.beginPath(); + backgroundShape.forEach(point => { + context.lineTo(point[0], point[1]); + }); + context.closePath(); + context.strokeStyle = context.fillStyle = colors.background; + context.fill(); + + shapes.forEach((points, i) => { + context.lineWidth = lineWidth; + context.beginPath(); + points.forEach(point => { + context.lineTo(point[0], point[1]); + }); + context.closePath(); + const stroke = i > 0; + context.strokeStyle = context.fillStyle = stroke ? colors.pen : colors.foreground; + if (stroke) context.stroke(); + else context.fill(); + }); + }; +}; + +canvasSketch(sketch, settings); diff --git a/examples/canvas-distributions.js b/examples/canvas-distributions.js new file mode 100644 index 0000000..acec35c --- /dev/null +++ b/examples/canvas-distributions.js @@ -0,0 +1,120 @@ +const canvasSketch = require('canvas-sketch'); +const Random = require('./util/random'); +const { lerp, clamp, clamp01 } = require('./util/math'); +const { vec2 } = require('gl-matrix'); + +const colors = { + background: '#f4d9be', + foreground: '#ff911c', + pen: '#1975ff' +}; + +const settings = { + // animation: true, + // When exporting, use the seed as the suffix + // This way we can reproduce it more easily later + animation: true, + dimensions: [ 9, 14 ], + units: 'in', + pixelsPerInch: 300 +}; + +const sketch = ({ context, width, height }) => { + const margin = -1; + + const pointCount = 500; + const aspect = width / height; + const points = Array.from(new Array(pointCount)).map((_, i) => { + const t = pointCount <= 1 ? 0.5 : i / (pointCount - 1); + const size = width * 0.45; + const position = vec2.add([], [ width / 2, height / 2 ], Random.onSquare([ aspect * size, 1 * size ])); + // const position = vec2.add([], [ width / 2, height / 2 ], Random.onCircle(2)); + // const position = [ lerp(margin, width - margin, t), height / 2 ]; + return { + attractionRadius: Random.range(0, 1), + attractionForce: Random.gaussian(0, 0.1), + maxFriends: Random.rangeFloor(0, pointCount), + friends: [], + radius: 0.01 * Random.gaussian(1, 0.1), + speed: Random.gaussian(0.0, 0.025), + velocity: Random.onCircle(), + history: [], + lineWidth: Math.abs(0.01 * Random.gaussian(1, 0.25)), + previous: position.slice(), + newPointThrehold: Math.abs(Random.gaussian(0, 0.0001)), + position + }; + }); + + points.forEach(point => { + const other = Random.shuffleArray(points).filter(p => p !== point); + point.friends = other.slice(0, point.maxFriends); + }); + + const step = () => { + points.forEach(point => { + const offset = Random.insideCircle(Random.gaussian(0, 0.1)); + vec2.add(point.position, point.position, offset); + + vec2.scaleAndAdd(point.position, point.position, point.velocity, point.speed); + + point.friends.forEach(friend => { + const distance = vec2.distance(friend.position, point.position); + if (distance <= point.attractionRadius) { + const strength = 1 - clamp01(distance / point.attractionRadius); + const force = vec2.sub([], friend.position, point.position); + vec2.normalize(force, force); + vec2.scaleAndAdd(point.velocity, point.velocity, force, point.attractionForce * strength); + } + }); + + const maxVel = 2; + point.velocity = point.velocity.map(n => clamp(n, -maxVel, maxVel)); + vec2.scale(point.velocity, point.velocity, 0.98); + + if (vec2.distance(point.position, point.previous) >= point.newPointThrehold) { + point.history.push(point.position.slice()); + point.previous = point.position.slice(); + } + }); + }; + + const clearing = false; + + // Render the shapes + return ({ time, frame }) => { + if (frame === 0 || clearing) { + context.fillStyle = 'white'; + context.fillRect(0, 0, width, height); + } + step(); + + // context.fillStyle = 'white'; + // context.fillRect(0, 0, width, height); + + points.forEach(point => { + point.history.forEach(history => { + context.beginPath(); + context.arc(history[0], history[1], point.radius, 0, Math.PI * 2, false); + context.fillStyle = 'black'; + context.fill(); + }); + }); + + // points.forEach(point => { + // context.beginPath(); + // point.history.forEach(p => context.lineTo(p[0], p[1])); + // context.lineWidth = point.lineWidth; + // context.globalAlpha = 0.5; + // context.stroke(); + // // point.history.forEach(history => { + // // context.beginPath(); + // // context.arc(history[0], history[1], point.radius, 0, Math.PI * 2, false); + // // context.fillStyle = 'black'; + // // context.fill(); + // // }); + // }); + }; +}; + +canvasSketch(sketch, settings); diff --git a/examples/canvas-random-test.js b/examples/canvas-random-test.js new file mode 100644 index 0000000..a7559ef --- /dev/null +++ b/examples/canvas-random-test.js @@ -0,0 +1,213 @@ +const canvasSketch = require('canvas-sketch'); // not yet released +const Random = require('./util/random'); +const { lerp, expand2D, expand3D, inverseLerp } = require('./util/math'); +const { mat4, vec2 } = require('gl-matrix'); +const defined = require('defined'); +const createPerspectiveCamera = require('perspective-camera'); +const PD = require('probability-distributions'); +const rough = require('roughjs'); + +// We can force a random seed or a specific string/number +// Random.setSeed(100); +// Random.setSeed(Random.getRandomSeed()); + +const settings = { + // animation: true, + // When exporting, use the seed as the suffix + // This way we can reproduce it more easily later + suffix: Random.getSeed(), + dimensions: [ 1280, 1280 ] + // dimensions: [ 7, 11 ], + // pixelsPerInch: 300, + // units: 'in' +}; + +const boundArray = array => { + let max = -Infinity; + let min = Infinity; + array.forEach(i => { + if (i > max) max = i; + if (i < min) min = i; + }); + if (!isFinite(max)) max = 0; + if (!isFinite(min)) min = 0; + return [ min, max ]; +}; + +const distribute = array => { + const [ min, max ] = boundArray(array); + return array.map(i => { + return min === max ? min : inverseLerp(min, max, i); + }); +}; + +const sketch = ({ context, width, height }) => { + const ktx = createContextUtil(context); + const roughCanvas = rough.canvas(context.canvas); + const roughGenerator = roughCanvas.generator; + + // const count = 20000; + // const points = Array.from(new Array(count)).map(() => { + // const r = Random.insideCircle(); + // return vec2.add([], [ width / 2, height / 2 ], r); + // }); + + // const samples = PD.rlaplace(count, 1, 0.05); + // const samples = PD.rnorm(count, 1, 0.05); + // const samples = PD.rgamma(count, 2, 10) + // const samples = PD.rbeta(count, 1000, 0.25); + // const samples = PD.rbinom(count, 50, 0.65); + + // const points3D = (samples).map(t => { + // const scale = 1; + // const T = 1 + 0.1 * Random.gaussian(); + // // // return expand3D([ s * Random.gaussianFast(), s * Random.gaussianFast() ]); + // // return expand3D(Random.onCircle(scale * t)); + // // return expand3D([ Random.gaussian(), Random.gaussian() ]); + // return expand3D(Random.onCircle(scale * T)); + // // return expand3D(Random.onLineSegment([ -1, -1, -1 ], [ 1, 1, 1 ])); + // }); + + const points = []; + + const stemAngles = [ + 20, + 100, + 50 + ]; + + const stems = stemAngles.map(angle => { + const rotation = Random.gaussian(angle, 10) * Math.PI / 180; + const size = [ Random.gaussian(30, 10), Random.gaussian(120, 20) ]; + const position = [ width / 2, height / 2 - width * 0.33 / 2 - Random.gaussian(0, 10) ]; + return { + fill: true, + position, + size, + rotation + }; + }); + + const camera = createPerspectiveCamera({ + fov: Math.PI / 4, + near: 0.01, + far: 100, + viewport: [0, 0, width, height] + }); + + return ({ time }) => { + ktx.update({ width, height }); + ktx.clear({ fill: 'white' }); + context.lineJoin = 'round'; + context.lineCap = 'round'; + + const margin = width * 0.1; + + roughCanvas.rectangle(margin, margin, width - margin * 2, height - margin * 2, { + fill: '#f4d9be', + fillStyle: 'solid', + stroke: '#f4d9be', + strokeWidth: 20 + }); + + roughCanvas.circle(width / 2, height / 2, width * 0.33, { + fillStyle: 'solid', + fill: '#ff911c', + stroke: '#1975ff', + strokeWidth: 1.5 + }); + + stems.forEach(({ position, size, rotation, fill }) => { + context.translate(position[0], position[1]); + context.rotate(rotation); + context.translate(-position[0], -position[1]); + + const params = fill ? { + fillStyle: 'hachure', + fill: '#1975ff', + stroke: 'transparent', + strokeWidth: 2 + } : { + stroke: '#1975ff', + strokeWidth: 1 + }; + roughCanvas.rectangle(position[0] - size[0] / 2, position[1] - size[1] / 2, size[0], size[1], params); + }); + + + const r = 6; + const angle = Math.PI / 2 + time * 0.25; + const x = Math.cos(angle) * r; + const z = Math.sin(angle) * r; + camera.identity(); + camera.translate([ x, 0, z ]); + camera.lookAt([ 0, 0, 0 ]); + camera.update(); + + // points3D.forEach(p => { + // ktx.circle({ position: camera.project(p), radius: 1 }); + // }) + // points.forEach(position => ktx.circle({ position, radius: 0.035 })); + }; +}; + +canvasSketch(sketch, settings); + +function createContextUtil (context) { + const projection = mat4.identity([]); + const view = mat4.identity([]); + const cameraMatrix = mat4.identity([]); + let width = 1; + let height = 1; + + return { + update: (opt = {}) => { + width = defined(opt.width, 1); + height = defined(opt.height, 1); + mat4.ortho(projection, 0, width, height, 0, -100, 100); + mat4.invert(view, cameraMatrix); + }, + clear: meshRenderer(opt => { + context.clearRect(0, 0, width, height); + if (opt.fill || opt.stroke) context.rect(0, 0, width, height); + }), + circle: meshRenderer(opt => { + context.beginPath(); + const position = expand2D(opt.position); + const radius = defined(opt.radius, 1); + const startAngle = defined(opt.startAngle, 0); + const endAngle = defined(opt.endAngle, Math.PI * 2); + const counterclockwise = Boolean(opt.counterclockwise); + context.arc(position[0], position[1], radius, startAngle, endAngle, counterclockwise); + }) + }; + + function meshRenderer (fn) { + return (opt = {}) => { + context.save(); + fn(opt); + let stroke = opt.stroke; + let fill = opt.fill; + if (stroke == null && fill == null) { + fill = 'black'; + } + + if (stroke) { + context.lineWidth = defined(opt.lineWidth, 1); + context.lineCap = opt.lineCap || 'butt'; + context.lineJoin = opt.lineJoint || 'miter'; + context.strokeStyle = stroke; + context.stroke(); + } + if (fill) { + context.fillStyle = fill; + context.fill(); + } + context.restore(); + }; + } + + function mesh (fn, opt = {}) { + + } +} \ No newline at end of file diff --git a/examples/canvas-rough-orange.js b/examples/canvas-rough-orange.js new file mode 100644 index 0000000..9203709 --- /dev/null +++ b/examples/canvas-rough-orange.js @@ -0,0 +1,87 @@ +const canvasSketch = require('canvas-sketch'); +const Random = require('./util/random'); + +const rough = require('roughjs'); + +const colors = { + background: '#f4d9be', + foreground: '#ff911c', + pen: '#1975ff' +}; + +const settings = { + // animation: true, + // When exporting, use the seed as the suffix + // This way we can reproduce it more easily later + dimensions: [ 1280, 1280 ], + exportPixelRatio: 2 +}; + +const sketch = ({ context, width, height }) => { + const roughCanvas = rough.canvas(context.canvas); + const margin = width * 0.1; + + // Make a few 'stems' for the fruit at different angles + const stemAngles = [ + 20, + 100, + 50 + ]; + + // The stems are just rectangles really... + const stems = stemAngles.map(angle => { + const rotation = Random.gaussian(angle, 10) * Math.PI / 180; + const size = [ Random.gaussian(30, 10), Random.gaussian(120, 20) ]; + const position = [ width / 2, height / 2 - width * 0.33 / 2 - Random.gaussian(0, 10) ]; + return { + fill: true, + position, + size, + rotation + }; + }); + + // Render the shapes + return ({ time }) => { + context.fillStyle = 'white'; + context.fillRect(0, 0, width, height); + context.lineJoin = 'round'; + context.lineCap = 'round'; + + // Draw a background + roughCanvas.rectangle(margin, margin, width - margin * 2, height - margin * 2, { + fill: colors.background, + fillStyle: 'solid', + stroke: colors.background, + strokeWidth: 20 + }); + + // Draw the fruit + roughCanvas.circle(width / 2, height / 2, width * 0.33, { + fillStyle: 'solid', + fill: colors.foreground, + stroke: colors.pen, + strokeWidth: 1.5 + }); + + // Draw the 'stems' + stems.forEach(({ position, size, rotation, fill }) => { + context.translate(position[0], position[1]); + context.rotate(rotation); + context.translate(-position[0], -position[1]); + + const params = fill ? { + fillStyle: 'hachure', + fill: colors.pen, + stroke: 'transparent', + strokeWidth: 2 + } : { + stroke: colors.pen, + strokeWidth: 1 + }; + roughCanvas.rectangle(position[0] - size[0] / 2, position[1] - size[1] / 2, size[0], size[1], params); + }); + }; +}; + +canvasSketch(sketch, settings); diff --git a/examples/ktx-test.js b/examples/ktx-test.js deleted file mode 100644 index a5047d3..0000000 --- a/examples/ktx-test.js +++ /dev/null @@ -1,45 +0,0 @@ -const canvasSketch = require('canvas-sketch'); // not yet released -const Random = require('./util/random'); -const { lerp } = require('./util/math'); -const { vec2 } = require('gl-matrix'); -const createKtx = require('./util/ktx'); - -const settings = { - dimensions: [ 8, 11 ], - units: 'in', - context: 'webgl', - animation: false, - attributes: { - premultipliedAlpha: true, - antialias: true // turn on MSAA - } -}; - -const sketch = ({ gl, width, height }) => { - const ktx = createKtx({ gl }); - - const pointCount = 1000; - const points = Array.from(new Array(pointCount)).map(() => { - return vec2.add([], [ width / 2, height / 2 ], Random.insideBox([], width / 2)); - }); - - const drawPoint = ktx.rect(); - - return ({ gl, width, height, time }) => { - ktx.update({ width, height }); - ktx.clear({ color: 'white', alpha: 1 }); - - points.forEach(position => { - drawPoint({ - position, - pivot: [ 0.5, 0.5 ], - scale: 0.25, - alpha: 0.5 - }); - }); - - gl.flush(); - }; -}; - -canvasSketch(sketch, settings); \ No newline at end of file diff --git a/examples/util/geom.js b/examples/util/geom.js new file mode 100644 index 0000000..f79891e --- /dev/null +++ b/examples/util/geom.js @@ -0,0 +1,160 @@ +const euclideanDistance = require('euclidean-distance'); +const { vec2 } = require('gl-matrix'); + +module.exports.getPolylinePerimeter = (points, closed = false) => { + let perimeter = 0; + let lastPosition = points.length - 1; + for (let i = 0; i < lastPosition; i++) { + perimeter += euclideanDistance(points[i], points[i + 1]); + } + if (closed && points.length > 1) { + perimeter += euclideanDistance(points[points.length - 1], points[0]); + } + return perimeter; +}; + +module.exports.getPolylineArclengths = (path) => { + let totalDistance = 0; + const distances = []; + for (let i = 0; i < path.length; i++) { + if (i > 0) { + const last = path[i - 1]; + const cur = path[i]; + totalDistance += euclideanDistance(last, cur); + } + distances.push(totalDistance); + } + for (let i = 0; i < distances.length; i++) { + distances[i] /= totalDistance; + } + return distances; +}; + +module.exports.resampleLineBySpacing = (points, spacing = 1, closed = false) => { + if (spacing <= 0) { + throw new Error('Spacing must be positive and larger than 0'); + } + let totalLength = 0; + let curStep = 0; + let lastPosition = points.length - 1; + if (closed) { + lastPosition++; + } + const result = []; + const tmp = [ 0, 0 ]; + for (let i = 0; i < lastPosition; i++) { + const repeatNext = i === points.length - 1; + const cur = points[i]; + const next = repeatNext ? points[0] : points[i + 1]; + const diff = vec2.subtract(tmp, next, cur); + + let curSegmentLength = vec2.length(diff); + totalLength += curSegmentLength; + + while (curStep * spacing <= totalLength) { + let curSample = curStep * spacing; + let curLength = curSample - (totalLength - curSegmentLength); + let relativeSample = curLength / curSegmentLength; + result.push(vec2.lerp([], cur, next, relativeSample)); + curStep++; + } + } + return result; +} + +module.exports.resampleLineByCount = (points, count = 1, closed = false) => { + if (count <= 0) return []; + const perimeter = module.exports.getPolylinePerimeter(points, closed); + return module.exports.resampleLineBySpacing(points, perimeter / count, closed); +} + +// Returns a list that is a cubic spline of the input points +// This function could probably be optimized for real-time a bit better +module.exports.cubicSpline = (points, tension = 0.5, segments = 25, closed = false) => { + // unroll pairs into flat array + points = points.reduce((a, b) => a.concat(b), []); + + var pts; // for cloning point array + var i = 1; + var l = points.length; + var rPos = 0; + var rLen = (l - 2) * segments + 2 + (closed ? 2 * segments : 0); + var res = new Float32Array(rLen); + var cache = new Float32Array((segments + 2) * 4); + var cachePtr = 4; + var st, st2, st3, st23, st32, parse; + + pts = points.slice(0); + if (closed) { + pts.unshift(points[l - 1]); // insert end point as first point + pts.unshift(points[l - 2]); + pts.push(points[0], points[1]); // first point as last point + } else { + pts.unshift(points[1]); // copy 1. point and insert at beginning + pts.unshift(points[0]); + pts.push(points[l - 2], points[l - 1]); // duplicate end-points + } + // cache inner-loop calculations as they are based on t alone + cache[0] = 1; // 1,0,0,0 + for (; i < segments; i++) { + st = i / segments; + st2 = st * st; + st3 = st2 * st; + st23 = st3 * 2; + st32 = st2 * 3; + cache[cachePtr++] = st23 - st32 + 1; // c1 + cache[cachePtr++] = st32 - st23; // c2 + cache[cachePtr++] = st3 - 2 * st2 + st; // c3 + cache[cachePtr++] = st3 - st2; // c4 + } + cache[++cachePtr] = 1; // 0,1,0,0 + + parse = function (pts, cache, l) { + var i = 2; + var t, pt1, pt2, pt3, pt4, t1x, t1y, t2x, t2y, c, c1, c2, c3, c4; + + for (i; i < l; i += 2) { + pt1 = pts[i]; + pt2 = pts[i + 1]; + pt3 = pts[i + 2]; + pt4 = pts[i + 3]; + t1x = (pt3 - pts[i - 2]) * tension; + t1y = (pt4 - pts[i - 1]) * tension; + t2x = (pts[i + 4] - pt1) * tension; + t2y = (pts[i + 5] - pt2) * tension; + for (t = 0; t < segments; t++) { + // t * 4 + c = t << 2; // jshint ignore: line + c1 = cache[c]; + c2 = cache[c + 1]; + c3 = cache[c + 2]; + c4 = cache[c + 3]; + + res[rPos++] = c1 * pt1 + c2 * pt3 + c3 * t1x + c4 * t2x; + res[rPos++] = c1 * pt2 + c2 * pt4 + c3 * t1y + c4 * t2y; + } + } + }; + + // calc. points + parse(pts, cache, l); + + if (closed) { + // l = points.length + pts = []; + pts.push(points[l - 4], points[l - 3], points[l - 2], points[l - 1]); // second last and last + pts.push(points[0], points[1], points[2], points[3]); // first and second + parse(pts, cache, 4); + } + // add last point + l = closed ? 0 : points.length - 2; + res[rPos++] = points[l]; + res[rPos] = points[l + 1]; + + // roll back up into pairs + const rolled = []; + for (let i = 0; i < res.length / 2; i++) { + rolled.push([ res[i * 2 + 0], res[i * 2 + 1] ]); + } + return rolled; +} diff --git a/examples/util/ktx-idea.js b/examples/util/ktx-idea.js deleted file mode 100644 index 4db3af5..0000000 --- a/examples/util/ktx-idea.js +++ /dev/null @@ -1,27 +0,0 @@ -const opts = { - fill: 'red', // true -> black - stroke: 'blue', // true -> black - alpha: 0.5, - lineWidth, - lineCap, - lineJoin, - vertex, - fragment, - blend, - projection, - view, - uniforms -}; - -const line = ktx.line([ [ 25, 25 ], [ 20, 10 ] ], {}); -const polyline = ktx.polyline([ [ 25, 10 ], [ 20, 10 ], [ 20, 30 ] ], {}); -const circle = ktx.circle([ 25, 10 ], 5, {}); -const poly = ktx.polygon([ - [ [ 25, 10 ], [ 30, 30 ], [ 20, 10 ] ] -], {}); -const mesh = ktx.mesh({ cells, positions, normals, uvs }); -const rect = ktx.rect([ 25, 10, 50, 50 ], {}); -const points = ktx.pointCloud([ [ 15, 10 ], [ 10, 10 ] ], {}); - -ktx.clear(); -ktx.clear({ rect: [ 0, 0, 10, 10 ], color: 'red', alpha: 0.5 }); \ No newline at end of file diff --git a/examples/util/math.js b/examples/util/math.js index 29ad3a9..d29e203 100644 --- a/examples/util/math.js +++ b/examples/util/math.js @@ -1,9 +1,57 @@ -module.exports = { - lerp: require('lerp'), - inverseLerp: require('unlerp'), - smoothstep: require('smoothstep') +const lerp = require('lerp'); +const inverseLerp = require('unlerp'); +const clamp = require('clamp'); + +const toFinite = (n, defaultValue = 0) => { + return typeof n === 'number' && isFinite(n) ? n : defaultValue; +}; + +const expandVector = dims => { + return (p, defaultValue = 0) => { + let scalar; + if (p == null) { + // No vector, create a default one + scalar = defaultValue; + } else if (typeof p === 'number' && isFinite(p)) { + // Expand single channel to multiple vector + scalar = p; + } + + const out = []; + if (scalar == null) { + for (let i = 0; i < dims; i++) { + out[i] = toFinite(p[i], defaultValue); + } + } else { + for (let i = 0; i < dims; i++) { + out[i] = scalar; + } + } + return out; + }; }; +const lerpArray = (min, max, t, out = []) => { + if (min.length !== max.length) { + throw new Error('min and max array are expected to have the same length'); + } + for (let i = 0; i < min.length; i++) { + out[i] = lerp(min[i], max[i], t); + } + return out; +}; + +module.exports = { + lerpArray, + lerp, + inverseLerp, + clamp, + clamp01: (v) => clamp(v, 0, 1), + smoothstep: require('smoothstep'), + expand2D: expandVector(2), + expand3D: expandVector(3), + expand4D: expandVector(4) +}; // clamp // clamp01 diff --git a/examples/util/polyline-util.js b/examples/util/polyline-util.js new file mode 100644 index 0000000..15b84ea --- /dev/null +++ b/examples/util/polyline-util.js @@ -0,0 +1,77 @@ +var util = require('polyline-miter-util'); +var { vec2 } = require('gl-matrix'); + +var lineA = [0, 0]; +var lineB = [0, 0]; +var tangent = [0, 0]; +var miter = [0, 0]; + +module.exports = function (points, closed) { + var curNormal = null; + var out = []; + if (closed) { + points = points.slice(); + points.push(points[0]); + } + + var total = points.length; + for (var i = 1; i < total; i++) { + var last = points[i - 1]; + var cur = points[i]; + var next = i < points.length - 1 ? points[i + 1] : null; + + util.direction(lineA, cur, last); + if (!curNormal) { + curNormal = [0, 0]; + util.normal(curNormal, lineA); + } + + if (i === 1) { // add initial normals + addNext(out, curNormal, 1); + } + + if (!next) { // no miter, simple segment + util.normal(curNormal, lineA); // reset normal + addNext(out, curNormal, 1); + } else { // miter with last + // get unit dir of next line + util.direction(lineB, next, cur); + + // stores tangent & miter + var miterLen = util.computeMiter(tangent, miter, lineA, lineB, 1); + let dot = vec2.dot(tangent, curNormal); + addNext(out, miter, miterLen, dot); + } + } + + // if the polyline is a closed loop, clean up the last normal + if (points.length > 2 && closed) { + var last2 = points[total - 2]; + var cur2 = points[0]; + var next2 = points[1]; + + util.direction(lineA, cur2, last2); + util.direction(lineB, next2, cur2); + util.normal(curNormal, lineA); + + var miterLen2 = util.computeMiter(tangent, miter, lineA, lineB, 1); + let dot = vec2.dot(tangent, curNormal); + const features = [ out[0], out[total - 1] ]; + features.forEach(feature => { + feature.normal = miter.slice(); + feature.length = miterLen2; + feature.dot = dot; + }); + out.pop(); + } + + return out; +}; + +function addNext (out, normal, length, dot = 0) { + out.push({ + normal: [ normal[0], normal[1] ], + length, + dot + }); +} diff --git a/examples/util/primitive-polyline.js b/examples/util/primitive-polyline.js new file mode 100644 index 0000000..0075859 --- /dev/null +++ b/examples/util/primitive-polyline.js @@ -0,0 +1,157 @@ +const polylineNormals = require('./polyline-util'); +const defined = require('defined'); +const earcut = require('earcut'); +const cdt2d = require('cdt2d'); +const { vec2 } = require('gl-matrix'); + +const list = (path, normalData, closed, lineWidth, side) => { + const result = path.map((point, i) => { + const data = normalData[i]; + const normal = data.normal; + const miterLength = data.length; + const position = typeof point.position !== 'undefined' ? point.position : point; + const thickness = typeof point.thickness === 'number' && isFinite(point.thickness) ? point.thickness : 1; + const computedLineWidth = defined(point.lineWidth, lineWidth); + const length = thickness * computedLineWidth / 2 * miterLength; + let t; + if (path.length <= 1) t = 0; + else if (closed) t = i / path.length; + else t = i / (path.length - 1); + return { + position: vec2.scaleAndAdd([], position, normal, length * side), + normal, + uv: [ t, side ] + }; + }); + return result; +}; + +const getPoints = path => { + if (path.length === 0) return path; + return path.map(point => { + return typeof point.position !== 'undefined' ? point.position : point; + }); +}; + +// module.exports = function (path, opt = {}) { +// return module.exports.contours(path, opt).map(contour => { +// return contour.map(p => p.position); +// }); +// }; + +module.exports = function (path, opt = {}) { + if (path.length < 2) { + return { + positions: [], + cells: [] + }; + } + + const points = getPoints(path); + const closed = path.length > 2 && opt.closed; + const lineWidth = defined(opt.lineWidth, 1); + const normalData = polylineNormals(points, closed); + + const edgeList = [ 1, -1 ].map(side => { + return list(path, normalData, closed, lineWidth, side); + }); + + edgeList[1].reverse(); + + if (closed) { + // If we need to flip + // const flip = true; + const flip = !normalData.every(p => p.dot <= 0); + + // const initialNormal = normalData[0][0]; + if (flip) edgeList.reverse(); + } + + // Flatten edge list into a single polygon + const edges = edgeList.reduce((a, b) => a.concat(b), []); + + if (edges.some(p => p.position.some(n => !isFinite(n)))) { + throw new Error('Some values in the input path are not finite, i.e. NaN or Infinity'); + } + + const mesh = { + positions: edges.map(p => p.position), + uvs: edges.map(p => p.uv), + normals: edges.map(p => p.normal) + }; + + // Convert to a list of contours + // An open stroke is just a regular polygon, a closed stroke + // is more like a polygon with a hole + const contours = closed + ? edgeList.map(edges => edges.map(p => p.position)) + : [ mesh.positions ]; + + // const result = triangulateCdt2d(contours); + const result = triangulateEarcut(contours); + + return Object.assign({}, mesh, result); +}; + +function triangulateCdt2d (contours) { + const positions = contours.reduce((a, b) => a.concat(b), []); + let edges = []; + let edgeIndex = 0; + let edgeStart = 0; + contours.forEach(contour => { + contour.forEach(_ => { + const a = edgeIndex; + const b = ++edgeIndex; + edges.push([ (a % contour.length) + edgeStart, (b % contour.length) + edgeStart ]) + }); + edgeStart += contour.length; + }); + + // TODO: Handle this with normals + uvs somehow? + let normals; + const positionsBefore = positions.length; + // if (require('clean-pslg')(positions, edges)) { + // const positionsAfter = positions.length; + // console.log('after before', positionsAfter, positionsBefore); + // } + + const cells = cdt2d(positions, edges, { + delaunay: false, + interior: true, + exterior: false + }); + + const outputData = { positions, cells }; + + return outputData; +} + +function triangulateEarcut (contours) { + const flat = earcut.flatten(contours); + const indices = earcut(flat.vertices, flat.holes, flat.dimensions); + const cells = []; + for (let i = 0; i < indices.length / 3; i++) { + const cell = []; + for (let d = 0; d < 3; d++) { + cell.push(indices[i * 3 + d]); + } + cells.push(cell); + } + return { cells, positions: contours.reduce((a, b) => a.concat(b), []) }; +} + + +const shoelace = (ring) => { + let sum = 0; + let i = 1; + let prev; + let cur; + + while (i < ring.length) { + prev = cur || ring[0]; + cur = ring[i]; + sum += ((cur[0] - prev[0]) * (cur[1] + prev[1])); + i++; + } + return sum; +}; diff --git a/examples/util/random.js b/examples/util/random.js index c9acebc..cfc8201 100644 --- a/examples/util/random.js +++ b/examples/util/random.js @@ -1,11 +1,15 @@ // a utility for random number generation const seedRandom = require('seed-random'); const SimplexNoise = require('simplex-noise'); +const { lerpArray, expand2D } = require('./math'); +const { getPolylineArclengths } = require('./geom'); class Rand { constructor (defaultSeed = null, opt = {}) { this.defaultRandom = Math.random; this.quiet = opt.quiet !== false; + this._nextGaussian = null; + this._hasNextGaussian = false; this.setSeed(defaultSeed); } @@ -18,16 +22,28 @@ class Rand { return seed; } - setSeed (seed) { + setSeed (seed, opt = {}) { if (typeof seed === 'number' || typeof seed === 'string') { this.seed = String(seed); if (!this.quiet) console.log('[util-random] Current Seed:', this.seed); - this.random = seedRandom(this.seed); + this.random = seedRandom(this.seed, opt); } else { this.seed = null; this.random = this.defaultRandom; } this.simplex = new SimplexNoise(this.random); + this._nextGaussian = null; + this._hasNextGaussian = false; + } + + value () { + return this.random(); + } + + valueNonZero () { + let u = 0; + while (u === 0) u = this.value(); + return u; } getSeed () { @@ -46,16 +62,16 @@ class Rand { return this.simplex.noise4D(x, y, z, w); } - gaussian () { - return Math.sqrt(-2.0 * Math.log(this.random())) * Math.cos(2.0 * Math.PI * this.random()); - } - sign () { return this.randomBoolean() ? 1 : -1; } boolean () { - return this.random() > 0.5; + return this.value() > 0.5; + } + + chance (n = 0.5) { + return this.value() < n; } range (min, max) { @@ -68,7 +84,7 @@ class Rand { throw new TypeError('Expected all arguments to be numbers'); } - return this.random() * (max - min) + min; + return this.value() * (max - min) + min; } rangeFloor (min, max) { @@ -94,7 +110,7 @@ class Rand { var len = arr.length; var ret = arr.slice(); while (len) { - rand = Math.floor(this.random() * len--); + rand = Math.floor(this.value() * len--); tmp = ret[len]; ret[len] = ret[rand]; ret[rand] = tmp; @@ -102,56 +118,90 @@ class Rand { return ret; } - insideBox (out = [], scale = 1) { - if (!Array.isArray(scale)) scale = [ scale, scale ]; - out[0] = scale[0] * this.range(-1, 1) / 2; - out[1] = scale[1] * this.range(-1, 1) / 2; + insideSquare (scale = 1, out = []) { + scale = expand2D(scale, 1); + out[0] = scale[0] * this.range(-1, 1); + out[1] = scale[1] * this.range(-1, 1); return out; } - onCircle (out = [], scale = 1) { - var r = this.random() * 2.0 * Math.PI; - out[0] = Math.cos(r) * scale; - out[1] = Math.sin(r) * scale; - return out; + onSquare (scale = 1, out = []) { + scale = expand2D(scale, 1); + const path = [ + [ -scale[0], -scale[1] ], [ scale[0], -scale[1] ], + [ scale[0], scale[1] ], [ -scale[0], scale[1] ] + ]; + path.push(path[0]); + return this.onPolyline(path, out); + } + + onPolyline (path, out = []) { + if (path.length === 0) { + throw new Error('The path has no points; cannot determine a random point along it.'); + } + if (path.length === 1) { + return path[0].slice(); + } + + const arclengths = getPolylineArclengths(path); + const edges = []; + for (let i = 0; i < path.length - 1; i++) { + edges.push(arclengths[i + 1] - arclengths[i]); + } + + const index = this.weighted(edges); + return this.onLineSegment(path[index], path[index + 1], out); } - insideCircle (out = [], scale = 1) { - return this.onCircle(out, this.range(0, scale)); + onLineSegment (a, b, out = []) { + const t = this.value(); + return lerpArray(a, b, t, out); } - onSphere (out = [], scale = 1) { - var r = this.random() * 2.0 * Math.PI; - var z = (this.random() * 2.0) - 1.0; - var zScale = Math.sqrt(1.0 - z * z) * scale; - out[0] = Math.cos(r) * zScale; - out[1] = Math.sin(r) * zScale; - out[2] = z * scale; + onCircle (radius = 1, out = []) { + const theta = this.value() * 2.0 * Math.PI; + out[0] = radius * Math.cos(theta); + out[1] = radius * Math.sin(theta); return out; } - insideSphere (out = [], scale = 1) { - return this.onSphere(out, this.range(0, scale)); + insideCircle (radius = 1, out = []) { + this.onCircle(1, out); + const r = radius * Math.sqrt(this.value()); + out[0] *= r; + out[1] *= r; + return out; } - onHemisphere (out = [], scale = 1) { - var r = this.random() * 1.0 * Math.PI; - var z = (this.random() * 2.0) - 1.0; - var zScale = Math.sqrt(1.0 - z * z) * scale; - out[0] = Math.cos(r) * zScale; - out[1] = Math.sin(r) * zScale; - out[2] = z * scale; + onSphere (radius = 1, out = []) { + const u = this.value() * Math.PI * 2; + const v = this.value() * 2 - 1; + const phi = u; + const theta = Math.acos(v); + out[0] = radius * Math.sin(theta) * Math.cos(phi); + out[1] = radius * Math.sin(theta) * Math.sin(phi); + out[2] = radius * Math.cos(theta); return out; } - insideHemisphere (out = [], scale = 1) { - return this.onHemisphere(out, this.range(0, scale)); + insideSphere (radius = 1, out = []) { + const u = this.value() * Math.PI * 2; + const v = this.value() * 2 - 1; + const k = this.value(); + + const phi = u; + const theta = Math.acos(v); + const r = radius * Math.cbrt(k); + out[0] = r * Math.sin(theta) * Math.cos(phi); + out[1] = r * Math.sin(theta) * Math.sin(phi); + out[2] = r * Math.cos(theta); + return out; } quaternion (out = []) { - const u1 = this.random(); - const u2 = this.random(); - const u3 = this.random(); + const u1 = this.value(); + const u2 = this.value(); + const u3 = this.value(); const sq1 = Math.sqrt(1 - u1); const sq2 = Math.sqrt(u1); @@ -190,7 +240,7 @@ class Rand { if (totalWeight <= 0) throw new Error('Weights must sum to > 0'); - let random = this.random() * totalWeight; + let random = this.value() * totalWeight; for (let i = 0; i < weights.length; i++) { if (random < weights[i]) { return i; @@ -199,6 +249,77 @@ class Rand { } return 0; } + + // Distributions + + gaussian (mean = 0, standardDerivation = 1) { + // https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/java/util/Random.java#L496 + if (this._hasNextGaussian) { + this._hasNextGaussian = false; + const result = this._nextGaussian; + this._nextGaussian = null; + return mean + standardDerivation * result; + } else { + let v1 = 0; + let v2 = 0; + let s = 0; + do { + v1 = this.value() * 2 - 1; // between -1 and 1 + v2 = this.value() * 2 - 1; // between -1 and 1 + s = v1 * v1 + v2 * v2; + } while (s >= 1 || s === 0); + const multiplier = Math.sqrt(-2 * Math.log(s) / s); + this._nextGaussian = (v2 * multiplier); + this._hasNextGaussian = true; + return mean + standardDerivation * (v1 * multiplier); + } + } + + laplace () { + let u = this.value(); + u = u + u - 1.0; + if (u > 0) return -Math.log(1.0 - u); + else return Math.log(1.0 + u); + } + + logistic () { + return (-Math.log(1.0 / this.valueNonZero() - 1.0)); + } + + powerLaw (alpha = 0, cut = 1) { + return cut * Math.pow(this.value(), 1.0 / (alpha + 1.0)); + } + + weibull (alpha = 1, beta = 1) { + return Math.pow(beta * (-Math.log(1.0 - this.value())), 1.0 / alpha); + } + + erlang (mean = 1, variance = 1) { + if (variance === 0) throw new Error('variance must be != 0'); + let k = Math.floor((mean * mean) / variance + 0.5); + k = (k > 0) ? k : 1; + let a = k / mean; + let prod = 1.0; + for (let i = 0; i < k; i++) prod *= this.value(); + return -Math.log(prod) / a; + } + + lambda (l3 = 1, l4 = 1) { + let lSign = ((l3 < 0) || (l4 < 0)) ? -1 : 1; + let u = this.valueNonZero(); + let x = lSign * (Math.exp(Math.log(u) * l3) - Math.exp(Math.log(1.0 - u) * l4)); + return x; + } + + triangular () { + let u = this.value(); + if (u <= 0.5) return (Math.sqrt(2.0 * u) - 1.0); + else return (1.0 - Math.sqrt(2.0 * (1.0 - u))); + } + + cauchy () { + return Math.tan(Math.PI * this.value()); + } } module.exports = new Rand(); diff --git a/examples/util/webgl-2d-ideas.js b/examples/util/webgl-2d-ideas.js new file mode 100644 index 0000000..3595c26 --- /dev/null +++ b/examples/util/webgl-2d-ideas.js @@ -0,0 +1,56 @@ +const opts = { + fill: 'red', // true -> black + stroke: 'blue', // true -> black + alpha: 0.5, + lineWidth, + lineCap, + lineJoin, + vertex, + fragment, + blend, + projection, + view, + uniforms +}; + +const line = ktx.line([ [ 25, 25 ], [ 20, 10 ] ], {}); +const polyline = ktx.polyline([ [ 25, 10 ], [ 20, 10 ], [ 20, 30 ] ], {}); +const circle = ktx.circle([ 25, 10 ], 5, {}); +const poly = ktx.polygon([ + [ [ 25, 10 ], [ 30, 30 ], [ 20, 10 ] ] +], {}); +const mesh = ktx.mesh({ cells, positions, normals, uvs }); +const rect = ktx.rect([ 25, 10, 50, 50 ], {}); +const points = ktx.pointCloud([ [ 15, 10 ], [ 10, 10 ] ], {}); + +ktx.clear(); +ktx.clear({ rect: [ 0, 0, 10, 10 ], color: 'red', alpha: 0.5 }); + + +line, circle, rect, triangle (a, b, c), polygon, polyline, mesh + +sphere, icosphere, torus, cube + +const { positions, cells } = ktx.geometry.icosphere(); +const mesh = ktx.icosphere(); +const mesh = ktx. + +"render data" -> color, position, etc +"constructor data" -> segments + any render data + +line() // single line render, set position uniform in shader, per-instance stuff +curve() // single curve render, can be bezier or quadratic, primitive is a plane w/ segments, per-instance stuff +polyline() // not per-instance, +polygon() // triangulated then rendered + +Later: +polylines() // list of lines all submitted in a single draw call; maybe useful for alpha stuff? + + +// Would be handy for 2D canvas convenience as well... + +const circle = ktx.circle(); +... + ktx.update({ width, height }); + ktx.clear(); + circle({ position: [ 25, 15 ] }); diff --git a/examples/util/ktx.js b/examples/util/webgl-2d.js similarity index 56% rename from examples/util/ktx.js rename to examples/util/webgl-2d.js index 1089c2f..880aed1 100644 --- a/examples/util/ktx.js +++ b/examples/util/webgl-2d.js @@ -2,11 +2,19 @@ const createRegl = require('regl'); const defined = require('defined'); const { inverseLerp } = require('./math'); const cssColorParse = require('parse-color'); -const createPrimitiveCircle = require('primitive-circle'); -const createPrimitiveQuad = require('primitive-quad'); const { vec3, quat, mat4 } = require('gl-matrix'); const mat4Recompose = require('mat4-recompose'); const boundPoints = require('bound-points'); +const { vertexNormals, faceNormals } = require('normals'); + +const createPrimitive = { + sphere: require('primitive-sphere'), + icosphere: require('primitive-icosphere'), + torus: require('primitive-torus'), + quad: require('primitive-quad'), + circle: require('primitive-circle'), + polyline: require('./primitive-polyline') +}; const IDENTITY_MAT4 = mat4.identity([]); @@ -36,7 +44,7 @@ const get2DUV = positions => { const v = h === 0 ? 0 : inverseLerp(bounds[0][1], bounds[1][1], y); return [ u, v ]; }); -} +}; const toFinite = (n, defaultValue = 0) => { return typeof n === 'number' && isFinite(n) ? n : defaultValue; @@ -127,31 +135,95 @@ module.exports = function (opt = {}) { const color = [ ...rgb, alpha ]; regl.clear({ stencil, depth, color }); }, - // Commands + + // 2D Meshes rect: (opt = {}) => { - return mesh(createPrimitiveQuad(), opt); + const data = getNormalizedPrimitive2D(createPrimitive.quad(), { + center: true + }); + return mesh(data, Object.assign({}, opt, { + unitShape: true + })); }, circle: (opt = {}) => { - const segments = defined(opt.segments, 128); - const primitive = createPrimitiveCircle(1, segments); - return mesh(primitive, Object.assign({ center: true }, opt, { + const fn = (params = {}) => { + const segments = defined(params.segments, 128); + return getNormalizedPrimitive2D(createPrimitive.circle(1, segments), { + center: true + }); + }; + return mesh(fn, Object.assign({}, opt, { + unitShape: true, primitive: 'triangle fan' })); - } + }, + polyline: (opt = {}) => { + const fn = (params = {}) => { + const data = createPrimitive.polyline(params.data || [], params); + const result = getNormalizedPrimitive2D(data); + return result; + }; + return mesh(fn, opt); + }, + // // 3D Meshes + // sphere: (opt = {}) => { + // const segments = defined(opt.segments, 32); + // const primitive = createPrimitive.sphere(1, { segments }); + // return mesh3D(primitive, opt); + // } }; - function mesh (vertexData, opt = {}) { - let { positions, cells } = vertexData; + // A unit 2D rectangle, circle, etc + function getNormalizedPrimitive2D ({ positions, cells, normals, uvs }, opt = {}) { + // Default to assuming the positions are a unit circle/box/etc + normals = normals ? expandVectorList(normals) : positions.map(p => vec3.normalize([], expandVector(p))); + // Planar UV across bounding box of mesh + uvs = uvs || get2DUV(positions); + // Assume 2D primitives are centered in -1..1 space, recenter to 0..1 + positions = opt.center ? recenter(positions) : positions; + // Expand to 3D + positions = expandVectorList(positions); + return { + positions, + uvs, + normals, + cells + }; + } + + // A unit 3D sphere, torus, etc + function getNormalizedPrimitive3D ({ positions, cells, normals, uvs }, opt = {}) { + return { + positions, + uvs, + normals, + cells + }; + } - // Compute normals before recentering - const normals = positions.map(p => { - const vec = vec3.normalize([], expandVector(p)); - return vec; + function computeBoundingBox (points, output = {}) { + if (!output.min) output.min = [ 0, 0, 0 ]; + if (!output.max) output.max = [ 0, 0, 0 ]; + if (!output.size) output.size = [ 0, 0, 0 ]; + vec3.set(output.min, Infinity, Infinity, Infinity); + vec3.set(output.max, -Infinity, -Infinity, -Infinity); + points.forEach(point => { + for (let i = 0; i < point.length; i++) { + if (point[i] < output.min[i]) output.min[i] = point[i]; + if (point[i] > output.max[i]) output.max[i] = point[i]; + } }); + for (let i = 0; i < output.min.length; i++) { + if (!isFinite(output.min[i])) output.min[i] = 0; + if (!isFinite(output.max[i])) output.max[i] = 0; + output.size[i] = output.max[i] - output.min[i]; + } + return output; + } - const uvs = get2DUV(positions); - positions = recenter(positions); - positions = expandVectorList(positions); + function mesh (vertexData, opt = {}) { + let geometry = typeof vertexData === 'function' ? vertexData(opt) : vertexData; + let destroyed = false; const model = mat4.clone(IDENTITY_MAT4); const centerOrigin = vec3.clone([ -0.5, -0.5, 0 ]); @@ -159,25 +231,33 @@ module.exports = function (opt = {}) { const tmpPivot = vec3.create(); const tmpPivotNegated = vec3.create(); const defaultColor = getRGBColor(opt.color); + const bounds = computeBoundingBox(geometry.positions); const attributes = { - position: regl.buffer(positions), - uv: regl.buffer(uvs), - normal: regl.buffer(normals) + position: regl.buffer(geometry.positions), + uv: regl.buffer(geometry.uvs), + normal: regl.buffer(geometry.normals) }; + // mix in new attributes + if (typeof opt.attributes === 'function') { + + } + + const elements = regl.elements(geometry.cells); + const drawMesh = regl({ // Fragment & Vertex shaders frag: (_, props) => props.frag || basicFragShader, vert: (_, props) => props.vert || basicVertShader, primitive: opt.primitive || 'triangles', - uniforms: { + uniforms: Object.assign({}, opt.uniforms, { projection: regl.prop('projection'), model: regl.prop('model'), view: regl.prop('view'), alpha: (_, props) => defined(props.alpha, 1), color: (_, props) => getRGBColor(props.color, defaultColor) - }, + }), // Setup transparency blending blend: Object.assign({ enable: true, @@ -191,10 +271,13 @@ module.exports = function (opt = {}) { enable: false }, opt.depth), attributes, - elements: regl.elements(cells) + elements }); - return (params = {}) => { + const draw = (params = {}) => { + // Skip rendering if destroyed + if (destroyed) return; + // Merge in defaults params = Object.assign({}, opt, params); @@ -207,7 +290,7 @@ module.exports = function (opt = {}) { if (params.position) mat4.translate(model, model, expandVector(params.position)); // Align to origin (center or corner mode) - if (params.center) { + if (params.unitShape && params.center !== false) { vec3.multiply(tmpOrigin, scale, centerOrigin); mat4.translate(model, model, tmpOrigin); } @@ -222,7 +305,8 @@ module.exports = function (opt = {}) { // Cancel Z component in pivoting pivot[2] = 0; - vec3.multiply(tmpPivot, scale, pivot); + vec3.multiply(tmpPivot, bounds.size, scale); + vec3.multiply(tmpPivot, tmpPivot, pivot); vec3.negate(tmpPivotNegated, tmpPivot); // Move to pivot point @@ -244,11 +328,50 @@ module.exports = function (opt = {}) { // Apply user transforms if (params.transform) mat4.multiply(model, model, params.transform); + // Finally submit GL draw call return drawMesh(Object.assign({}, params, { projection, model, view })); }; + + // Maintain reference to the gometry of the mesh in case + // user wishes to render it wireframe, vertices, etc... + draw.geometry = geometry; + + // Update default parameters + draw.update = (params = {}) => { + Object.assign(opt, params); + }; + + // Send new vertex data to GPU + draw.updateGeometry = (params = {}) => { + if (typeof vertexData === 'function') { + // Merge in defaults + params = Object.assign({}, opt, params); + + const newData = vertexData(params, params); + attributes.position(newData.positions); + attributes.normal(newData.normals); + attributes.uv(newData.uvs); + elements(newData.cells); + computeBoundingBox(newData.positions, bounds); + draw.geometry = newData; + } + }; + + // Destroy this command + draw.destroy = () => { + if (destroyed) return; + destroyed = true; + elements.destroy(); + Object.keys(attributes).forEach(key => { + attributes[key].destroy(); + }); + draw.geometry = {}; + }; + + return draw; } }; diff --git a/examples/webgl-2d-test.js b/examples/webgl-2d-test.js new file mode 100644 index 0000000..80eea3c --- /dev/null +++ b/examples/webgl-2d-test.js @@ -0,0 +1,135 @@ +const canvasSketch = require('canvas-sketch'); // not yet released +const Random = require('./util/random'); +const { lerp } = require('./util/math'); +const { vec2 } = require('gl-matrix'); +const createWebGL2D = require('./util/webgl-2d'); +const polyline = require('./util/primitive-polyline'); +const boundPoints = require('bound-points'); + +const settings = { + dimensions: [ 8, 11 ], + units: 'in', + pixelsPerInch: 300, + context: 'webgl', + animation: true, + attributes: { + premultipliedAlpha: true, + antialias: true // turn on MSAA + } +}; + +const sketch = ({ gl, width, height }) => { + const glx = createWebGL2D({ gl }); + + const pointCount = 50; + const points = Array.from(new Array(pointCount)).map(() => { + return vec2.add([], [ width / 2, height / 2 ], Random.insideBox([], width / 2)); + }); + + const drawPoint = glx.circle({ + // uniforms: {ยก + // other: [ 1, 0, 0 ] + // }, + // frag: ` + // precision highp float; + // varying vec2 vUv; + // uniform vec3 other; + // void main () { + // gl_FragColor = vec4(vec3(vUv.x) * other, 1.0); + // } + // `, + // segments: 4 + }); + + const square = glx.rect({ + attributes: (data, opt = {}) => { + return { + random: data.positions.map(p => Random.onSphere()) + }; + } + }); + + // const path = [ [ 2, 2 ], [ 3, 3 ], [ 2, 4 ], [ 1, 3 ] ]; + // const path = [ [ 2, 2 ], [ 3, 3 ], [ 2, 4 ], [ 1, 4 ], [ 3, 2 ] ] + // const path = [ [ 1.5, 1 ], [ 2, 3 ], [ 1, 8 ] ]; + // const path = [ [ 1, 1 ], [ 2, 3 ], [ 2, 5 ] ]; + // const path = [ [ 1, 1 ], [ 2, 3 ], [ 0, 8 ] ]; + // const path = [ + // [ 1, 1 ], + // { position: [ 2, 3 ], thickness: 1 }, + // [ 3, 2 ] + // ]; + // const path = [ [ 1, 1 ], [ 2, 3 ] ]; + const path = [ [ 1, 1 ], [ 2, 3 ], [ 0, 2 ] ]; + const lines = glx.polyline({ + uniforms: { + other: [ 1, 0, 0 ] + }, + frag: ` + precision highp float; + varying vec2 vUv; + uniform vec3 other; + void main () { + gl_FragColor = vec4(vec3(vUv.x) * other, 1.0); + } + `, + lineWidth: 0.1, + closed: true, + color: 'red' + }); + + lines.updateGeometry({ data: path }); + lines.update({ + pivot: [ 0.5, 0.5 ] + }); + + return ({ gl, width, height, time }) => { + glx.update({ width, height }); + glx.clear({ color: 'white', alpha: 1 }); + + lines({ rotation: time }); + const data = lines.geometry; + data.positions.forEach((position, i) => { + drawPoint({ + color: i > data.positions.length / 2 - 1 ? 'green' : 'blue', + position, + scale: 0.1 + }); + }); + + square({ + rotation: 1, + center: true, + pivot: [ 0.5, 0.5 ], + scale: [ 2, 1 ], + position: [ width / 2, height / 2 ] + }) + + // square({ + // vert: ` + // precision highp float; + // attribute vec3 position; + // attribute vec3 normal; + // attribute vec2 uv; + // attribute vec3 random; + + // uniform mat4 projection; + // uniform mat4 model; + // uniform mat4 view; + + // varying vec2 vUv; + // varying vec3 vNormal; + + // void main () { + // vUv = uv; + // vNormal = normal; + // gl_Position = projection * view * model * vec4(position.xyz + random.xyz, 1.0); + // } + // `, + // position: [ width / 2, height / 2 ], + // color: 'blue' + // }); + }; +}; + +canvasSketch(sketch, settings); \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index 26b789c..5fa3290 100644 --- a/lib/index.js +++ b/lib/index.js @@ -304,6 +304,14 @@ class SketchManager { props.context.restore(); } + // Flush by default, this may be revisited at a later point. + // We do this to ensure toDataURL can be called immediately after. + // Most likely browsers already handle this, so we may revisit this and + // remove it if it improves performance without any usability issues. + if (props.gl && this.settings.flush !== false) { + props.gl.flush(); + } + return drawResult; } diff --git a/package-lock.json b/package-lock.json index 02fe96b..64dcfca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1438,18 +1438,6 @@ "acorn": "^5.1.2" } }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, "alphanum-sort": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", @@ -1461,12 +1449,6 @@ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" }, - "ansi-escapes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", - "dev": true - }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -1549,12 +1531,6 @@ "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", "dev": true }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true - }, "asn1.js": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", @@ -1592,36 +1568,18 @@ } } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, - "async": { - "version": "0.1.22", - "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", - "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=", - "dev": true - }, "async-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", "dev": true }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, "asyncro": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/asyncro/-/asyncro-3.0.0.tgz", @@ -1648,18 +1606,6 @@ "postcss-value-parser": "^3.2.3" } }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", - "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==", - "dev": true - }, "babel-polyfill": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", @@ -1780,14 +1726,15 @@ "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", "dev": true }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "big-rat": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/big-rat/-/big-rat-1.0.4.tgz", + "integrity": "sha1-do0JO7V5MN0Y7Vdcf8on3FORreo=", "dev": true, - "optional": true, "requires": { - "tweetnacl": "^0.14.3" + "bit-twiddle": "^1.0.2", + "bn.js": "^4.11.6", + "double-bits": "^1.1.1" } }, "big.js": { @@ -1802,6 +1749,18 @@ "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", "dev": true }, + "binary-search-bounds": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.4.tgz", + "integrity": "sha512-2hg5kgdKql5ClF2ErBcSx0U5bnl5hgS4v7wMnLFodyR47yMtj2w+UAZB+0CiqyHct2q543i7Bi4/aMIegorCCg==", + "dev": true + }, + "bit-twiddle": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz", + "integrity": "sha1-DGwfq+KyPRcXPZpht7cJPrnhdp4=", + "dev": true + }, "bl": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", @@ -1812,15 +1771,6 @@ "safe-buffer": "^5.1.1" } }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dev": true, - "requires": { - "inherits": "~2.0.0" - } - }, "bluebird": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", @@ -1844,21 +1794,22 @@ "json-stringify-safe": ">=5.0.0 <5.1.0-0" } }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "dev": true, - "requires": { - "hoek": "4.x.x" - } - }, "bound-points": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/bound-points/-/bound-points-1.0.0.tgz", "integrity": "sha1-fhY5uFMYRRlLfKCYIGmADvR0Mqc=", "dev": true }, + "box-intersect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/box-intersect/-/box-intersect-1.0.1.tgz", + "integrity": "sha1-tyilnj8aPHPCJJM8JmC5J6oTeQI=", + "dev": true, + "requires": { + "bit-twiddle": "^1.0.2", + "typedarray-pool": "^1.1.0" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2297,11 +2248,16 @@ "integrity": "sha512-vMrE8BED4MJC9IhDJKP8ok6bJUfn5+YHvxwXMYfiPqQOJ3r2B9ihcArlUnXu6yPWf7b3jHqiEBwXZEbrbiFUqg==", "dev": true }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "cdt2d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cdt2d/-/cdt2d-1.0.0.tgz", + "integrity": "sha1-TyEkNLzWe9s9aLj+9KzcLFRBUUE=", + "dev": true, + "requires": { + "binary-search-bounds": "^2.0.3", + "robust-in-sphere": "^1.1.3", + "robust-orientation": "^1.1.3" + } }, "chalk": { "version": "2.4.1", @@ -2314,12 +2270,6 @@ "supports-color": "^5.3.0" } }, - "chardet": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.5.0.tgz", - "integrity": "sha512-9ZTaoBaePSCFvNlNGrsyI8ZVACP2svUtq0DkM7t4K2ClAa96sqOIRjAzDTc8zXzFt1cZR46rRzLTiHFSJ+Qw0g==", - "dev": true - }, "charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", @@ -2353,6 +2303,12 @@ "safe-buffer": "^5.0.1" } }, + "clamp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz", + "integrity": "sha1-ZqDmQBGBbjcZaCj9yMjBRzEshjQ=", + "dev": true + }, "clap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", @@ -2424,52 +2380,27 @@ "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==", "dev": true }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-table2": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/cli-table2/-/cli-table2-0.2.0.tgz", - "integrity": "sha1-LR738hig54biFFQFYtS9F3/jLZc=", + "clean-pslg": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/clean-pslg/-/clean-pslg-1.1.2.tgz", + "integrity": "sha1-vTXHRgt+irWp92Gl7VF5aqPIbBE=", "dev": true, "requires": { - "colors": "^1.1.2", - "lodash": "^3.10.1", - "string-width": "^1.0.1" - }, - "dependencies": { - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - } + "big-rat": "^1.0.3", + "box-intersect": "^1.0.1", + "nextafter": "^1.0.0", + "rat-vec": "^1.1.1", + "robust-segment-intersect": "^1.0.1", + "union-find": "^1.0.2", + "uniq": "^1.0.1" } }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", "dev": true }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, "coa": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", @@ -2479,12 +2410,6 @@ "q": "^1.1.2" } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -2579,15 +2504,6 @@ } } }, - "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, "commander": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", @@ -2760,25 +2676,11 @@ "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", "dev": true }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "dev": true, - "requires": { - "boom": "5.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "dev": true, - "requires": { - "hoek": "4.x.x" - } - } - } + "crypto": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-0.0.3.tgz", + "integrity": "sha1-RwqBuGvkxe4XrMggeh9TFa4g27A=", + "dev": true }, "crypto-browserify": { "version": "3.12.0", @@ -3041,21 +2943,6 @@ } } }, - "cycle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "date-now": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", @@ -3183,12 +3070,6 @@ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -3297,14 +3178,17 @@ "is-obj": "^1.0.0" } }, - "du": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/du/-/du-0.1.0.tgz", - "integrity": "sha1-8m40CgnHvFtv1pr2263qYPqMb00=", - "dev": true, - "requires": { - "async": "~0.1.22" - } + "double-bits": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/double-bits/-/double-bits-1.1.1.tgz", + "integrity": "sha1-WKu6RUlNpND6Nrc60RoobJGEscY=", + "dev": true + }, + "dup": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dup/-/dup-1.0.0.tgz", + "integrity": "sha1-UfxaxoX4GWRp3wuQXpNLIK9bQCk=", + "dev": true }, "duplexer": { "version": "0.1.1", @@ -3332,22 +3216,18 @@ "stream-shift": "^1.0.0" } }, + "earcut": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.1.3.tgz", + "integrity": "sha512-AxdCdWUk1zzK/NuZ7e1ljj6IGC+VAdC3Qb7QQDsXpfNrc5IM8tL9nNXUmEGE6jRHTfZ10zhzRhtDmWVsR5pd3A==", + "dev": true + }, "eases": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/eases/-/eases-1.0.8.tgz", "integrity": "sha1-8fUGmmtu0upRD5xhEDmNY+/pruY=", "dev": true }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "~0.1.0" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -3507,6 +3387,11 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, + "euclidean-distance": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/euclidean-distance/-/euclidean-distance-1.0.0.tgz", + "integrity": "sha1-cDvCE+l8HXMdebWZ6TeUG6lnCmg=" + }, "event-stream": { "version": "3.3.4", "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", @@ -3571,12 +3456,6 @@ "fill-range": "^2.1.0" } }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true - }, "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", @@ -3598,17 +3477,6 @@ } } }, - "external-editor": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.0.tgz", - "integrity": "sha512-mpkfj0FEdxrIhOC04zk85X7StNtr0yXnG7zCb+8ikO8OJi2jsHh5YGoknNTyXgsbHOf1WOOcVU3kPFWT2WgCkQ==", - "dev": true, - "requires": { - "chardet": "^0.5.0", - "iconv-lite": "^0.4.22", - "tmp": "^0.0.33" - } - }, "extglob": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", @@ -3618,18 +3486,6 @@ "is-extglob": "^1.0.0" } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", - "dev": true - }, "factor-bundle": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/factor-bundle/-/factor-bundle-2.5.0.tgz", @@ -3864,18 +3720,6 @@ } } }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", @@ -3969,23 +3813,6 @@ "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", "dev": true }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "1.0.6", - "mime-types": "^2.1.12" - } - }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -4572,40 +4399,6 @@ } } }, - "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - } - }, - "fstream-ignore": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.2.tgz", - "integrity": "sha1-GMiR2wG3gqdKe/+Tag8kmXdBx6s=", - "dev": true, - "requires": { - "fstream": "^1.0.0", - "inherits": "2", - "minimatch": "^2.0.1" - }, - "dependencies": { - "minimatch": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", - "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", - "dev": true, - "requires": { - "brace-expansion": "^1.0.0" - } - } - } - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -4722,15 +4515,6 @@ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "gl-mat4": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gl-mat4/-/gl-mat4-1.2.0.tgz", @@ -5071,22 +4855,6 @@ "pify": "^3.0.0" } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, - "requires": { - "ajv": "^5.1.0", - "har-schema": "^2.0.0" - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -5197,18 +4965,6 @@ "minimalistic-assert": "^1.0.0" } }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "dev": true, - "requires": { - "boom": "4.x.x", - "cryptiles": "3.x.x", - "hoek": "4.x.x", - "sntp": "2.x.x" - } - }, "hex-rgb": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hex-rgb/-/hex-rgb-3.0.0.tgz", @@ -5239,12 +4995,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "hoek": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", - "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", - "dev": true - }, "hosted-git-info": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", @@ -5275,38 +5025,12 @@ "statuses": ">= 1.4.0 < 2" } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, - "i": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz", - "integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0=", - "dev": true - }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, "icosphere": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/icosphere/-/icosphere-1.0.0.tgz", @@ -5404,69 +5128,6 @@ } } }, - "inquirer": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.0.0.tgz", - "integrity": "sha512-tISQWRwtcAgrz+SHPhTH7d3e73k31gsOy6i1csonLc0u1dVK/wYvuOnFeiWqC5OXFIYbmrIFInef31wbT8MEJg==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.0", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.1.0", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "insert-module-globals": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.0.tgz", @@ -5633,12 +5294,6 @@ "resolved": "https://registry.npmjs.org/is-dom/-/is-dom-1.0.9.tgz", "integrity": "sha1-SDgy1SlyBz3hK5/j9gMghw2oNw0=" }, - "is-domain": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/is-domain/-/is-domain-0.0.1.tgz", - "integrity": "sha1-f/sojVzO1rB8Ty35HJvpFTURNI4=", - "dev": true - }, "is-dotfile": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", @@ -5675,15 +5330,6 @@ "number-is-nan": "^1.0.0" } }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, "is-function": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", @@ -5790,12 +5436,6 @@ "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", "dev": true }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -5823,12 +5463,6 @@ "isarray": "1.0.0" } }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, "js-base64": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.5.tgz", @@ -5857,13 +5491,6 @@ "esprima": "^2.6.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, "jsesc": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", @@ -5876,18 +5503,6 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, "json-stable-stringify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", @@ -5928,18 +5543,6 @@ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -6475,27 +6078,6 @@ "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", "dev": true }, - "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "dev": true - }, - "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "dev": true, - "requires": { - "mime-db": "~1.33.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, "min-document": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", @@ -6599,12 +6181,6 @@ "integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is=", "dev": true }, - "moniker": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/moniker/-/moniker-0.1.2.tgz", - "integrity": "sha1-hy37pXXc6o+gSlE1sT1fJL7MyX4=", - "dev": true - }, "mri": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.1.tgz", @@ -6623,12 +6199,6 @@ "integrity": "sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E=", "dev": true }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, "nan": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", @@ -6760,17 +6330,14 @@ } } }, - "ncp": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", - "integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ=", - "dev": true - }, - "netrc": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/netrc/-/netrc-0.1.4.tgz", - "integrity": "sha1-a+lPysqNd63gqWcNxGCRTJRHJEQ=", - "dev": true + "nextafter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nextafter/-/nextafter-1.0.0.tgz", + "integrity": "sha1-t9d7U1MQ4+CX5gJauwqQNHfsGjo=", + "dev": true, + "requires": { + "double-bits": "^1.1.0" + } }, "nice-try": { "version": "1.0.4", @@ -6843,6 +6410,12 @@ "sort-keys": "^1.0.0" } }, + "normals": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/normals/-/normals-1.1.0.tgz", + "integrity": "sha1-MltZXtNK/kZ6bFWhT9kIV4f/WcA=", + "dev": true + }, "npm-run-all": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.3.tgz", @@ -6886,12 +6459,6 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6999,15 +6566,6 @@ "wrappy": "1" } }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, "opn": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/opn/-/opn-3.0.3.tgz", @@ -7257,12 +6815,6 @@ "which": "^1.2.4" } }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, "perspective-camera": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/perspective-camera/-/perspective-camera-2.0.1.tgz", @@ -7293,18 +6845,30 @@ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, - "pkginfo": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", - "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=", - "dev": true - }, "plur": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/plur/-/plur-1.0.0.tgz", "integrity": "sha1-24XGgU9eXlo7Se/CjWBP7GKXUVY=", "dev": true }, + "polyline-miter-util": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/polyline-miter-util/-/polyline-miter-util-1.0.1.tgz", + "integrity": "sha1-tpPyOJ6g3tNqa89ezS7OS2kX2Vc=", + "dev": true, + "requires": { + "gl-vec2": "^1.0.0" + } + }, + "polyline-normals": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/polyline-normals/-/polyline-normals-2.0.2.tgz", + "integrity": "sha1-oXN+ddjA3MsaWR+csn8J7vS30TU=", + "dev": true, + "requires": { + "polyline-miter-util": "^1.0.1" + } + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -9352,12 +8916,41 @@ "integrity": "sha1-wgmT5PlgAjiSa7UA5SkFMEPvius=", "dev": true }, + "primitive-sphere": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/primitive-sphere/-/primitive-sphere-3.0.0.tgz", + "integrity": "sha1-dgm9pfb2ySUiE++jEnBg7mXm9zI=", + "dev": true, + "requires": { + "gl-mat4": "^1.1.4", + "gl-vec3": "^1.0.3" + } + }, + "primitive-torus": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/primitive-torus/-/primitive-torus-1.0.4.tgz", + "integrity": "sha1-e6721j2jmZqgsy8m0cSvezGENmg=", + "dev": true, + "requires": { + "defined": "^1.0.0", + "gl-vec3": "^1.0.3" + } + }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, + "probability-distributions": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/probability-distributions/-/probability-distributions-0.9.1.tgz", + "integrity": "sha1-xonI1iFzuygaCZnpgmOsx2pijlU=", + "dev": true, + "requires": { + "crypto": "0.0.3" + } + }, "process": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", @@ -9370,31 +8963,12 @@ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, - "progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", - "dev": true - }, "promise.series": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/promise.series/-/promise.series-0.2.0.tgz", "integrity": "sha1-LMfr6Vn8OmYZwEq029yeRS2GS70=", "dev": true }, - "prompt": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/prompt/-/prompt-0.2.14.tgz", - "integrity": "sha1-V3VPZPVD/XsIRXB8gY7OYY8F/9w=", - "dev": true, - "requires": { - "pkginfo": "0.x.x", - "read": "1.0.x", - "revalidator": "0.1.x", - "utile": "0.2.x", - "winston": "0.8.x" - } - }, "ps-tree": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.1.0.tgz", @@ -9435,12 +9009,6 @@ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", "dev": true }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, "query-string": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", @@ -9513,6 +9081,15 @@ "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", "dev": true }, + "rat-vec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/rat-vec/-/rat-vec-1.1.1.tgz", + "integrity": "sha1-Dd4rZrezS7G80qI4BerIBth/0X8=", + "dev": true, + "requires": { + "big-rat": "^1.0.3" + } + }, "ray-3d": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ray-3d/-/ray-3d-1.1.1.tgz", @@ -9559,15 +9136,6 @@ "gl-vec3": "^1.0.2" } }, - "read": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/read/-/read-1.0.5.tgz", - "integrity": "sha1-AHo9FpR4qnEKSRcn5FPv+5LnYgM=", - "dev": true, - "requires": { - "mute-stream": "~0.0.4" - } - }, "read-only-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", @@ -9789,36 +9357,6 @@ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, - "request": { - "version": "2.83.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "hawk": "~6.0.2", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "stringstream": "~0.0.5", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" - } - }, "require-from-string": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", @@ -9879,16 +9417,6 @@ } } }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, "resumer": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", @@ -9904,12 +9432,6 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, - "revalidator": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", - "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=", - "dev": true - }, "reversepoint": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/reversepoint/-/reversepoint-0.2.1.tgz", @@ -9939,6 +9461,61 @@ "inherits": "^2.0.1" } }, + "robust-in-sphere": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/robust-in-sphere/-/robust-in-sphere-1.1.3.tgz", + "integrity": "sha1-HFiD0WpOkjkpR27zSBmFe/Kpz3U=", + "dev": true, + "requires": { + "robust-scale": "^1.0.0", + "robust-subtract": "^1.0.0", + "robust-sum": "^1.0.0", + "two-product": "^1.0.0" + } + }, + "robust-orientation": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/robust-orientation/-/robust-orientation-1.1.3.tgz", + "integrity": "sha1-2v9bANO+TmByLw6cAVbvln8cIEk=", + "dev": true, + "requires": { + "robust-scale": "^1.0.2", + "robust-subtract": "^1.0.0", + "robust-sum": "^1.0.0", + "two-product": "^1.0.2" + } + }, + "robust-scale": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/robust-scale/-/robust-scale-1.0.2.tgz", + "integrity": "sha1-d1Ey7QlULQKOWLLMecBikLz3jDI=", + "dev": true, + "requires": { + "two-product": "^1.0.2", + "two-sum": "^1.0.0" + } + }, + "robust-segment-intersect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/robust-segment-intersect/-/robust-segment-intersect-1.0.1.tgz", + "integrity": "sha1-MlK2oPwboUreaRXMvgnLzpqrHBw=", + "dev": true, + "requires": { + "robust-orientation": "^1.1.3" + } + }, + "robust-subtract": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/robust-subtract/-/robust-subtract-1.0.0.tgz", + "integrity": "sha1-4LFk4e2LpOOl3aRaEgODSNvtPpo=", + "dev": true + }, + "robust-sum": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/robust-sum/-/robust-sum-1.0.0.tgz", + "integrity": "sha1-FmRuUlKStNJdgnV6KGlV4Lv6U9k=", + "dev": true + }, "rollup": { "version": "0.55.5", "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.55.5.tgz", @@ -10154,23 +9731,11 @@ "micromatch": "^2.3.11" } }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "rxjs": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.1.tgz", - "integrity": "sha512-OwMxHxmnmHTUpgO+V7dZChf3Tixf4ih95cmXjzzadULziVl/FKhHScGLj4goEw9weePVOH2Q0+GcCBUhKCZc/g==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } + "roughjs": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-2.2.3.tgz", + "integrity": "sha512-ZnlptJSz5Ii+Tcx3T60rZ9x4jZUK52iURLziYZiE3hjpaDJ5pzmYEnZiRpzRue6AUUK5uiMoJ9z+/pIO3eSKrA==", + "dev": true }, "sade": { "version": "1.4.1", @@ -10197,12 +9762,6 @@ "ret": "~0.1.10" } }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -10523,15 +10082,6 @@ "kind-of": "^3.2.0" } }, - "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", - "dev": true, - "requires": { - "hoek": "4.x.x" - } - }, "sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", @@ -10667,23 +10217,6 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "sshpk": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", - "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, "stack-trace": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", @@ -10811,17 +10344,6 @@ "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=", "dev": true }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, "string.prototype.padend": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz", @@ -10853,12 +10375,6 @@ "safe-buffer": "~5.1.0" } }, - "stringstream": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", - "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", - "dev": true - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -10904,53 +10420,6 @@ "has-flag": "^3.0.0" } }, - "surge": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/surge/-/surge-0.20.1.tgz", - "integrity": "sha512-/H8iz8TODGNKnoq2VGOwx+r2UqeA8lmjmSHw+GEroKGYSbKyM/JfjT4+sPcdb/Pl5uvux1+2+X1lTEDLv2ywFA==", - "dev": true, - "requires": { - "cli-table2": "^0.2.0", - "du": "0.1.0", - "fstream-ignore": "1.0.2", - "inquirer": "^6.0.0", - "is-domain": "0.0.1", - "minimist": "1.1.1", - "moniker": "0.1.2", - "netrc": "0.1.4", - "progress": "1.1.8", - "prompt": "~0.2.14", - "read": "1.0.5", - "request": "2.83.0", - "split": "0.3.1", - "surge-ignore": "0.2.0", - "tarr": "1.0.3", - "url-parse-as-address": "1.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.1.tgz", - "integrity": "sha1-G8K8cWWM3KVxJHVoQ2NhWwtPaVs=", - "dev": true - }, - "split": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.1.tgz", - "integrity": "sha1-zrzxQr9hu7ZLFBYo5ttIKikUZUw=", - "dev": true, - "requires": { - "through": "2" - } - } - } - }, - "surge-ignore": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/surge-ignore/-/surge-ignore-0.2.0.tgz", - "integrity": "sha1-Wn+KIKcRiM+edaLP6OsYLekNrzs=", - "dev": true - }, "svgo": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", @@ -10996,17 +10465,6 @@ "through": "~2.3.8" } }, - "tarr": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tarr/-/tarr-1.0.3.tgz", - "integrity": "sha512-ax05RcadKxg/EyogjIzzsT5NUl7B5Io9ASrv02bOdMFtt4E9w29Ok5kI66qqq7SNc5ai43DG91ZluBMtDjPoTQ==", - "dev": true, - "requires": { - "block-stream": "*", - "fstream": "^1.0.2", - "inherits": "2" - } - }, "term-color": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/term-color/-/term-color-1.0.1.tgz", @@ -11031,6 +10489,12 @@ } } }, + "tess2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tess2/-/tess2-1.0.0.tgz", + "integrity": "sha1-Li6yGCIpQGG4PUbLrv3QnYbd9ZM=", + "dev": true + }, "three": { "version": "0.93.0", "resolved": "https://registry.npmjs.org/three/-/three-0.93.0.tgz", @@ -11079,15 +10543,6 @@ "globrex": "^0.1.1" } }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -11142,13 +10597,14 @@ } } }, - "tough-cookie": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "triangulate-contours": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/triangulate-contours/-/triangulate-contours-1.0.2.tgz", + "integrity": "sha1-wl3cnw4AMfORB2TPF/aELS+PwnQ=", "dev": true, "requires": { - "punycode": "^1.4.1" + "tess2": "^1.0.0", + "xtend": "^4.0.0" } }, "trim": { @@ -11175,21 +10631,17 @@ "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", "dev": true }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } + "two-product": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/two-product/-/two-product-1.0.2.tgz", + "integrity": "sha1-Z9ldSyV6kh4stL16+VEfkIhSLqo=", + "dev": true }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true + "two-sum": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/two-sum/-/two-sum-1.0.0.tgz", + "integrity": "sha1-MdPzIjnk9zHsqd+RVeKyl/AIq2Q=", + "dev": true }, "type-check": { "version": "0.3.2", @@ -11206,6 +10658,16 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typedarray-pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/typedarray-pool/-/typedarray-pool-1.1.0.tgz", + "integrity": "sha1-0RT0hIAUifU+yrXoCIqiMET0mNk=", + "dev": true, + "requires": { + "bit-twiddle": "^1.0.0", + "dup": "^1.0.0" + } + }, "typescript": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", @@ -11273,6 +10735,12 @@ "integrity": "sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg==", "dev": true }, + "union-find": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/union-find/-/union-find-1.0.2.tgz", + "integrity": "sha1-KSusQV5q06iVNdI3AQ20pTYoTlg=", + "dev": true + }, "union-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", @@ -11402,12 +10870,6 @@ } } }, - "url-parse-as-address": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-as-address/-/url-parse-as-address-1.0.0.tgz", - "integrity": "sha1-+4CQGIPzOLPL7TU49fqiatr38uc=", - "dev": true - }, "url-trim": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-trim/-/url-trim-1.0.0.tgz", @@ -11446,34 +10908,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "utile": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/utile/-/utile-0.2.1.tgz", - "integrity": "sha1-kwyI6ZCY1iIINMNWy9mncFItkNc=", - "dev": true, - "requires": { - "async": "~0.2.9", - "deep-equal": "*", - "i": "0.3.x", - "mkdirp": "0.x.x", - "ncp": "0.4.x", - "rimraf": "2.x.x" - }, - "dependencies": { - "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", - "dev": true - } - } - }, - "uuid": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.0.tgz", - "integrity": "sha512-ijO9N2xY/YaOqQ5yz5c4sy2ZjWmA6AR6zASb/gdpeKZ8+948CxwfMW9RrKVk5may6ev8c0/Xguu32e2Llelpqw==", - "dev": true - }, "validate-npm-package-license": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", @@ -11502,17 +10936,6 @@ "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==", "dev": true }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "vlq": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", @@ -11584,41 +11007,6 @@ "isexe": "^2.0.0" } }, - "winston": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", - "integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=", - "dev": true, - "requires": { - "async": "0.2.x", - "colors": "0.6.x", - "cycle": "1.0.x", - "eyes": "0.1.x", - "isstream": "0.1.x", - "pkginfo": "0.3.x", - "stack-trace": "0.0.x" - }, - "dependencies": { - "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", - "dev": true - }, - "colors": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", - "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=", - "dev": true - }, - "pkginfo": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", - "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=", - "dev": true - } - } - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", diff --git a/package.json b/package.json index 30e1dae..8bee0b8 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "dateformat": "^3.0.3", "deep-equal": "^1.0.1", "defined": "^1.0.0", + "euclidean-distance": "^1.0.0", "factor-bundle": "^2.5.0", "get-canvas-context": "^1.0.2", "gl-matrix": "^2.6.1", @@ -40,9 +41,13 @@ "bound-points": "^1.0.0", "browserify": "^16.2.2", "budo": "^11.3.0", + "cdt2d": "^1.0.0", + "clamp": "^1.0.1", "classnames": "^2.2.6", + "clean-pslg": "^1.1.2", "concat-stream": "^1.6.2", "duplexer2": "^0.1.4", + "earcut": "^2.1.3", "eases": "^1.0.8", "glsl-aastep": "^1.0.1", "glsl-dither": "^1.0.1", @@ -56,21 +61,28 @@ "mat4-recompose": "^1.0.4", "microbundle": "^0.4.4", "mkdirp": "^0.5.1", + "normals": "^1.1.0", "npm-run-all": "^4.1.3", "perspective-camera": "^2.0.1", + "polyline-miter-util": "^1.0.1", + "polyline-normals": "^2.0.2", "preact": "^8.2.9", "preact-router": "^2.6.1", "primitive-circle": "^1.0.2", "primitive-icosphere": "^1.0.2", "primitive-quad": "^2.0.0", + "primitive-sphere": "^3.0.0", + "primitive-torus": "^1.0.4", + "probability-distributions": "^0.9.1", "regl": "^1.3.7", "rimraf": "^2.6.2", + "roughjs": "^2.2.3", "seed-random": "^2.2.0", "simplex-noise": "^2.4.0", "smoothstep": "^1.0.1", - "surge": "^0.20.1", "three": "^0.93.0", "through2": "^2.0.3", + "triangulate-contours": "^1.0.2", "uglify-es": "^3.3.9", "unlerp": "^1.0.1", "watchify": "^3.11.0"