💡 Trying with linear transformations
This commit is contained in:
		
							parent
							
								
									8f94659eea
								
							
						
					
					
						commit
						58a4784a71
					
				
							
								
								
									
										156
									
								
								bundle.js
								
								
								
								
							
							
						
						
									
										156
									
								
								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>} 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<Array>} vertices Vertices to scale | ||||
|          * @return {Array<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", { | ||||
|  |  | |||
							
								
								
									
										103
									
								
								scripts/chaos.js
								
								
								
								
							
							
						
						
									
										103
									
								
								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>} 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<Array>} vertices Vertices to scale | ||||
|  * @return {Array<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; | ||||
|     } | ||||
| }; | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue