flashsync / src /neural_heatmap.gleam
Dhurgh's picture
Permanently remove build artifacts from history
d0b8e8f
Raw
History Blame Contribute Delete
5.41 kB
import gleam/list
import gleam/float
import gleam/int
pub type Node {
Node(
card_id: String,
label: String,
mastery_level: Float,
brightness: Float,
x: Float,
y: Float,
connections: List(String),
)
}
pub type ConstellationMap {
ConstellationMap(
nodes: List(Node),
edges: List(#(String, String, Float)),
total_mastery: Float,
focus_areas: List(#(String, Float)),
)
}
pub fn create_node(
card_id: String,
label: String,
mastery: Float,
) -> Node {
Node(
card_id: card_id,
label: label,
mastery_level: mastery,
brightness: mastery /. 100.0,
x: 0.0,
y: 0.0,
connections: [],
)
}
pub fn connect_nodes(
source: Node,
target: Node,
strength: Float,
) -> #(Node, Node, Float) {
let updated_source =
Node(
..source,
connections: [target.card_id, ..source.connections],
)
#(updated_source, target, strength)
}
pub fn calculate_brightness(mastery_level: Float) -> Float {
// Scale brightness based on mastery
mastery_level /. 100.0
}
pub fn build_constellation(
cards: List(#(String, String, Float)),
) -> ConstellationMap {
let nodes = list.map(cards, fn(card) {
let #(id, label, mastery) = card
create_node(id, label, mastery)
})
let total_mastery = case list.length(nodes) {
0 -> 0.0
n ->
list.fold(nodes, 0.0, fn(acc, node) {
acc +. node.mastery_level
}) /. int.to_float(n)
}
let edges = build_relationships(nodes)
ConstellationMap(
nodes: position_nodes(nodes),
edges: edges,
total_mastery: total_mastery,
focus_areas: find_focus_areas(nodes),
)
}
fn build_relationships(nodes: List(Node)) -> List(#(String, String, Float)) {
// Create connections between related topics
case nodes {
[] -> []
[_] -> []
[a, b, ..rest] -> {
let connection = #(a.card_id, b.card_id, 0.5)
[connection, ..build_relationships([b, ..rest])]
}
}
}
fn position_nodes(nodes: List(Node)) -> List(Node) {
position_nodes_grid(nodes, 0, 0)
}
fn position_nodes_grid(nodes: List(Node), row: Int, col: Int) -> List(Node) {
case nodes {
[] -> []
[node, ..rest] -> {
let x = 120.0 +. int.to_float(col) *. 180.0
let y = 140.0 +. int.to_float(row) *. 160.0
let placed = Node(..node, x: x, y: y)
case col >= 4 {
True -> [placed, ..position_nodes_grid(rest, row + 1, 0)]
False -> [placed, ..position_nodes_grid(rest, row, col + 1)]
}
}
}
}
fn find_focus_areas(nodes: List(Node)) -> List(#(String, Float)) {
nodes
|> list.filter(fn(node) { node.mastery_level <. 70.0 })
|> list.map(fn(node) { #(node.label, node.mastery_level) })
|> list.sort(fn(a, b) {
float.compare(a.1, b.1)
})
}
pub fn generate_svg_constellation(map: ConstellationMap) -> String {
let svg_header =
"<svg width='1000' height='1000' class='constellation-map'>"
let edges_svg =
list.map(map.edges, fn(edge) {
let #(from_id, to_id, strength) = edge
let from_node = list.find(map.nodes, fn(n) { n.card_id == from_id })
let to_node = list.find(map.nodes, fn(n) { n.card_id == to_id })
case from_node, to_node {
Ok(f), Ok(t) -> {
let opacity = 0.2 +. strength *. 0.3
"<line x1='" <>
float.to_string(f.x) <>
"' y1='" <>
float.to_string(f.y) <>
"' x2='" <>
float.to_string(t.x) <>
"' y2='" <>
float.to_string(t.y) <>
"' stroke='#bc13fe' stroke-width='2' opacity='" <>
float.to_string(opacity) <>
"' />"
}
_, _ -> ""
}
})
|> list.fold("", fn(acc, svg) { acc <> svg })
let nodes_svg =
list.map(map.nodes, fn(node) {
let glow_radius = node.brightness *. 50.0
"<g><circle cx='" <>
float.to_string(node.x) <>
"' cy='" <>
float.to_string(node.y) <>
"' r='" <>
float.to_string(glow_radius) <>
"' fill='#00f2ff' opacity='" <>
float.to_string(node.brightness *. 0.3) <>
"' filter='url(#glow)' /><circle cx='" <>
float.to_string(node.x) <>
"' cy='" <>
float.to_string(node.y) <>
"' r='20' fill='#00f2ff' /><text x='" <>
float.to_string(node.x) <>
"' y='" <>
float.to_string(node.y +. 5.0) <>
"' font-size='12' text-anchor='middle' fill='white'>" <>
node.label <>
"</text></g>"
})
|> list.fold("", fn(acc, svg) { acc <> svg })
svg_header <> edges_svg <> nodes_svg <> "</svg>"
}
pub fn calculate_heatmap_analytics(map: ConstellationMap) -> String {
let weak_areas = list.length(map.focus_areas)
let strong_areas = list.length(map.nodes) - weak_areas
"Total Nodes: " <>
int.to_string(list.length(map.nodes)) <>
" | Strong: " <>
int.to_string(strong_areas) <>
" | Needs Work: " <>
int.to_string(weak_areas) <>
" | Overall Mastery: " <>
float.to_string(map.total_mastery) <>
"%"
}
pub fn identify_knowledge_gaps(map: ConstellationMap) -> List(String) {
list.map(map.focus_areas, fn(area) {
let #(label, mastery) = area
"📍 " <> label <> " (" <> float.to_string(mastery) <> "% mastery)"
})
}
pub fn recommend_next_topics(
map: ConstellationMap,
) -> List(String) {
let poorly_mastered = list.filter(map.nodes, fn(node) {
node.mastery_level <. 50.0
})
list.map(poorly_mastered, fn(node) {
node.label
})
}