Skip to content

Commit 68f74df

Browse files
committed
tree generator completed
0 parents  commit 68f74df

File tree

5 files changed

+285
-0
lines changed

5 files changed

+285
-0
lines changed

app.css

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
body{
2+
margin:0;
3+
}
4+
5+
.input-container{
6+
display: flex;
7+
justify-content: flex-start;
8+
margin: 10px;
9+
}
10+
11+
.input-container > *{
12+
margin-right: 10px;
13+
}
14+
15+
button{
16+
cursor: pointer;
17+
}
18+
19+
input{
20+
padding: 10px;
21+
height: 35px;
22+
width: 200px;
23+
}
24+
25+
#canvas-container{
26+
height: 100vh;
27+
width: 100%;
28+
}
29+
30+
canvas{
31+
border: 1px solid lightgray;
32+
}

canvas.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
var canvas = document.querySelector('canvas')
2+
3+
canvas.height = document.getElementById('canvas-container').offsetHeight;
4+
canvas.width = document.getElementById('canvas-container').offsetWidth;
5+
6+
var c = canvas.getContext('2d')
7+
8+
$('#generate-tree').click(() => {
9+
var expression = $('#expression-input').val()
10+
if (typeof expression !== 'undefined' && null != expression) {
11+
expression = expression.replace(/\s+/g, '')
12+
expression = expression.toLowerCase()
13+
var postfix = infixToPostfix(expression);
14+
if (null !== postfix) {
15+
var root = constructTree(postfix)
16+
setCoordinates(root)
17+
c.clearRect(0, 0, canvas.width, canvas.height);
18+
drawTree(root, c)
19+
} else {
20+
alert('Enter valid expression')
21+
}
22+
23+
} else {
24+
alert('Enter valid expression')
25+
}
26+
})

index.html

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Canvas</title>
8+
<link rel="stylesheet" href="app.css">
9+
</head>
10+
11+
<body>
12+
<div class="input-container">
13+
<input id="expression-input" type="text" />
14+
<button id="generate-tree">Generate Tree</button>
15+
</div>
16+
<div id="canvas-container">
17+
<canvas></canvas>
18+
</div>
19+
</body>
20+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
21+
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/6.6.1/math.min.js"></script>
22+
<script src="tree.js"></script>
23+
<script src="infixToPostfix.js"></script>
24+
<script src="canvas.js"></script>
25+
26+
</html>

infixToPostfix.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
function isBracketed(expr) {
2+
var stack = []
3+
for (var i = 0; i < expr.length; i++) {
4+
if ('(' === expr[i]) {
5+
stack.push('(')
6+
} else if (')' === expr[i]) {
7+
stack.pop()
8+
if (stack.length === 0) {
9+
if (i !== expr.length - 1) {
10+
return false
11+
} else {
12+
return true
13+
}
14+
}
15+
}
16+
}
17+
return false;
18+
}
19+
20+
function isValidExpression(expr) {
21+
if (expr.length < 3) {
22+
return false;
23+
}
24+
for (var i = 0; i < expr.length; i++) {
25+
if ('abcdefghijklmnopqrstuvwxyz*()/+-'.indexOf(expr[i]) === -1) {
26+
return false;
27+
}
28+
}
29+
try {
30+
while ("(" === expr[0] && ")" === expr[expr.length - 1]) {
31+
if (isBracketed(expr)) {
32+
expr = expr.substring(1, expr.length - 1);
33+
} else break;
34+
}
35+
var res = math.parse(expr);
36+
if ((typeof res.implicit === 'undefined') || res.fn.indexOf('unary') !== -1) {
37+
return false;
38+
}
39+
return true;
40+
}
41+
catch (ex) {
42+
return false;
43+
}
44+
}
45+
46+
function infixToPostfix(expression) {
47+
if (!isValidExpression(expression)) {
48+
return null;
49+
}
50+
const prec = { "*": 3, "/": 3, "-": 2, "+": 2, "(": 1 }
51+
op_stack = []
52+
postfixList = []
53+
tokens = expression.split('')
54+
for (const token of tokens) {
55+
if ("abcdefghijklmnopqrstuvwxyz".indexOf(token) !== -1) {
56+
postfixList.push(token)
57+
} else if ("(" === token) {
58+
op_stack.push(token)
59+
} else if (")" === token) {
60+
var top_op_token = op_stack.pop()
61+
while (top_op_token !== '(') {
62+
postfixList.push(top_op_token)
63+
top_op_token = op_stack.pop()
64+
}
65+
} else {
66+
var peek_elem = op_stack.slice(-1)[0];
67+
while (op_stack.length > 0 && (prec[peek_elem] >= prec[token])) {
68+
postfixList.push(op_stack.pop())
69+
peek_elem = op_stack.slice(-1)[0];
70+
}
71+
op_stack.push(token)
72+
}
73+
}
74+
while (op_stack.length > 0) {
75+
postfixList.push(op_stack.pop())
76+
}
77+
return postfixList
78+
}

