Skip to content

Commit

Permalink
feat: update triangle algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
iam-medvedev committed Jan 26, 2023
1 parent de0eb88 commit 4cc59f8
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 139 deletions.
16 changes: 8 additions & 8 deletions benchmark.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import b from "benny";
import { createSierpinskiTriangle } from "./src/triangle";
import b from 'benny';
import { createSierpinskiTriangle } from './src/triangle';

b.suite(
"Iterations",
'Iterations',

b.add("1", () => createSierpinskiTriangle({ size: 1000, iterations: 1 })),
b.add("2", () => createSierpinskiTriangle({ size: 1000, iterations: 2 })),
b.add("3", () => createSierpinskiTriangle({ size: 1000, iterations: 3 })),
b.add("4", () => createSierpinskiTriangle({ size: 1000, iterations: 4 })),
b.add('1', () => createSierpinskiTriangle({ size: 1000, depth: 1 })),
b.add('2', () => createSierpinskiTriangle({ size: 1000, depth: 2 })),
b.add('3', () => createSierpinskiTriangle({ size: 1000, depth: 3 })),
b.add('4', () => createSierpinskiTriangle({ size: 1000, depth: 4 })),

b.cycle(),
b.complete(),

b.save({ file: "result", folder: ".bench", format: "chart.html" })
b.save({ file: 'result', folder: '.bench', format: 'chart.html' }),
);
21 changes: 14 additions & 7 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,34 +43,41 @@

<div id="config">
<label>
<span>Iterations:</span><br />
<input type="number" min="0" max="10" value="0" />
<span>Depth:</span><br />
<input autofocus type="number" min="0" max="10" value="0" />
</label>
<br />
<br />
<span>Triangles: <span id="triangles-counter">1</span></span>
<br />
<br />
<a href="https://github.com/iam-medvedev/sierpinski-generator" target="_blank" rel="noopener noreferrer">Repo</a>
<br />
<a href="https://www.npmjs.com/package/sierpinski-generator" target="_blank" rel="noopener noreferrer">NPM</a>
</div>

<script type="module">
import { createSierpinskiTriangle } from 'https://esm.sh/sierpinski-generator';
// import { createSierpinskiTriangle } from 'https://esm.sh/sierpinski-generator';
import { createSierpinskiTriangle } from '../src/triangle';

