-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add tree construction in tree-path-finder visualizer
- Loading branch information
1 parent
fc0ed4d
commit b5383b7
Showing
11 changed files
with
298 additions
and
0 deletions.
There are no files selected for viewing
35 changes: 35 additions & 0 deletions
35
...nesh153-astro-svelte/src/components/projects/graphics/tree-path-finder/ProjectRoot.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<script lang="ts"> | ||
import { onMount } from 'svelte'; | ||
import { TreePathFinderGame, CanvasWrapperImpl, type CanvasWrapper } from '@vighnesh153/graphics-programming'; | ||
let canvasElement: HTMLCanvasElement; | ||
let canvasWrapper: CanvasWrapper; | ||
let game: TreePathFinderGame; | ||
function newGame() { | ||
if (game) { | ||
game.stop(); | ||
} | ||
if (canvasWrapper) { | ||
game = new TreePathFinderGame(canvasWrapper); | ||
const frames = game.start(); | ||
function showNextFrame() { | ||
if (!frames.next().done) { | ||
requestAnimationFrame(showNextFrame); | ||
} | ||
} | ||
showNextFrame(); | ||
} | ||
} | ||
onMount(() => { | ||
canvasWrapper = new CanvasWrapperImpl(canvasElement); | ||
newGame(); | ||
}); | ||
</script> | ||
|
||
<div class="flex justify-center items-center gap-10"></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> |
21 changes: 21 additions & 0 deletions
21
...s/nodejs-apps/vighnesh153-astro-svelte/src/pages/projects/graphics/tree-path-finder.astro
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
--- | ||
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/tree-path-finder/ProjectRoot.svelte'; | ||
const project = graphicsProjectsMap.treePathFinder; | ||
verifyGraphicsProjectPath(project, Astro.request.url); | ||
const title = `Vighnesh Raut | Graphics Projects - Tree Path Finder`; | ||
const description = `Find a path between two nodes in a tree`; | ||
--- | ||
|
||
<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">Tree Path Finder</h1> | ||
<ProjectRoot client:load /> | ||
</div> | ||
</ContentLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
nodejs-tools/nodejs-lib/graphics-programming/src/tree-path-finder/Edge.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { CanvasWrapper } from '@/canvas-wrapper'; | ||
import { TreeNode } from './TreeNode'; | ||
|
||
interface ConfigOptions { | ||
color?: string; | ||
thinkness?: number; | ||
} | ||
|
||
export class TreeNodeEdge { | ||
readonly #canvasWrapper: CanvasWrapper; | ||
readonly #node1: TreeNode; | ||
readonly #node2: TreeNode; | ||
|
||
readonly #color: string; | ||
readonly #thickness: number; | ||
|
||
constructor(canvasWrapper: CanvasWrapper, node1: TreeNode, node2: TreeNode, options: ConfigOptions = {}) { | ||
this.#canvasWrapper = canvasWrapper; | ||
this.#node1 = node1; | ||
this.#node2 = node2; | ||
|
||
this.#color = options.color ?? 'green'; | ||
this.#thickness = options.thinkness ?? 2; | ||
} | ||
|
||
draw() { | ||
const pos1 = this.#node1.position; | ||
const pos2 = this.#node2.position; | ||
if (pos1 === null || pos2 === null) { | ||
return; | ||
} | ||
this.#canvasWrapper.drawLine(pos1.x, pos1.y, pos2.x, pos2.y, this.#thickness, this.#color); | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
nodejs-tools/nodejs-lib/graphics-programming/src/tree-path-finder/Game.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { Queue, not } from '@vighnesh153/utils'; | ||
import { CanvasWrapper } from '@/canvas-wrapper'; | ||
import { getCanvasBgColor } from '@/getCanvasBgColor'; | ||
import { TreeNode } from './TreeNode'; | ||
import { populateTreeNodePositions } from './populateTreeNodePositions'; | ||
import { createTreeStructure } from './createTreeStructure'; | ||
import { TreeNodeEdge } from './Edge'; | ||
import { createTreeNodeEdges } from './createTreeNodeEdges'; | ||
|
||
interface GameOptions { | ||
bgColor?: string; | ||
depth?: number; | ||
} | ||
|
||
export class TreePathFinderGame { | ||
readonly #canvasWrapper: CanvasWrapper; | ||
readonly #bgColor: string; | ||
readonly #depth: number; | ||
|
||
readonly #rootNode: TreeNode; | ||
readonly #treeNodeEdges: TreeNodeEdge[]; | ||
#isRunning = false; | ||
|
||
constructor(canvasWrapper: CanvasWrapper, options: GameOptions = {}) { | ||
this.#canvasWrapper = canvasWrapper; | ||
this.#bgColor = options.bgColor ?? getCanvasBgColor(canvasWrapper); | ||
this.#depth = options.depth ?? 7; | ||
|
||
const rootNode = createTreeStructure(this.#canvasWrapper, 1, this.#depth); | ||
if (rootNode == null) { | ||
throw new Error(`Depth should be an integer >= 1`); | ||
} | ||
this.#rootNode = rootNode; | ||
populateTreeNodePositions(this.#rootNode, this.#depth, this.#canvasWrapper); | ||
this.#treeNodeEdges = createTreeNodeEdges(this.#canvasWrapper, rootNode); | ||
} | ||
|
||
*start() { | ||
this.#isRunning = true; | ||
this.draw(); | ||
yield; | ||
} | ||
|
||
stop() { | ||
this.#isRunning = false; | ||
} | ||
|
||
draw() { | ||
this.drawEdges(); | ||
this.drawTreeNodes(); | ||
} | ||
|
||
clear() { | ||
const rect = this.#canvasWrapper.getBoundingClientRect(); | ||
const canvasWidth = rect.width; | ||
const canvasHeight = rect.height; | ||
this.#canvasWrapper.drawFilledRect(0, 0, canvasWidth, canvasHeight, this.#bgColor); | ||
} | ||
|
||
private drawTreeNodes(): void { | ||
const nodes = new Queue<TreeNode>(); | ||
nodes.pushRight(this.#rootNode); | ||
|
||
while (not(nodes.isEmpty)) { | ||
const node = nodes.popLeft(); | ||
node.draw(); | ||
for (const child of node.children) { | ||
nodes.pushRight(child); | ||
} | ||
} | ||
} | ||
|
||
private drawEdges(): void { | ||
this.#treeNodeEdges.forEach((edge) => { | ||
edge.draw(); | ||
}); | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
nodejs-tools/nodejs-lib/graphics-programming/src/tree-path-finder/Position.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface Position { | ||
x: number; | ||
y: number; | ||
} |
54 changes: 54 additions & 0 deletions
54
nodejs-tools/nodejs-lib/graphics-programming/src/tree-path-finder/TreeNode.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { CanvasWrapper } from '@/canvas-wrapper'; | ||
import { Position } from './Position'; | ||
|
||
interface TreeNodeOptions { | ||
color?: string; | ||
radius?: number; | ||
} | ||
|
||
export class TreeNode { | ||
readonly #canvasWrapper: CanvasWrapper; | ||
readonly #children: TreeNode[] = []; | ||
|
||
readonly #color: string; | ||
readonly #radius: number; | ||
|
||
#position: Position | null = null; | ||
|
||
get children(): TreeNode[] { | ||
return [...this.#children]; | ||
} | ||
|
||
get position(): Position | null { | ||
if (this.#position === null) { | ||
return null; | ||
} | ||
return { ...this.#position }; | ||
} | ||
|
||
constructor(canvasWrapper: CanvasWrapper, options: TreeNodeOptions = {}) { | ||
this.#canvasWrapper = canvasWrapper; | ||
|
||
this.#color = options.color ?? 'green'; | ||
this.#radius = options.radius ?? 4; | ||
} | ||
|
||
setPosition(position: Position) { | ||
this.#position = position; | ||
} | ||
|
||
addChild(child: TreeNode): void { | ||
this.#children.push(child); | ||
} | ||
|
||
draw() { | ||
if (this.#position == null) { | ||
throw new Error(`Treenode's position is null`); | ||
} | ||
const { x, y } = this.#position; | ||
const color = this.#color; | ||
const radius = this.#radius; | ||
|
||
this.#canvasWrapper.drawFilledCircle(x, y, radius, color); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
nodejs-tools/nodejs-lib/graphics-programming/src/tree-path-finder/createTreeNodeEdges.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { CanvasWrapper } from '@/canvas-wrapper'; | ||
import { TreeNodeEdge } from './Edge'; | ||
import { TreeNode } from './TreeNode'; | ||
|
||
export function createTreeNodeEdges( | ||
canvasWrapper: CanvasWrapper, | ||
rootNode: TreeNode, | ||
result: TreeNodeEdge[] = [] | ||
): TreeNodeEdge[] { | ||
for (const child of rootNode.children) { | ||
result.push(new TreeNodeEdge(canvasWrapper, rootNode, child)); | ||
createTreeNodeEdges(canvasWrapper, child, result); | ||
} | ||
return result; | ||
} |
21 changes: 21 additions & 0 deletions
21
nodejs-tools/nodejs-lib/graphics-programming/src/tree-path-finder/createTreeStructure.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { CanvasWrapper } from '@/canvas-wrapper'; | ||
import { TreeNode } from './TreeNode'; | ||
|
||
export function createTreeStructure(canvasWrapper: CanvasWrapper, level: number, maxLevel: number): TreeNode | null { | ||
if (level > maxLevel) { | ||
return null; | ||
} | ||
|
||
const node = new TreeNode(canvasWrapper); | ||
const child1 = createTreeStructure(canvasWrapper, level + 1, maxLevel); | ||
const child2 = createTreeStructure(canvasWrapper, level + 1, maxLevel); | ||
|
||
if (child1 !== null) { | ||
node.addChild(child1); | ||
} | ||
if (child2 !== null) { | ||
node.addChild(child2); | ||
} | ||
|
||
return node; | ||
} |
1 change: 1 addition & 0 deletions
1
nodejs-tools/nodejs-lib/graphics-programming/src/tree-path-finder/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { TreePathFinderGame } from './Game'; |
34 changes: 34 additions & 0 deletions
34
...s-tools/nodejs-lib/graphics-programming/src/tree-path-finder/populateTreeNodePositions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { Queue, not } from '@vighnesh153/utils'; | ||
import { CanvasWrapper } from '@/canvas-wrapper'; | ||
import { TreeNode } from './TreeNode'; | ||
|
||
export function populateTreeNodePositions(rootNode: TreeNode, depth: number, canvasWrapper: CanvasWrapper) { | ||
const nodes = new Queue({ node: rootNode, level: 1 }); | ||
const nodesPerLevel = new Map<number, TreeNode[]>(); | ||
while (not(nodes.isEmpty)) { | ||
const { node, level } = nodes.popLeft(); | ||
|
||
nodesPerLevel.set(level, nodesPerLevel.get(level) ?? []); | ||
nodesPerLevel.get(level)!.push(node); | ||
|
||
for (const child of node.children) { | ||
nodes.pushRight({ | ||
node: child, | ||
level: level + 1, | ||
}); | ||
} | ||
} | ||
|
||
const h = canvasWrapper.height / (depth + 1); | ||
Array.from(nodesPerLevel.keys()).forEach((level) => { | ||
const nodesAtLevel = nodesPerLevel.get(level) ?? []; | ||
const w = canvasWrapper.width / (nodesAtLevel.length + 1); | ||
for (let i = 0; i < nodesAtLevel.length; i++) { | ||
const node = nodesAtLevel[i]; | ||
node.setPosition({ | ||
x: w * (i + 1), | ||
y: h * level, | ||
}); | ||
} | ||
}); | ||
} |