Skip to content

Commit 7b34403

Browse files
committed
Code refactors & Constrain camera movement
1 parent 1d24c66 commit 7b34403

File tree

1 file changed

+129
-27
lines changed

1 file changed

+129
-27
lines changed

main.js

+129-27
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ class Node {
1212
this.level = 0;
1313

1414
this.extras = {
15-
new: true
15+
initialized: false,
16+
entered: false
1617
};
1718
}
1819
}
@@ -44,6 +45,20 @@ class BST {
4445
if (!node) return 0;
4546
return node.height;
4647
}
48+
49+
static breadthFirstTraverse(node, callback) {
50+
if (!node) return;
51+
const deque = [];
52+
53+
deque.push(node);
54+
55+
while (deque.length) {
56+
const current = deque.shift();
57+
callback(current);
58+
if (current.left) deque.push(current.left);
59+
if (current.right) deque.push(current.right);
60+
}
61+
}
4762
}
4863

4964
let tree = null;
@@ -64,7 +79,13 @@ let mouse = {
6479

6580
const camera = {
6681
x: 0,
67-
y: 0
82+
y: 0,
83+
bounds: {
84+
minX: Infinity,
85+
maxX: -Infinity,
86+
minY: Infinity,
87+
maxY: -Infinity
88+
}
6889
};
6990

7091
const dimensions = {
@@ -83,7 +104,39 @@ const resize = () => {
83104
const radius = 20;
84105
const padding = 20;
85106

86-
const renderNode = (node, parent) => {
107+
const calculateNodePosition = (node, parent) => {
108+
return parent
109+
? {
110+
x: parent.extras.x + (parent.left === node ? +1 : -1) * (padding + radius * 2),
111+
y: parent.extras.y + padding + radius * 2
112+
} : {
113+
x: 0,
114+
y: -(dimensions.height / 2) + padding + radius
115+
};
116+
};
117+
118+
const updateNode = (node, parent = null) => {
119+
if (!node) return;
120+
121+
if (!node.extras.initialized) {
122+
node.extras.multiplier = 0;
123+
const {x, y} = calculateNodePosition(node, parent);
124+
node.extras.x = x;
125+
node.extras.y = y;
126+
updateCameraBounds(tree); // TODO: Find a better way to do this
127+
node.extras.initialized = true;
128+
} else {
129+
if (!node.extras.entered) {
130+
node.extras.multiplier += 0.03;
131+
if (node.extras.multiplier > 1) node.extras.entered = true;
132+
}
133+
}
134+
135+
updateNode(node.left, node);
136+
updateNode(node.right, node);
137+
};
138+
139+
const renderNode = (node, parent = null) => {
87140
if (!node) return;
88141

89142
ctx.save();
@@ -93,41 +146,61 @@ const renderNode = (node, parent) => {
93146

94147
ctx.beginPath();
95148

96-
if (node.extras.new) {
97-
node.extras.radius = 0;
98-
if (parent) {
99-
node.extras.x = parent.extras.x + (parent.left === node ? +1 : -1) * (padding + radius * 2);
100-
node.extras.y = parent.extras.y + padding + radius * 2;
101-
} else {
102-
node.extras.x = 0;
103-
node.extras.y = -(dimensions.height / 2) + padding + radius;
104-
}
105-
node.extras.new = false;
106-
} else {
107-
if (node.extras.radius < radius) {
108-
node.extras.radius += 1;
109-
}
110-
}
111149

112150
let { x, y } = node.extras;
113151

114152
const textMeasurements = ctx.measureText(node.value);
115153

116-
ctx.arc(x, y, node.extras.radius, 0, Math.PI * 2);
154+
ctx.arc(x, y, node.extras.multiplier * radius, 0, Math.PI * 2);
117155
ctx.fillText(node.value, x - textMeasurements.width / 2, y + ctx.measureText("M").width / 2 - 2);
118156
ctx.stroke();
157+
158+
159+
if (parent) {
160+
ctx.save();
161+
162+
ctx.strokeStyle = "black";
163+
164+
const dx = (parent.extras.x - node.extras.x)
165+
dy = (parent.extras.y - node.extras.y);
166+
167+
168+
const angle = Math.atan2(dy, dx);
169+
const start = {
170+
x: parent.extras.x - radius * Math.cos(angle),
171+
y: parent.extras.y - radius * Math.sin(angle)
172+
};
173+
174+
const d = Math.sqrt(Math.pow(dx, 2), Math.pow(dy, 2)) - radius;
175+
176+
const end = {
177+
x: node.extras.x - (radius + (d - d * node.extras.multiplier)) * Math.cos(angle + Math.PI),
178+
y: node.extras.y - (radius + (d - d * node.extras.multiplier)) * Math.sin(angle + Math.PI)
179+
};
180+
181+
ctx.beginPath();
182+
ctx.moveTo(start.x, start.y);
183+
ctx.lineTo(end.x, end.y);
184+
ctx.stroke();
185+
186+
ctx.restore();
187+
}
188+
119189
ctx.restore();
120190

121191
renderNode(node.left, node);
122192
renderNode(node.right, node);
123193
};
124194

125-
const renderTree = () => {
126-
if (!tree) return;
195+
const updateTree = (tree) => {
196+
updateNode(tree);
197+
};
127198

128-
renderNode(tree, null);
199+
const renderTree = (tree) => {
200+
renderNode(tree);
129201
};
130202

203+
131204
const render = () => {
132205
const hWidth = dimensions.width / 2,
133206
hHeight = dimensions.height / 2;
@@ -137,12 +210,23 @@ const render = () => {
137210
ctx.translate(hWidth + camera.x, hHeight + camera.y);
138211
ctx.clearRect(-hWidth * 10, -hHeight * 10, dimensions.width * 10, dimensions.height * 10);
139212

140-
renderTree();
213+
updateTree(tree);
214+
renderTree(tree);
141215

142216
ctx.restore();
143217
window.requestAnimationFrame(render);
144218
};
145219

220+
221+
const updateCameraBounds = (tree) => {
222+
BST.breadthFirstTraverse(tree, (item) => {
223+
camera.bounds.minX = Math.min(item.extras.x, camera.bounds.minX);
224+
camera.bounds.minY = Math.min(item.extras.y, camera.bounds.minY);
225+
camera.bounds.maxX = Math.max(item.extras.x, camera.bounds.maxX);
226+
camera.bounds.maxY = Math.max(item.extras.y, camera.bounds.maxY);
227+
});
228+
};
229+
146230
window.addEventListener("resize", () => {
147231
resize();
148232
});
@@ -164,15 +248,33 @@ window.addEventListener("mouseup", ({ clientX, clientY }) => {
164248

165249
// This is temporary
166250
const dragDistance = Math.sqrt(Math.pow(clientX - mouse.downPos.x, 2), Math.pow(clientY - mouse.downPos.y, 2));
167-
if (dragDistance < 8) {
168-
BST.push(tree, Math.round(Math.random() * 100)).then((t) => tree = t);
251+
if (dragDistance < 2) {
252+
((async () => {
253+
tree = await BST.push(tree, Math.round(Math.random() * 100));
254+
})())
169255
}
170256
});
171257

172258
window.addEventListener("mousemove", ({ clientX, clientY }) => {
173259
if (mouse.isDown) {
174-
camera.x = (clientX - mouse.downPos.x) + mouse.oldCamera.x;
175-
camera.y = (clientY - mouse.downPos.y) + mouse.oldCamera.y;
260+
const newX = (clientX - mouse.downPos.x) + mouse.oldCamera.x,
261+
newY = (clientY - mouse.downPos.y) + mouse.oldCamera.y;
262+
263+
// I have no idea how this even works. it just does
264+
const halfWidth = dimensions.width / 2,
265+
halfHeight = dimensions.height / 2,
266+
bxa = newX + halfWidth + camera.bounds.maxX,
267+
bxb = newX - halfWidth + camera.bounds.minX,
268+
bya = newY + halfHeight + camera.bounds.maxY,
269+
byb = newY - halfHeight + camera.bounds.minY;
270+
271+
if (bxa < 0) camera.x = -halfWidth - camera.bounds.maxX;
272+
else if (bxb > 0) camera.x = halfWidth - camera.bounds.minX;
273+
else camera.x = newX;
274+
275+
if (bya < 0) camera.y = -halfHeight - camera.bounds.maxY;
276+
else if (byb > 0) camera.y = halfHeight - camera.bounds.minY;
277+
else camera.y = newY;
176278
}
177279
});
178280

0 commit comments

Comments
 (0)