tree.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
function Node(value) {
2+
const radius = 32.5
3+
this.value = value;
4+
this.x = null;
5+
this.y = null;
6+
this.right = null;
7+
this.left = null;
8+
9+
this.isLeaf = () => this.right == null && this.left == null;
10+
11+
this.drawEdge = function (context, x, y, left_way) {
12+
context.strokeStyle = 'gray';
13+
context.beginPath()
14+
const x_y_ratio = Math.abs(this.y - y) / Math.abs(this.x - x)
15+
const w = radius * Math.sqrt(1 / (1 + Math.pow(x_y_ratio, 2)))
16+
const d = x_y_ratio * w
17+
if (left_way) {
18+
context.moveTo(this.x - w, this.y + d)
19+
context.lineTo(x + w, y - d)
20+
} else {
21+
context.moveTo(this.x + w, this.y + d)
22+
context.lineTo(x - w, y - d)
23+
}
24+
25+
context.stroke()
26+
}
27+
28+
this.draw = function (context) {
29+
context.beginPath()
30+
context.arc(this.x, this.y, radius, 0, Math.PI * 2, false)
31+
context.fillStyle = 'rgba(255, 255, 255, 0.5)'
32+
context.fill()
33+
context.strokeStyle = 'black'
34+
context.stroke()
35+
context.font = '25px Times New Roman'
36+
context.textAlign = 'center'
37+
context.textBaseline = 'middle'
38+
context.fillStyle = "black";
39+
context.fillText(this.value, this.x, this.y);
40+
}
41+
}
42+
43+
function constructTree(postfix) {
44+
const OPERATORS = ['*', '/', '-', '+']
45+
var stack = []
46+
var root = null;
47+
var current;
48+
var shift = false;
49+
for (var i = postfix.length - 1; i >= 0; i--) {
50+
if (null === root) {
51+
current = new Node(postfix[i]);
52+
root = current;
53+
} else {
54+
if (shift) {
55+
current.left = new Node(postfix[i])
56+
current = current.left
57+
shift = false
58+
} else {
59+
current.right = new Node(postfix[i])
60+
current = current.right
61+
}
62+
}
63+
if (OPERATORS.includes(postfix[i])) {
64+
stack.push(current);
65+
} else {
66+
current = stack.pop();
67+
shift = true
68+
}
69+
}
70+
return root;
71+
}
72+
73+
function getSize(root) {
74+
var size = 0
75+
function countSize(root) {
76+
if (null != root) {
77+
size++;
78+
countSize(root.left)
79+
countSize(root.right)
80+
}
81+
}
82+
countSize(root);
83+
return size;
84+
}
85+
86+
function print_coords(root) {
87+
if (null != root) {
88+
print_coords(root.left)
89+
console.log(root.value, root.x, root.y)
90+
print_coords(root.right)
91+
}
92+
}
93+
94+
function setCoordinates(root) {
95+
var i = 0
96+
const OFFSET = 50
97+
const size = getSize(root)
98+
const canvas_mid_point = window.innerWidth / 2;
99+
function setCoordinates(subt, depth) {
100+
if (null != subt) {
101+
setCoordinates(subt.left, depth + 1)
102+
subt.x = canvas_mid_point + (OFFSET * (i - size / 2))
103+
subt.y = OFFSET + (depth * 1.5 * OFFSET)
104+
i++
105+
setCoordinates(subt.right, depth + 1)
106+
}
107+
}
108+
setCoordinates(root, 0)
109+
}
110+
111+
function drawTree(root, context) {
112+
if (null != root) {
113+
root.draw(context)
114+
if (null != root.left) {
115+
root.drawEdge(context, root.left.x, root.left.y, true);
116+
}
117+
drawTree(root.left, context)
118+
if (null != root.right) {
119+
root.drawEdge(context, root.right.x, root.right.y, false)
120+
}
121+
drawTree(root.right, context)
122+
}
123+
}

0 commit comments

Comments
 (0)