Skip to content

Commit c0b8299

Browse files
committed
bit-byte tree fixed
1 parent a456b0c commit c0b8299

2 files changed

Lines changed: 55 additions & 18 deletions

File tree

hknweb/templates/about/bitbyte_tree.html

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ <h4>Search</h4>
137137

138138
const baseNodeRadius = 5;
139139
const clickedNodeRadius = 7;
140+
const degreeScaleFactor = 0.9; // How much to scale nodes based on degree
140141

141142
async function renderd3() {
142143
// Current clicked node.
@@ -219,10 +220,21 @@ <h4>Search</h4>
219220
backwardChildren.get(link.target.id).add(link.source.id);
220221
});
221222

222-
// n => baseNodeRadius + forwardChildren.get(n.id).size/7.0;
223-
// ^for size dependent on number of bits, but I decided not to implement it on this iteration of the graph.
223+
// Calculate node radius based on degree (total connections)
224+
function getNodeRadius(nodeId, isClicked = false) {
225+
const forwardDegree = forwardChildren.get(nodeId).size;
226+
const backwardDegree = backwardChildren.get(nodeId).size;
227+
const totalDegree = forwardDegree + backwardDegree;
228+
229+
// Scale radius based on square root of degree for better visual balance
230+
const scaledRadius = baseNodeRadius + Math.sqrt(totalDegree) * degreeScaleFactor;
231+
232+
// If clicked, enlarge further
233+
return isClicked ? scaledRadius + 2 : scaledRadius;
234+
}
235+
224236
node.append("circle")
225-
.attr("r", baseNodeRadius)
237+
.attr("r", n => getNodeRadius(n.id))
226238
.attr("fill", n => (forwardChildren.get(n.id).size != 0) ? "#4E76AF" : "#679BE5")
227239
.attr("stroke", "#EEE")
228240
.attr("stroke-width", 1.5);
@@ -237,7 +249,7 @@ <h4>Search</h4>
237249
.attr("z-index", "1001")
238250
.attr("font-size", maxTextSize)
239251
.attr("dx", 0) // Horizontal centering
240-
.attr("dy", -baseNodeRadius) // Position label slightly above the node
252+
.attr("dy", d => -getNodeRadius(d.id) - 2) // Position label above the node based on its size
241253
.attr("text-anchor", "middle") // Anchor text in the middle for centering
242254
.text(d => d.name.split("(")[0])
243255
.style("display", "none"); // Initially hide all labels
@@ -336,7 +348,7 @@ <h4>Search</h4>
336348
// Reset nodes to the unselected state
337349
function resetNodes() {
338350
activeNode = null;
339-
node.selectAll("circle").transition().duration(300).style("opacity", 1).attr("r", baseNodeRadius);
351+
node.selectAll("circle").transition().duration(300).style("opacity", 1).attr("r", n => getNodeRadius(n.id));
340352
node.selectAll("text").transition().duration(300).style("opacity", 1);
341353
link.transition().duration(300).style("stroke-opacity", 0.6);
342354
link.attr("stroke", "#999");
@@ -371,7 +383,7 @@ <h4>Search</h4>
371383
node.selectAll("circle")
372384
.transition().duration(300)
373385
.style("opacity", otherNode => highlightableNodeIds.has(otherNode.id) || otherNode.id === d.id ? 1 : 0.1)
374-
.attr("r", otherNode => otherNode.id === d.id ? clickedNodeRadius : baseNodeRadius); // Enlarge clicked node
386+
.attr("r", otherNode => getNodeRadius(otherNode.id, otherNode.id === d.id)); // Enlarge clicked node
375387

376388
node.selectAll("text")
377389
.transition().duration(300)
@@ -463,10 +475,9 @@ <h4>Search</h4>
463475
const sourceNode = d_link.source;
464476
const targetNode = d_link.target;
465477

466-
let currentTargetRadius = baseNodeRadius;
467-
if (activeNode && targetNode.id === activeNode.id) {
468-
currentTargetRadius = clickedNodeRadius;
469-
}
478+
// Use scaled radius based on degree
479+
const isTargetClicked = activeNode && targetNode.id === activeNode.id;
480+
const currentTargetRadius = getNodeRadius(targetNode.id, isTargetClicked);
470481

471482
const dx = targetNode.x - sourceNode.x;
472483
const dy = targetNode.y - sourceNode.y;
@@ -568,19 +579,37 @@ <h4>Search</h4>
568579
searchFeedback.textContent = '';
569580
if (!searchTerm) return;
570581

571-
const foundNode = nodes.find(n => n.id.toLowerCase().includes(searchTerm)
572-
|| n.name.toLowerCase().includes(searchTerm));
582+
// Find ALL matching nodes, not just the first one
583+
const matchingNodes = nodes.filter(n => n.id.toLowerCase().includes(searchTerm)
584+
|| n.name.toLowerCase().includes(searchTerm));
585+
586+
if (matchingNodes.length > 0) {
587+
const foundNode = matchingNodes[0];
588+
589+
// Keep search highlights visible (don't clear them)
590+
// This shows all matches with gold stroke
591+
592+
// Show feedback about multiple matches
593+
if (matchingNodes.length > 1) {
594+
const matchNames = matchingNodes.slice(0, 5).map(n => n.name).join(', ');
595+
const remaining = matchingNodes.length > 5 ? ` (+${matchingNodes.length - 5} more)` : '';
596+
searchFeedback.textContent = `Found ${matchingNodes.length} matches: ${matchNames}${remaining}`;
597+
searchFeedback.style.color = '#5bc0de'; // Info color (blue)
598+
} else {
599+
searchFeedback.textContent = `Found: ${foundNode.name}`;
600+
searchFeedback.style.color = '#5cb85c'; // Success color (green)
601+
}
573602

574-
if (foundNode) {
575-
clearSearchHighlights();
603+
// Click and zoom to the first match
576604
nodeClicked({ stopPropagation: () => {} }, foundNode, false);
577605
const scale = d3.zoomTransform(svg.node()).k;
578606
const x = -foundNode.x * scale;
579607
const y = -foundNode.y * scale;
580608
const transform = d3.zoomIdentity.translate(x, y).scale(scale);
581609
svg.transition().duration(750).call(zoom.transform, transform);
582610
} else {
583-
searchFeedback.textContent = `Node starting with "${searchTerm}" not found.`;
611+
searchFeedback.textContent = `No matches found for "${searchTerm}"`;
612+
searchFeedback.style.color = '#d9534f'; // Error color (red)
584613
}
585614
}
586615

hknweb/views/bitbyte_tree.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,20 @@ def bitbyte_tree_data(request):
2525
byte_bits[line.split(",")[0]].append(line.split(",")[1])
2626

2727
for byte in all_bytes:
28-
user = User.objects.get(username__iexact=byte)
28+
try:
29+
user = User.objects.get(username__iexact=byte)
30+
name = user.first_name + " " + user.last_name
31+
candidate_semester = user.date_joined.year
32+
except User.DoesNotExist:
33+
# If user doesn't exist in database, use username as name
34+
name = byte
35+
candidate_semester = 2020 # Default year for users not in DB
36+
2937
data["nodes"].append(
3038
{
3139
"id": byte,
32-
"name": user.first_name + " " + user.last_name,
33-
"candidate_semester": user.date_joined.year,
40+
"name": name,
41+
"candidate_semester": candidate_semester,
3442
}
3543
)
3644

0 commit comments

Comments
 (0)