💡 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", {
|
Object.defineProperty(exports, "__esModule", {
|
||||||
value: true
|
value: true
|
||||||
});
|
});
|
||||||
exports.applyChaos = exports.scaleVertices = exports.createRegularVertices = undefined;
|
exports.applyChaos = undefined;
|
||||||
|
|
||||||
var _utils = require('./utils');
|
var _utils = require('./utils');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the position of a regular polygon's vertices
|
* Choose an index at random among a list of weights,
|
||||||
* inside a 2 x 2 squared centered on the origin
|
* more weighted indices have a greater proability to be chosen
|
||||||
*
|
*
|
||||||
* @param {number} count Vertices amount
|
* @param {Array} weights List of weights
|
||||||
* @return {Array<Array>} Array of points representing the vertices
|
* @return {number} Selected index
|
||||||
*/
|
*/
|
||||||
var createRegularVertices = exports.createRegularVertices = function createRegularVertices(count) {
|
var chooseIndex = function chooseIndex(weights) {
|
||||||
var step = 2 * Math.PI / count;
|
var number = Math.random();
|
||||||
var initial = -Math.atan(Math.sin(step) / (Math.cos(step) - 1));
|
var sum = 0,
|
||||||
var result = [];
|
index = 0;
|
||||||
|
|
||||||
for (var i = 0; i < count; i += 1) {
|
while (number >= sum) {
|
||||||
var current = step * i + initial;
|
sum += weights[index];
|
||||||
|
index += 1;
|
||||||
result.push([Math.cos(current), Math.sin(current)]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {ImageData} image Image to write on
|
||||||
* @param {number} height Bounding rectangle height
|
* @param {Array} start Starting point
|
||||||
* @param {Array<Array>} vertices Vertices to scale
|
* @param {number} iterations Number of points to plot
|
||||||
* @return {Array<Array>} Scaled vertices
|
* @param {Array} transforms List of available transforms
|
||||||
*/
|
* @param {Array} weights Probability weights for each transform
|
||||||
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
|
|
||||||
* @return {null}
|
* @return {null}
|
||||||
*/
|
*/
|
||||||
var applyChaos = exports.applyChaos = function applyChaos(image, fraction, vertices) {
|
var applyChaos = exports.applyChaos = function applyChaos(image, start, iterations, transforms, weights) {
|
||||||
var count = vertices.length,
|
var width = image.width;
|
||||||
imageWidth = image.width;
|
var point = start;
|
||||||
|
|
||||||
// now we apply the chaos algorithm:
|
if (weights === undefined) {
|
||||||
// for any point, the next point is a `fraction` of the
|
weights = Array.apply(null, Array(transforms.length)).map(function () {
|
||||||
// distance between it and a random vertex
|
return 1 / transforms.length;
|
||||||
var point = vertices[0];
|
});
|
||||||
var iterations = Math.floor(500 * imageWidth * fraction);
|
}
|
||||||
var drop = Math.floor(iterations / 200);
|
|
||||||
|
|
||||||
while (iterations--) {
|
while (iterations--) {
|
||||||
var vertexNumber = (0, _utils.getRandomNumber)(0, count);
|
var index = chooseIndex(weights);
|
||||||
var vertex = vertices[vertexNumber],
|
var color = (0, _utils.getColor)(2);
|
||||||
color = (0, _utils.getColor)(vertexNumber);
|
|
||||||
|
|
||||||
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
|
var i = (Math.floor(point[1] * 50 + 50) * width + Math.floor(point[0] * 50 + 200)) * 4;
|
||||||
if (drop === 0) {
|
|
||||||
var i = (point[1] * imageWidth + point[0]) * 4;
|
|
||||||
|
|
||||||
image.data[i] = color[0];
|
image.data[i] = color[0];
|
||||||
image.data[i + 1] = color[1];
|
image.data[i + 1] = color[1];
|
||||||
image.data[i + 2] = color[2];
|
image.data[i + 2] = color[2];
|
||||||
image.data[i + 3] = 255;
|
image.data[i + 3] = 255;
|
||||||
} else {
|
|
||||||
drop--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, { "./utils": 8 }], 7: [function (require, module, exports) {
|
}, { "./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 _theDom = require('the-dom');
|
||||||
|
|
||||||
var _utils = require('./utils');
|
|
||||||
|
|
||||||
var _chaos = require('./chaos');
|
var _chaos = require('./chaos');
|
||||||
|
|
||||||
var _html = (0, _theDom.html)(document);
|
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 body = _html.body;
|
||||||
|
|
||||||
var content = body.find('#content');
|
var content = body.find('#content');
|
||||||
var verticesRange = body.find('#vertices');
|
|
||||||
var fractionRange = body.find('#fraction');
|
|
||||||
|
|
||||||
var plotting = body.find('#plotting').node;
|
var plotting = body.find('#plotting').node;
|
||||||
var ctx = plotting.getContext('2d');
|
var ctx = plotting.getContext('2d');
|
||||||
|
|
||||||
var padding = 40; // padding between the canvas edges and the points
|
var padding = 40; // padding between the canvas edges and the points
|
||||||
var width = undefined,
|
var width = undefined,
|
||||||
height = undefined,
|
height = undefined;
|
||||||
vertices = 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
|
* Re-render the scene from scratch
|
||||||
|
@ -582,9 +561,6 @@ function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.const
|
||||||
* @return {null}
|
* @return {null}
|
||||||
*/
|
*/
|
||||||
var render = function render() {
|
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.width = width + 2 * padding;
|
||||||
plotting.height = height + 2 * padding;
|
plotting.height = height + 2 * padding;
|
||||||
|
|
||||||
|
@ -593,33 +569,11 @@ function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.const
|
||||||
return;
|
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
|
// do the chaos game
|
||||||
var image = ctx.getImageData(padding, padding, width, height);
|
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);
|
ctx.putImageData(image, padding, padding);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -635,21 +589,9 @@ function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.const
|
||||||
render();
|
render();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Create new vertices
|
|
||||||
*/
|
|
||||||
verticesRange.on('input', function () {
|
|
||||||
vertices = (0, _chaos.createRegularVertices)(parseInt(verticesRange.node.value, 10));
|
|
||||||
|
|
||||||
render();
|
|
||||||
});
|
|
||||||
|
|
||||||
window.onresize = resize;
|
window.onresize = resize;
|
||||||
fractionRange.on('input', render);
|
|
||||||
|
|
||||||
vertices = (0, _chaos.createRegularVertices)(3);
|
|
||||||
resize();
|
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';
|
'use strict';
|
||||||
|
|
||||||
Object.defineProperty(exports, "__esModule", {
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
|
103
scripts/chaos.js
103
scripts/chaos.js
|
@ -1,85 +1,64 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { getRandomNumber, getColor } from './utils';
|
import { getColor } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the position of a regular polygon's vertices
|
* Choose an index at random among a list of weights,
|
||||||
* inside a 2 x 2 squared centered on the origin
|
* more weighted indices have a greater proability to be chosen
|
||||||
*
|
*
|
||||||
* @param {number} count Vertices amount
|
* @param {Array} weights List of weights
|
||||||
* @return {Array<Array>} Array of points representing the vertices
|
* @return {number} Selected index
|
||||||
*/
|
*/
|
||||||
export const createRegularVertices = count => {
|
const chooseIndex = weights => {
|
||||||
const step = 2 * Math.PI / count;
|
const number = Math.random();
|
||||||
const initial = -Math.atan(Math.sin(step) / (Math.cos(step) - 1));
|
let sum = 0, index = 0;
|
||||||
const result = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < count; i += 1) {
|
while (number >= sum) {
|
||||||
let current = step * i + initial;
|
sum += weights[index];
|
||||||
|
index += 1;
|
||||||
result.push([Math.cos(current), Math.sin(current)]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {ImageData} image Image to write on
|
||||||
* @param {number} height Bounding rectangle height
|
* @param {Array} start Starting point
|
||||||
* @param {Array<Array>} vertices Vertices to scale
|
* @param {number} iterations Number of points to plot
|
||||||
* @return {Array<Array>} Scaled vertices
|
* @param {Array} transforms List of available transforms
|
||||||
*/
|
* @param {Array} weights Probability weights for each transform
|
||||||
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
|
|
||||||
* @return {null}
|
* @return {null}
|
||||||
*/
|
*/
|
||||||
export const applyChaos = (image, fraction, vertices) => {
|
export const applyChaos = (image, start, iterations, transforms, weights) => {
|
||||||
const count = vertices.length, imageWidth = image.width;
|
const width = image.width;
|
||||||
|
let point = start;
|
||||||
|
|
||||||
// now we apply the chaos algorithm:
|
if (weights === undefined) {
|
||||||
// for any point, the next point is a `fraction` of the
|
weights = Array.apply(null, Array(transforms.length)).map(
|
||||||
// distance between it and a random vertex
|
() => 1 / transforms.length
|
||||||
let point = vertices[0];
|
);
|
||||||
let iterations = Math.floor(500 * imageWidth * fraction);
|
}
|
||||||
let drop = Math.floor(iterations / 200);
|
|
||||||
|
|
||||||
while (iterations--) {
|
while (iterations--) {
|
||||||
const vertexNumber = getRandomNumber(0, count);
|
const index = chooseIndex(weights);
|
||||||
const vertex = vertices[vertexNumber], color = getColor(vertexNumber);
|
const color = getColor(2);
|
||||||
|
|
||||||
point = [
|
// console.log(point);
|
||||||
Math.floor((point[0] - vertex[0]) * fraction + vertex[0]),
|
// console.log(point.map(x => Math.floor(x * 10 + 100)));
|
||||||
Math.floor((point[1] - vertex[1]) * fraction + vertex[1])
|
point = transforms[index](point);
|
||||||
];
|
|
||||||
|
|
||||||
// skip the first 1000 points
|
const i = (
|
||||||
if (drop === 0) {
|
Math.floor(point[1] * 50 + 50) * width +
|
||||||
const i = (point[1] * imageWidth + point[0]) * 4;
|
Math.floor(point[0] * 50 + 200)
|
||||||
|
) * 4;
|
||||||
|
|
||||||
image.data[i] = color[0];
|
image.data[i] = color[0];
|
||||||
image.data[i + 1] = color[1];
|
image.data[i + 1] = color[1];
|
||||||
image.data[i + 2] = color[2];
|
image.data[i + 2] = color[2];
|
||||||
image.data[i + 3] = 255;
|
image.data[i + 3] = 255;
|
||||||
} else {
|
|
||||||
drop--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { html } from 'the-dom';
|
import { html } from 'the-dom';
|
||||||
import { getColor } from './utils';
|
import { applyChaos } from './chaos';
|
||||||
import { createRegularVertices, scaleVertices, applyChaos } from './chaos';
|
|
||||||
|
|
||||||
const { body } = html(document);
|
const { body } = html(document);
|
||||||
|
|
||||||
const content = body.find('#content');
|
const content = body.find('#content');
|
||||||
const verticesRange = body.find('#vertices');
|
|
||||||
const fractionRange = body.find('#fraction');
|
|
||||||
|
|
||||||
const plotting = body.find('#plotting').node;
|
const plotting = body.find('#plotting').node;
|
||||||
const ctx = plotting.getContext('2d');
|
const ctx = plotting.getContext('2d');
|
||||||
|
|
||||||
const padding = 40; // padding between the canvas edges and the points
|
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
|
* Re-render the scene from scratch
|
||||||
|
@ -22,9 +23,6 @@ let width, height, vertices;
|
||||||
* @return {null}
|
* @return {null}
|
||||||
*/
|
*/
|
||||||
const render = () => {
|
const render = () => {
|
||||||
const fraction = 1 / parseFloat(fractionRange.node.value);
|
|
||||||
const scaledVerts = scaleVertices(width, height, vertices);
|
|
||||||
|
|
||||||
plotting.width = width + 2 * padding;
|
plotting.width = width + 2 * padding;
|
||||||
plotting.height = height + 2 * padding;
|
plotting.height = height + 2 * padding;
|
||||||
|
|
||||||
|
@ -33,36 +31,16 @@ const render = () => {
|
||||||
return;
|
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
|
// do the chaos game
|
||||||
const image = ctx.getImageData(padding, padding, width, height);
|
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);
|
ctx.putImageData(image, padding, padding);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,19 +56,5 @@ const resize = () => {
|
||||||
render();
|
render();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Create new vertices
|
|
||||||
*/
|
|
||||||
verticesRange.on('input', () => {
|
|
||||||
vertices = createRegularVertices(
|
|
||||||
parseInt(verticesRange.node.value, 10)
|
|
||||||
);
|
|
||||||
|
|
||||||
render();
|
|
||||||
});
|
|
||||||
|
|
||||||
window.onresize = resize;
|
window.onresize = resize;
|
||||||
fractionRange.on('input', render);
|
|
||||||
|
|
||||||
vertices = createRegularVertices(3);
|
|
||||||
resize();
|
resize();
|
||||||
|
|
Loading…
Reference in New Issue