function render(iterations) {
function render(depth) {
const result = createSierpinskiTriangle({
size: window.innerHeight,
iterations,
depth,
});

console.log(result);

const counter = document.getElementById('triangles-counter');
counter.innerHTML = Number(result.length).toLocaleString();

const svg = document.getElementById('svg');
svg.innerHTML = '';

for (const triangle of result) {
const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');

polygon.setAttribute('fill', result.indexOf(triangle) === 0 ? 'black' : 'white');

polygon.setAttribute('fill', 'black');
polygon.setAttribute('points', triangle.points.map((point) => `${point.x},${point.y}`).join(' '));
svg.appendChild(polygon);
}
Expand Down
195 changes: 71 additions & 124 deletions src/triangle.ts
Original file line number Diff line number Diff line change
@@ -1,103 +1,17 @@
import type { Box, Triangle } from './types';
import type { Triangle } from './types';

type CreateOptions = {
size: number;
iterations: number;
depth: number;
};

type LoopOptions = {
currentIteration: number;
totalIterations: number;
previousBoxes: Box[];
newPoints?: Triangle[];
};

type FindTrianglesResult = {
top: Box;
left: Box;
right: Box;
x: number;
y: number;
size: number;
depth: number;
};

/**
* Creates triangle from box
*/
function createTriangle(box: Box, reversed: boolean): Triangle {
return {
box,
points: reversed
? [
{ x: box.x, y: box.y },
{ x: box.x + box.width, y: box.y },
{ x: box.x + box.width / 2, y: box.y + box.height },
]
: [
{ x: box.x + box.width / 2, y: box.y },
{ x: box.x, y: box.y + box.height },
{ x: box.x + box.width, y: box.y + box.height },
],
};
}

/**
* Returns top/left/right box for new triangles
*/
function findTrianglesInBox(box: Box): FindTrianglesResult {
const halfWidth = box.width / 2;
const halfHeight = box.height / 2;
const quarterWidth = box.width / 4;

return {
top: {
width: halfWidth,
height: halfHeight,
x: box.x + quarterWidth,
y: box.y - halfHeight,
},
left: {
width: halfWidth,
height: halfHeight,
x: box.x - quarterWidth,
y: box.y + halfHeight,
},
right: {
width: halfWidth,
height: halfHeight,
x: box.x + quarterWidth * 3,
y: box.y + halfHeight,
},
};
}

/**
* Creating triangles in loop
*/
function loop({ currentIteration, totalIterations, previousBoxes, newPoints = [] }: LoopOptions): Triangle[] {
if (currentIteration >= totalIterations) {
return newPoints;
}

const newBoxes: Box[] = [];
for (let index = 0; index < previousBoxes.length; index++) {
const box = previousBoxes[index];

// Find top, left, right boxes
const { top: topBox, left: leftBox, right: rightBox } = findTrianglesInBox(box);

// Save this boxes for next usage
newBoxes.push(topBox, leftBox, rightBox);

// Create triangles for boxes
newPoints.push(createTriangle(topBox, true), createTriangle(leftBox, true), createTriangle(rightBox, true));
}

return loop({
currentIteration: currentIteration + 1,
totalIterations,
previousBoxes: newBoxes,
newPoints,
});
}

/**
* Creates array with Sierpiński triangle data
*
Expand All @@ -107,49 +21,82 @@ function loop({ currentIteration, totalIterations, previousBoxes, newPoints = []
* ```ts
* const result = createSierpinskiTriangle({
* size: 400,
* iterations: 4
* depth: 4
* })
* ```
*/
export function createSierpinskiTriangle(options: CreateOptions) {
export function createSierpinskiTriangle(options: CreateOptions): Triangle[] {
const triangles: Triangle[] = [];

// Create first triangle
const firstTriangle = createTriangle(
{
width: options.size,
height: options.size,
x: 0,
y: 0,
},
false,
);
triangles.push(firstTriangle);
if (typeof options !== 'object') {
throw new Error('[createSierpinskiTriangle] Please provide `options`');
}

if (options.iterations === 0) {
return triangles;
if (typeof options.depth !== 'number') {
throw new Error('[createSierpinskiTriangle] Please provide `depth`');
}

// Create second triangle
const mainBox: Box = {
width: options.size / 2,
height: options.size / 2,
x: options.size / 4,
y: options.size / 2,
};
const secondTriangle = createTriangle(mainBox, true);
triangles.push(secondTriangle);
if (typeof options.size !== 'number') {
throw new Error('[createSierpinskiTriangle] Please provide `size`');
}

if (options.iterations === 1) {
return triangles;
/**
* Creates triangles in loop
*/
function createTriangle({ x, y, size, depth }: LoopOptions) {
// Break on last iteration
if (depth === 0) {
triangles.push({
box: {
width: size,
height: size,
x,
y,
},
points: [
{ x, y },
{ x: x + size, y },
{ x: x + size / 2, y: y + size },
],
});
return;
}

const newSize = size / 2;
const nextDepth = depth - 1;

// Create top left triangle
createTriangle({
x,
y,
size: newSize,
depth: nextDepth,
});

// Create top right triangle
createTriangle({
x: x + newSize,
y,
size: newSize,
depth: nextDepth,
});

// Create bottom triangle
createTriangle({
x: x + newSize / 2,
y: y + newSize,
size: newSize,
depth: nextDepth,
});
}

// Start a cycle with triangles generation
const result = loop({
currentIteration: 1,
totalIterations: options.iterations,
previousBoxes: [mainBox],
// Create first triangle
createTriangle({
x: 0,
y: 0,
size: options.size,
depth: options.depth,
});

return triangles.concat(result);
return triangles;
}

0 comments on commit 4cc59f8

Please sign in to comment.