diff --git a/bundle.js b/bundle.js index a28003b..2137384 100644 --- a/bundle.js +++ b/bundle.js @@ -468,87 +468,66 @@ function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.const Object.defineProperty(exports, "__esModule", { value: true }); - exports.applyChaos = exports.scaleVertices = exports.createRegularVertices = undefined; + exports.applyChaos = undefined; var _utils = require('./utils'); /** - * Calculate the position of a regular polygon's vertices - * inside a 2 x 2 squared centered on the origin + * Choose an index at random among a list of weights, + * more weighted indices have a greater proability to be chosen * - * @param {number} count Vertices amount - * @return {Array} Array of points representing the vertices + * @param {Array} weights List of weights + * @return {number} Selected index */ - var createRegularVertices = exports.createRegularVertices = function createRegularVertices(count) { - var step = 2 * Math.PI / count; - var initial = -Math.atan(Math.sin(step) / (Math.cos(step) - 1)); - var result = []; + var chooseIndex = function chooseIndex(weights) { + var number = Math.random(); + var sum = 0, + index = 0; - for (var i = 0; i < count; i += 1) { - var current = step * i + initial; - - result.push([Math.cos(current), Math.sin(current)]); + while (number >= sum) { + sum += weights[index]; + index += 1; } - return result; + return index - 1; }; /** - * Scale the vertices so that they fit in given bounding rectangle + * Apply the chaos game: starting from `start`, we plot + * the next `n` points. To get to the next point, we apply + * a random transformation among given ones * - * @param {number} width Bounding rectangle width - * @param {number} height Bounding rectangle height - * @param {Array} vertices Vertices to scale - * @return {Array} Scaled vertices - */ - var scaleVertices = exports.scaleVertices = function scaleVertices(width, height, vertices) { - var centerX = Math.floor(width / 2); - var centerY = Math.floor(height / 2); - var radius = Math.min(centerX, centerY); - - return vertices.map(function (vertex) { - return [vertex[0] * radius + centerX, vertex[1] * radius + centerY]; - }); - }; - - /** - * Apply the chaos game algorithm in a polygon - * of given vertices, with given fraction - * - * @param {ImageData} image Image to write on Data to amend - * @param {number} fraction Fraction to use - * @param {Array} vertices List of vertices of the bounding polygon + * @param {ImageData} image Image to write on + * @param {Array} start Starting point + * @param {number} iterations Number of points to plot + * @param {Array} transforms List of available transforms + * @param {Array} weights Probability weights for each transform * @return {null} */ - var applyChaos = exports.applyChaos = function applyChaos(image, fraction, vertices) { - var count = vertices.length, - imageWidth = image.width; + var applyChaos = exports.applyChaos = function applyChaos(image, start, iterations, transforms, weights) { + var width = image.width; + var point = start; - // now we apply the chaos algorithm: - // for any point, the next point is a `fraction` of the - // distance between it and a random vertex - var point = vertices[0]; - var iterations = Math.floor(500 * imageWidth * fraction); - var drop = Math.floor(iterations / 200); + if (weights === undefined) { + weights = Array.apply(null, Array(transforms.length)).map(function () { + return 1 / transforms.length; + }); + } while (iterations--) { - var vertexNumber = (0, _utils.getRandomNumber)(0, count); - var vertex = vertices[vertexNumber], - color = (0, _utils.getColor)(vertexNumber); + var index = chooseIndex(weights); + var color = (0, _utils.getColor)(2); - point = [Math.floor((point[0] - vertex[0]) * fraction + vertex[0]), Math.floor((point[1] - vertex[1]) * fraction + vertex[1])]; + // console.log(point); + // console.log(point.map(x => Math.floor(x * 10 + 100))); + point = transforms[index](point); - // skip the first 1000 points - if (drop === 0) { - var i = (point[1] * imageWidth + point[0]) * 4; + var i = (Math.floor(point[1] * 50 + 50) * width + Math.floor(point[0] * 50 + 200)) * 4; - image.data[i] = color[0]; - image.data[i + 1] = color[1]; - image.data[i + 2] = color[2]; - image.data[i + 3] = 255; - } else { - drop--; - } + image.data[i] = color[0]; + image.data[i + 1] = color[1]; + image.data[i + 2] = color[2]; + image.data[i + 3] = 255; } }; }, { "./utils": 8 }], 7: [function (require, module, exports) { @@ -556,8 +535,6 @@ function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.const var _theDom = require('the-dom'); - var _utils = require('./utils'); - var _chaos = require('./chaos'); var _html = (0, _theDom.html)(document); @@ -565,16 +542,18 @@ function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.const var body = _html.body; var content = body.find('#content'); - var verticesRange = body.find('#vertices'); - var fractionRange = body.find('#fraction'); - var plotting = body.find('#plotting').node; var ctx = plotting.getContext('2d'); var padding = 40; // padding between the canvas edges and the points var width = undefined, - height = undefined, - vertices = undefined; + height = undefined; + + var linearTransform = function linearTransform(a, b, c, d, e, f) { + return function (point) { + return [a * point[0] + b * point[1] + e, c * point[0] + d * point[1] + f]; + }; + }; /** * Re-render the scene from scratch @@ -582,9 +561,6 @@ function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.const * @return {null} */ var render = function render() { - var fraction = 1 / parseFloat(fractionRange.node.value); - var scaledVerts = (0, _chaos.scaleVertices)(width, height, vertices); - plotting.width = width + 2 * padding; plotting.height = height + 2 * padding; @@ -593,33 +569,11 @@ function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.const return; } - // draw the polygon - ctx.strokeStyle = '#aaa'; - ctx.lineWidth = 1; - - ctx.beginPath(); - - for (var i = 0; i < vertices.length; i += 1) { - ctx.lineTo(scaledVerts[i][0] + padding, scaledVerts[i][1] + padding); - } - - ctx.closePath(); - ctx.stroke(); - - // draw the vertices - for (var i = 0; i < vertices.length; i += 1) { - ctx.beginPath(); - ctx.fillStyle = 'rgb(' + (0, _utils.getColor)(i).join(', ') + ')'; - - ctx.arc(scaledVerts[i][0] + padding, scaledVerts[i][1] + padding, 4, 0, Math.PI * 2); - - ctx.fill(); - } - // do the chaos game var image = ctx.getImageData(padding, padding, width, height); - (0, _chaos.applyChaos)(image, fraction, scaledVerts); + (0, _chaos.applyChaos)(image, [0, 0], 500000, [linearTransform(0, 0, 0, 0.16, 0, 0), linearTransform(.85, .04, -.04, .85, 0, 1.6), linearTransform(.20, -.26, .23, .22, 0, 1.6), linearTransform(-.15, .28, .26, .24, 0, .44)], [.01, .85, .07, .07]); + ctx.putImageData(image, padding, padding); }; @@ -635,21 +589,9 @@ function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.const render(); }; - /** - * Create new vertices - */ - verticesRange.on('input', function () { - vertices = (0, _chaos.createRegularVertices)(parseInt(verticesRange.node.value, 10)); - - render(); - }); - window.onresize = resize; - fractionRange.on('input', render); - - vertices = (0, _chaos.createRegularVertices)(3); resize(); - }, { "./chaos": 6, "./utils": 8, "the-dom": 1 }], 8: [function (require, module, exports) { + }, { "./chaos": 6, "the-dom": 1 }], 8: [function (require, module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { diff --git a/scripts/chaos.js b/scripts/chaos.js index f5f6b81..6887197 100644 --- a/scripts/chaos.js +++ b/scripts/chaos.js @@ -1,85 +1,64 @@ 'use strict'; -import { getRandomNumber, getColor } from './utils'; +import { getColor } from './utils'; /** - * Calculate the position of a regular polygon's vertices - * inside a 2 x 2 squared centered on the origin + * Choose an index at random among a list of weights, + * more weighted indices have a greater proability to be chosen * - * @param {number} count Vertices amount - * @return {Array} Array of points representing the vertices + * @param {Array} weights List of weights + * @return {number} Selected index */ -export const createRegularVertices = count => { - const step = 2 * Math.PI / count; - const initial = -Math.atan(Math.sin(step) / (Math.cos(step) - 1)); - const result = []; +const chooseIndex = weights => { + const number = Math.random(); + let sum = 0, index = 0; - for (let i = 0; i < count; i += 1) { - let current = step * i + initial; - - result.push([Math.cos(current), Math.sin(current)]); + while (number >= sum) { + sum += weights[index]; + index += 1; } - return result; + return index - 1; }; /** - * Scale the vertices so that they fit in given bounding rectangle + * Apply the chaos game: starting from `start`, we plot + * the next `n` points. To get to the next point, we apply + * a random transformation among given ones * - * @param {number} width Bounding rectangle width - * @param {number} height Bounding rectangle height - * @param {Array} vertices Vertices to scale - * @return {Array} Scaled vertices - */ -export const scaleVertices = (width, height, vertices) => { - const centerX = Math.floor(width / 2); - const centerY = Math.floor(height / 2); - const radius = Math.min(centerX, centerY); - - return vertices.map(vertex => ([ - vertex[0] * radius + centerX, - vertex[1] * radius + centerY - ])); -}; - -/** - * Apply the chaos game algorithm in a polygon - * of given vertices, with given fraction - * - * @param {ImageData} image Image to write on Data to amend - * @param {number} fraction Fraction to use - * @param {Array} vertices List of vertices of the bounding polygon + * @param {ImageData} image Image to write on + * @param {Array} start Starting point + * @param {number} iterations Number of points to plot + * @param {Array} transforms List of available transforms + * @param {Array} weights Probability weights for each transform * @return {null} */ -export const applyChaos = (image, fraction, vertices) => { - const count = vertices.length, imageWidth = image.width; +export const applyChaos = (image, start, iterations, transforms, weights) => { + const width = image.width; + let point = start; - // now we apply the chaos algorithm: - // for any point, the next point is a `fraction` of the - // distance between it and a random vertex - let point = vertices[0]; - let iterations = Math.floor(500 * imageWidth * fraction); - let drop = Math.floor(iterations / 200); + if (weights === undefined) { + weights = Array.apply(null, Array(transforms.length)).map( + () => 1 / transforms.length + ); + } while (iterations--) { - const vertexNumber = getRandomNumber(0, count); - const vertex = vertices[vertexNumber], color = getColor(vertexNumber); + const index = chooseIndex(weights); + const color = getColor(2); - point = [ - Math.floor((point[0] - vertex[0]) * fraction + vertex[0]), - Math.floor((point[1] - vertex[1]) * fraction + vertex[1]) - ]; + // console.log(point); + // console.log(point.map(x => Math.floor(x * 10 + 100))); + point = transforms[index](point); - // skip the first 1000 points - if (drop === 0) { - const i = (point[1] * imageWidth + point[0]) * 4; + const i = ( + Math.floor(point[1] * 50 + 50) * width + + Math.floor(point[0] * 50 + 200) + ) * 4; - image.data[i] = color[0]; - image.data[i + 1] = color[1]; - image.data[i + 2] = color[2]; - image.data[i + 3] = 255; - } else { - drop--; - } + image.data[i] = color[0]; + image.data[i + 1] = color[1]; + image.data[i + 2] = color[2]; + image.data[i + 3] = 255; } }; diff --git a/scripts/index.js b/scripts/index.js index c3069d9..6a8a60c 100644 --- a/scripts/index.js +++ b/scripts/index.js @@ -1,20 +1,21 @@ 'use strict'; import { html } from 'the-dom'; -import { getColor } from './utils'; -import { createRegularVertices, scaleVertices, applyChaos } from './chaos'; +import { applyChaos } from './chaos'; const { body } = html(document); const content = body.find('#content'); -const verticesRange = body.find('#vertices'); -const fractionRange = body.find('#fraction'); - const plotting = body.find('#plotting').node; const ctx = plotting.getContext('2d'); const padding = 40; // padding between the canvas edges and the points -let width, height, vertices; +let width, height; + +const linearTransform = (a, b, c, d, e, f) => point => [ + a * point[0] + b * point[1] + e, + c * point[0] + d * point[1] + f +]; /** * Re-render the scene from scratch @@ -22,9 +23,6 @@ let width, height, vertices; * @return {null} */ const render = () => { - const fraction = 1 / parseFloat(fractionRange.node.value); - const scaledVerts = scaleVertices(width, height, vertices); - plotting.width = width + 2 * padding; plotting.height = height + 2 * padding; @@ -33,36 +31,16 @@ const render = () => { return; } - // draw the polygon - ctx.strokeStyle = '#aaa'; - ctx.lineWidth = 1; - - ctx.beginPath(); - - for (let i = 0; i < vertices.length; i += 1) { - ctx.lineTo(scaledVerts[i][0] + padding, scaledVerts[i][1] + padding); - } - - ctx.closePath(); - ctx.stroke(); - - // draw the vertices - for (let i = 0; i < vertices.length; i += 1) { - ctx.beginPath(); - ctx.fillStyle = 'rgb(' + getColor(i).join(', ') + ')'; - - ctx.arc( - scaledVerts[i][0] + padding, scaledVerts[i][1] + padding, - 4, 0, Math.PI * 2 - ); - - ctx.fill(); - } - // do the chaos game const image = ctx.getImageData(padding, padding, width, height); - applyChaos(image, fraction, scaledVerts); + applyChaos(image, [0, 0], 500000, [ + linearTransform(0, 0, 0, 0.16, 0, 0), + linearTransform(.85, .04, -.04, .85, 0, 1.6), + linearTransform(.20, -.26, .23, .22, 0, 1.6), + linearTransform(-.15, .28, .26, .24, 0, .44) + ], [.01, .85, .07, .07]); + ctx.putImageData(image, padding, padding); }; @@ -78,19 +56,5 @@ const resize = () => { render(); }; -/** - * Create new vertices - */ -verticesRange.on('input', () => { - vertices = createRegularVertices( - parseInt(verticesRange.node.value, 10) - ); - - render(); -}); - window.onresize = resize; -fractionRange.on('input', render); - -vertices = createRegularVertices(3); resize();