Skip to content

Instantly share code, notes, and snippets.

@ikr7
Last active May 1, 2017 22:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ikr7/a4c9a0aa46dab41543c6c41cd875cfaf to your computer and use it in GitHub Desktop.
Save ikr7/a4c9a0aa46dab41543c6c41cd875cfaf to your computer and use it in GitHub Desktop.
Fractal Dimension Measurement
license: mit
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Fractal Dimension Measurement</title>
<style media="screen">
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
position: relative;
}
canvas {
cursor: pointer;
}
form {
position: absolute;
top: 1em;
left: 1em;
}
</style>
</head>
<body>
<canvas></canvas>
<form>
<div>
Dim ≈ <input type="text" id="dimension-display" disabled="disabled">
<input type="button" id="measure-button" value="Measure">
<input type="button" id="clear-button" value="Clear">
<input type="button" id="line-button" value="Line">
<input type="button" id="circumference-button" value="Circumference">
<input type="button" id="rectangle-button" value="Rectangle">
<input type="button" id="circle-button" value="Circle">
<input type="button" id="sierpinski-button" value="Sierpinski Triangle">
<input type="button" id="random-walk-button" value="Random Walk">
</form>
</body>
<script>
const $ = document.querySelector.bind(document);
const width = 960;
const height = 500;
const drawView = $('canvas');
const drawViewContext = drawView.getContext('2d');
document.body.style.width = width;
document.body.style.height = height;
drawView.width = width;
drawView.height = height;
drawViewContext.lineWidth = 2;
let clicked = false;
let lastX;
let lastY;
drawView.addEventListener('mousedown', (e) => {
clicked = true;
lastX = e.offsetX;
lastY = e.offsetY;
drawViewContext.fillRect(e.offsetX, e.offsetY, 1, 1);
});
drawView.addEventListener('mouseup', (e) => {
clicked = false;
});
drawView.addEventListener('mouseout', (e) => {
clicked = false;
});
drawView.addEventListener('mousemove', (e) => {
if (clicked) {
drawViewContext.beginPath();
drawViewContext.moveTo(lastX, lastY);
drawViewContext.lineTo(e.offsetX, e.offsetY);
drawViewContext.stroke();
lastX = e.offsetX;
lastY = e.offsetY;
}
});
const measureButton = $('#measure-button');
const dimensionDisplay = $('#dimension-display')
measureButton.addEventListener('click', () => {
const dimension = measure();
dimensionDisplay.value = dimension.toFixed(4);
});
const clearButton = $('#clear-button');
clearButton.addEventListener('click', clear);
function clear () {
drawViewContext.clearRect(0, 0, width, height);
}
const lineButton = $('#line-button');
lineButton.addEventListener('click', () => {
clear();
drawViewContext.beginPath();
drawViewContext.moveTo(100, height / 2);
drawViewContext.lineTo(width - 100, height / 2);
drawViewContext.stroke();
});
const circumferenceButton = $('#circumference-button');
circumferenceButton.addEventListener('click', () => {
clear();
drawViewContext.beginPath();
drawViewContext.arc(width / 2, height / 2, 200, 0, 2 * Math.PI, false);
drawViewContext.closePath();
drawViewContext.stroke();
});
const rectangleButton = $('#rectangle-button');
rectangleButton.addEventListener('click', () => {
clear();
drawViewContext.fillRect(100, 100, width - 200, height - 200);
});
const circleButton = $('#circle-button');
circleButton.addEventListener('click', () => {
clear();
drawViewContext.beginPath();
drawViewContext.arc(width / 2, height / 2, 200, 0, 2 * Math.PI, false);
drawViewContext.closePath();
drawViewContext.fill();
});
const sierpinskiButton = $('#sierpinski-button');
sierpinskiButton.addEventListener('click', () => {
function triangle (x, y, size) {
const xs = [
x + size * Math.cos(30 * Math.PI / 180),
x + size * Math.cos(150 * Math.PI / 180),
x + size * Math.cos(270 * Math.PI / 180)
];
const ys = [
y + size * Math.sin(30 * Math.PI / 180),
y + size * Math.sin(150 * Math.PI / 180),
y + size * Math.sin(270 * Math.PI / 180)
];
drawViewContext.beginPath();
drawViewContext.moveTo(xs[0], ys[0]);
drawViewContext.lineTo(xs[1], ys[1]);
drawViewContext.lineTo(xs[2], ys[2]);
drawViewContext.closePath();
drawViewContext.fill();
}
function drawSierpinski (x, y, size, depth) {
if (depth <= 0) {
triangle(x, y, size);
return;
}
drawSierpinski(
x + size / 2 * Math.cos(30 * Math.PI / 180),
y + size / 2 * Math.sin(30 * Math.PI / 180),
size / 2,
depth - 1
);
drawSierpinski(
x + size / 2 * Math.cos(150 * Math.PI / 180),
y + size / 2 * Math.sin(150 * Math.PI / 180),
size / 2,
depth - 1
);
drawSierpinski(
x + size / 2 * Math.cos(270 * Math.PI / 180),
y + size / 2 * Math.sin(270 * Math.PI / 180),
size / 2,
depth - 1
);
};
clear();
drawSierpinski(width / 2, height / 2 + 75, 250, 8);
});
const randomWalkButton = $('#random-walk-button');
randomWalkButton.addEventListener('click', () => {
clear();
let x = width / 2;
let y = height / 2;
drawViewContext.beginPath();
drawViewContext.moveTo(width / 2, height / 2);
for (let i = 0; i < 1000; i++) {
x += Math.round(10 * (Math.random() * 2 - 1));
y += Math.round(10 * (Math.random() * 2 - 1));
drawViewContext.lineTo(x, y);
}
drawViewContext.stroke();
});
function countHitGrids (gridSize) {
let count = 0;
const image = drawViewContext.getImageData(0, 0, width, height);
for (let y = 0; y < height; y += gridSize) {
for (let x = 0; x < width; x += gridSize) {
let hit = false;
for (let dy = 0; dy < gridSize; dy++) {
if (hit) {
break;
}
for (let dx = 0; dx < gridSize; dx++) {
if (hit) {
break;
}
const index = width * (y + dy) + (x + dx);
if (image.data[4 * index + 3] !== 0) {
hit = true;
}
}
}
if (hit) {
count++;
}
}
}
return count;
}
function measure () {
const s1 = 10;
const s2 = 5;
return (
Math.log(
countHitGrids(s1) /
countHitGrids(s2)
) /
Math.log(s2 / s1)
);
}
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment