-
Notifications
You must be signed in to change notification settings - Fork 194
Draw_Data: Implement new visualizers #3678
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 27 commits
cd1e01f
b49f0a6
d85785f
a315027
05bb175
58a208c
c65e301
eb9aac3
2d1c95c
c0b52bb
7787a4e
8d19cb3
495784a
ac0eddf
daeae3b
8611d1d
01c2ca3
9cfe51e
1fa3f15
0fb5d00
dc6f460
10893c5
c98f6f5
e29a4f8
fd2c700
3f8a3fa
a291d70
b8e71bd
83f8166
3872542
f556faa
0c1756e
58156b6
965dd8e
bad97f5
d64a8e6
55f27ef
585f2bd
fafea9a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,4 @@ | ||||||||||||||
| import type { JSX } from 'react'; | ||||||||||||||
| import { Stage } from 'react-konva'; | ||||||||||||||
|
|
||||||||||||||
| import { Config } from './Config'; | ||||||||||||||
| import { Data, Step } from './dataVisualizerTypes'; | ||||||||||||||
|
|
@@ -15,30 +14,172 @@ import { DataTreeNode } from './tree/TreeNode'; | |||||||||||||
| * clear is used by WorkspaceSaga to reset the visualizer after every "Run" button press | ||||||||||||||
| */ | ||||||||||||||
| export default class DataVisualizer { | ||||||||||||||
| private static counter = 1; | ||||||||||||||
|
elsxyss marked this conversation as resolved.
Outdated
|
||||||||||||||
| private static empty(step: Step[]) {} | ||||||||||||||
| private static setSteps: (step: Step[]) => void = DataVisualizer.empty; | ||||||||||||||
| public static dataRecords: Data[] = []; | ||||||||||||||
| public static isRedraw = false; | ||||||||||||||
| private static _instance = new DataVisualizer(); | ||||||||||||||
| public static treeMode = false; | ||||||||||||||
| public static BinTreeMode = false; | ||||||||||||||
| public static normalMode = true; | ||||||||||||||
| public static TreeDepth = 0; | ||||||||||||||
| public static isBinTree = false; | ||||||||||||||
| public static isGenTree = false; | ||||||||||||||
| public static nodeCount: number[] = []; | ||||||||||||||
| public static nodeColor: number[] = []; | ||||||||||||||
| public static longestNodePos: number = 0; | ||||||||||||||
| public static colorMap: WeakMap<any, number> = new WeakMap(); | ||||||||||||||
| public static posMap: WeakMap<any, number> = new WeakMap(); | ||||||||||||||
|
|
||||||||||||||
| private steps: Step[] = []; | ||||||||||||||
| private nodeLabel = 0; | ||||||||||||||
| private nodeToLabelMap: Map<DataTreeNode, number> = new Map(); | ||||||||||||||
|
|
||||||||||||||
| private constructor() {} | ||||||||||||||
|
|
||||||||||||||
| public static isBinaryTree(structures: Data[]): boolean { | ||||||||||||||
| if (!(structures instanceof Array)) { | ||||||||||||||
| return false; | ||||||||||||||
| } | ||||||||||||||
| if (structures[0] === null) { | ||||||||||||||
| return true; | ||||||||||||||
| } | ||||||||||||||
|
TYS2 marked this conversation as resolved.
|
||||||||||||||
| if (structures[0].length != 2) { | ||||||||||||||
| return false; | ||||||||||||||
| } | ||||||||||||||
| let next = structures[0]; | ||||||||||||||
| let ans = false; | ||||||||||||||
| let count = 0; | ||||||||||||||
| while (next instanceof Array) { | ||||||||||||||
| count++; | ||||||||||||||
| next = next[1]; | ||||||||||||||
| } | ||||||||||||||
| if (count == 3) { | ||||||||||||||
| ans = true; | ||||||||||||||
| } | ||||||||||||||
| return ans && this.isBinaryTree(structures[0][1]); | ||||||||||||||
|
TYS2 marked this conversation as resolved.
Outdated
|
||||||||||||||
| } | ||||||||||||||
|
TYS2 marked this conversation as resolved.
TYS2 marked this conversation as resolved.
TYS2 marked this conversation as resolved.
elsxyss marked this conversation as resolved.
|
||||||||||||||
|
|
||||||||||||||
| public static isGeneralTree(structures: Data[]): boolean { | ||||||||||||||
| if (structures == null || (structures.length == 2 && structures[1] == null)) { | ||||||||||||||
| return true; | ||||||||||||||
| } | ||||||||||||||
| if (!(structures[0] instanceof Array) && structures[1] != null) { | ||||||||||||||
| return this.isGeneralTree(structures[1]); | ||||||||||||||
| } | ||||||||||||||
| if (structures.length != 2 || (!(structures[1] instanceof Array) && structures[1] != null)) { | ||||||||||||||
| return false; | ||||||||||||||
| } | ||||||||||||||
| return this.isGeneralTree(structures[1]) && this.isGeneralTree(structures[0]); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| public static get_depth( | ||||||||||||||
| structures: Data[], | ||||||||||||||
| depth: number, | ||||||||||||||
| nodePos: number, | ||||||||||||||
| newNode: boolean | ||||||||||||||
| ): number { | ||||||||||||||
|
elsxyss marked this conversation as resolved.
Outdated
|
||||||||||||||
| if (!(structures instanceof Array)) { | ||||||||||||||
| return 0; | ||||||||||||||
| } | ||||||||||||||
| // nodeCount keeps track of the current index of nodes at each depth | ||||||||||||||
| if (this.getTreeMode()) { | ||||||||||||||
| if (this.nodeCount[depth] === undefined) { | ||||||||||||||
| this.nodeCount[depth] = 0; | ||||||||||||||
| } | ||||||||||||||
| this.posMap.set(structures, this.nodeCount[depth]); | ||||||||||||||
| if (this.nodeCount[depth] > this.longestNodePos) { | ||||||||||||||
| this.longestNodePos = this.nodeCount[depth]; | ||||||||||||||
| } | ||||||||||||||
| this.nodeCount[depth]++; | ||||||||||||||
| } | ||||||||||||||
| if (this.getBinTreeMode() || this.getTreeMode()) { | ||||||||||||||
| if (this.nodeColor[depth] === undefined) { | ||||||||||||||
| this.nodeColor[depth] = depth; | ||||||||||||||
| } | ||||||||||||||
| if (newNode) { | ||||||||||||||
| this.nodeColor[depth]++; | ||||||||||||||
| } | ||||||||||||||
| this.colorMap.set(structures, this.nodeColor[depth]); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| this.TreeDepth = Math.max(this.TreeDepth, depth); | ||||||||||||||
| this.get_depth(structures[0], depth + 1, 0, true); | ||||||||||||||
| this.get_depth(structures[1], depth, nodePos + 1, false); | ||||||||||||||
|
TYS2 marked this conversation as resolved.
Outdated
|
||||||||||||||
| return depth; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| public static init(setSteps: (step: Step[]) => void): void { | ||||||||||||||
| DataVisualizer.setSteps = setSteps; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * Set the visualization mode. This ensures only one mode is active at a time. | ||||||||||||||
| * @param mode - 'normal' for original view, 'binTree' for binary tree, 'tree' for general tree | ||||||||||||||
| */ | ||||||||||||||
| public static setMode(mode: 'normal' | 'binTree' | 'tree'): void { | ||||||||||||||
| DataVisualizer.normalMode = mode === 'normal'; | ||||||||||||||
| DataVisualizer.BinTreeMode = mode === 'binTree'; | ||||||||||||||
| DataVisualizer.treeMode = mode === 'tree'; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| // RenderBinaryTree | ||||||||||||||
| public static toggleBinTreeMode(): void { | ||||||||||||||
| DataVisualizer.BinTreeMode = !DataVisualizer.BinTreeMode; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| // RenderGeneralTree | ||||||||||||||
| public static toggleTreeMode(): void { | ||||||||||||||
| DataVisualizer.treeMode = !DataVisualizer.treeMode; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| // OriginalView | ||||||||||||||
| public static toggleNormalMode(): void { | ||||||||||||||
| DataVisualizer.normalMode = !DataVisualizer.normalMode; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| public static getBinTreeMode(): boolean { | ||||||||||||||
| return DataVisualizer.BinTreeMode; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| public static getTreeMode(): boolean { | ||||||||||||||
| return DataVisualizer.treeMode; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| public static getNormalMode(): boolean { | ||||||||||||||
| return DataVisualizer.normalMode; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| public static drawData(structures: Data[]): void { | ||||||||||||||
| if (!DataVisualizer.setSteps) { | ||||||||||||||
| throw new Error('Data visualizer not initialized'); | ||||||||||||||
| } | ||||||||||||||
| if (!DataVisualizer.isRedraw) { | ||||||||||||||
| this.dataRecords.push(structures); | ||||||||||||||
| } | ||||||||||||||
| DataVisualizer.isBinTree = this.isBinaryTree(structures); | ||||||||||||||
| DataVisualizer.isGenTree = this.isGeneralTree(structures[0]); | ||||||||||||||
| DataVisualizer.nodeCount = []; | ||||||||||||||
| DataVisualizer.nodeColor = []; | ||||||||||||||
| this.nodeColor[0] = -1; | ||||||||||||||
| this.get_depth(structures[0], 0, 0, false); | ||||||||||||||
|
TYS2 marked this conversation as resolved.
Outdated
|
||||||||||||||
|
|
||||||||||||||
| DataVisualizer._instance.addStep(structures); | ||||||||||||||
| DataVisualizer.setSteps(DataVisualizer._instance.steps); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| public static clearWithData(): void { | ||||||||||||||
| DataVisualizer.longestNodePos = 0; | ||||||||||||||
| DataVisualizer.dataRecords = []; | ||||||||||||||
| DataVisualizer.isRedraw = false; | ||||||||||||||
| DataVisualizer.clear(); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| public static clear(): void { | ||||||||||||||
| DataVisualizer._instance = new DataVisualizer(); | ||||||||||||||
| this.nodeCount = []; | ||||||||||||||
| this.TreeDepth = 0; | ||||||||||||||
| DataVisualizer.setSteps(DataVisualizer._instance.steps); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -50,36 +191,32 @@ export default class DataVisualizer { | |||||||||||||
| if (this.nodeToLabelMap.has(dataNode)) { | ||||||||||||||
| return this.nodeToLabelMap.get(dataNode) ?? 0; | ||||||||||||||
| } else { | ||||||||||||||
| console.log('*' + this.nodeLabel + ': ' + dataNode.data); | ||||||||||||||
| this.nodeToLabelMap.set(dataNode, this.nodeLabel); | ||||||||||||||
| return this.nodeLabel++; | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| private addStep(structures: Data[]) { | ||||||||||||||
| const step = structures.map(this.createDrawing); | ||||||||||||||
| const step = structures.map((xs, index) => this.createDrawing(xs, index)); | ||||||||||||||
| this.steps.push(step); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * For student use. Draws a structure by converting it into a tree object, attempts to draw on the canvas, | ||||||||||||||
| * Then shift it to the left end. | ||||||||||||||
| */ | ||||||||||||||
| private createDrawing(xs: Data): JSX.Element { | ||||||||||||||
| private createDrawing(xs: Data, key: number): JSX.Element { | ||||||||||||||
| const treeDrawer = Tree.fromSourceStructure(xs).draw(); | ||||||||||||||
|
|
||||||||||||||
| // To account for overflow to the left side due to a backward arrow | ||||||||||||||
| // const leftMargin = Config.ArrowMarginHorizontal + Config.StrokeWidth; | ||||||||||||||
| const leftMargin = Config.StrokeWidth / 2; | ||||||||||||||
|
|
||||||||||||||
| // To account for overflow to the top due to a backward arrow | ||||||||||||||
| const topMargin = Config.StrokeWidth / 2; | ||||||||||||||
|
|
||||||||||||||
| const layer = treeDrawer.draw(leftMargin, topMargin); | ||||||||||||||
| return ( | ||||||||||||||
| <Stage key={xs} width={treeDrawer.width + leftMargin} height={treeDrawer.height + topMargin}> | ||||||||||||||
| {layer} | ||||||||||||||
| </Stage> | ||||||||||||||
| ); | ||||||||||||||
| return treeDrawer.draw(leftMargin, topMargin, key); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| static redraw() { | ||||||||||||||
| this.isRedraw = true; | ||||||||||||||
| this.clear(); | ||||||||||||||
| DataVisualizer.counter = -DataVisualizer.counter; | ||||||||||||||
| return DataVisualizer.dataRecords.map(structures => this.drawData(structures)); | ||||||||||||||
|
TYS2 marked this conversation as resolved.
Outdated
TYS2 marked this conversation as resolved.
Outdated
|
||||||||||||||
| return DataVisualizer.dataRecords.map(structures => this.drawData(structures)); | |
| try { | |
| return DataVisualizer.dataRecords.map(structures => this.drawData(structures)); | |
| } finally { | |
| this.isRedraw = false; | |
| } |
Uh oh!
There was an error while loading. Please reload this page.