Skip to content

Commit

Permalink
feat: create symmetric-binary-tree project
Browse files Browse the repository at this point in the history
  • Loading branch information
vighnesh153 committed Oct 22, 2023
1 parent ddad23a commit 21e4113
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script lang="ts">
import { onMount } from 'svelte';
import { SymmetricBinaryTreeGame, CanvasWrapperImpl } from '@vighnesh153/graphics-programming';
let canvasElement: HTMLCanvasElement;
let game: SymmetricBinaryTreeGame;
let angle = 49.66;
let chaos = false;
onMount(() => {
const canvasWrapper = new CanvasWrapperImpl(canvasElement);
game = new SymmetricBinaryTreeGame(canvasWrapper, {});
});
$: {
game?.update(angle, chaos);
}
</script>

<div class="flex justify-center items-center gap-20">
<div class="flex flex-col items-center gap-1">
<label for="chaos">Chaos?</label>
<input type="checkbox" bind:checked={chaos} id="chaos" />
</div>
<div class="flex flex-col items-center gap-1">
<label for="angle">Seed angle</label>
<input type="range" min="47" max="56.55" bind:value={angle} id="angle" step="0.01" />
</div>
</div>

<canvas class="mt-6 mx-auto w-full max-w-3xl aspect-video bg-text" bind:this={canvasElement}>
Sorry your browser doesn't support the canvas element
</canvas>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
import { graphicsProjectsMap } from '@vighnesh153/graphics-programming';
import { classes, verifyGraphicsProjectPath } from '@/utils';
import { projectNavItems } from '@/constants';
import ContentLayout from '@/layouts/ContentLayout.astro';
import ProjectRoot from '@/components/projects/graphics/symmetric-binary-tree/ProjectRoot.svelte';
const project = graphicsProjectsMap.symmetricBinaryTree;
verifyGraphicsProjectPath(project, Astro.request.url);
const title = `Vighnesh Raut | Graphics Projects - Symmetric Binary Tree`;
const description =
`A binary tree representation by its branches and how the final shape is ` +
`different for even a slight change in the angle`;
---

<ContentLayout title={title} description={description} navItems={projectNavItems} showFooter={false}>
<div class={classes(`mt-28 mb-12 max-w-xl mx-auto lg:max-w-[unset] scroll-mt-8`)}>
<h1 class="text-3xl mb-6 text-center">Symmetric Binary Tree</h1>
<ProjectRoot client:load />
</div>
</ContentLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface CanvasWrapper {
readonly width: number;
readonly height: number;
readonly canvasElement: HTMLCanvasElement;
readonly context: CanvasRenderingContext2D;

reset(): void;
getBoundingClientRect(): DOMRect;
Expand Down Expand Up @@ -35,6 +36,10 @@ export class CanvasWrapperImpl implements CanvasWrapper {
return this.#canvas;
}

get context(): CanvasRenderingContext2D {
return this.#canvasContext;
}

constructor(canvas: HTMLCanvasElement) {
const canvasContext = canvas.getContext('2d', {
willReadFrequently: true,
Expand Down
1 change: 1 addition & 0 deletions nodejs-tools/nodejs-lib/graphics-programming/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './grid-path-finder';
export * from './sierpinskis-triangle';
export * from './symmetric-binary-tree';
export * from './tower-of-hanoi';
export * from './canvas-wrapper';
export * from './collection';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { CanvasWrapper } from '@/canvas-wrapper';
import { randomColor } from './randomColor';

interface Options {
/**
* In degrees
*/
angle?: number;

thickness?: number;
color?: string;
scaleDownFactor?: number;
initialLength?: number;
lengthThreshold?: number;
}

export class SymmetricBinaryTreeGame {
#canvasWrapper: CanvasWrapper;

readonly #thickness: number;
readonly #color: string;
readonly #scaleDownFactor: number;
readonly #initialLength: number;
readonly #lengthThreshold: number;
#angle: number;
#chaos = false;

constructor(canvasWrapper: CanvasWrapper, options: Options = {}) {
this.#canvasWrapper = canvasWrapper;
const h = this.#canvasWrapper.getBoundingClientRect().height;

this.#angle = ((options.angle ?? 40) * Math.PI) / 180;
this.#thickness = options.thickness ?? 3;
this.#color = options.color ?? 'black';
this.#scaleDownFactor = options.scaleDownFactor ?? 0.7;
this.#initialLength = options.initialLength ?? h * 0.3;
this.#lengthThreshold = options.lengthThreshold ?? 3;

this.draw();
}

update(newAngle: number, chaos: boolean) {
this.#angle = newAngle;
this.#chaos = chaos;

this.draw();
}

draw() {
const w = this.#canvasWrapper.width;
const h = this.#canvasWrapper.height;
const x = w / 2;
const y = h;
this.#canvasWrapper.context.clearRect(0, 0, w, h);

this.recursivelyBranch(x, y, this.#initialLength);
}

private recursivelyBranch(x: number, y: number, length: number) {
if (length < this.#lengthThreshold) return;

const thickness = this.#thickness;
const color = this.#chaos ? randomColor() : this.#color;
const scaleDownFactor = this.#scaleDownFactor;

this.#canvasWrapper.drawLine(x, y, x, y - length, thickness, color);

[-this.wrapAngle(), this.wrapAngle()].forEach((angle) => {
this.#canvasWrapper.pushState();
this.#canvasWrapper.translate(0, -length);
this.#canvasWrapper.translate(x, y);
this.#canvasWrapper.rotate(angle);
this.#canvasWrapper.translate(-x, -y);
this.recursivelyBranch(x, y, length * scaleDownFactor);
this.#canvasWrapper.popState();
});
}

private wrapAngle(): number {
return this.#chaos ? this.#angle + Math.random() : this.#angle;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SymmetricBinaryTreeGame } from './Game';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { shuffle } from '@vighnesh153/utils';

const colors = ['red', 'green', 'black', 'blue', 'yellow', 'orange', 'purple'];

export function randomColor(): string {
return shuffle(colors)[0];
}

0 comments on commit 21e4113

Please sign in to comment.