Dernière activité 1785852451

Interactive Knowledge Graph Debugger

ZZ's Avatar ZZ a révisé ce gist 1785852451. Aller à la révision

Aucun changement

ZZ's Avatar ZZ a révisé ce gist 1785852414. Aller à la révision

Aucun changement

ZZ's Avatar ZZ a révisé ce gist 1785852365. Aller à la révision

Aucun changement

ZZ's Avatar ZZ a révisé ce gist 1785851897. Aller à la révision

1 file changed, 0 insertions, 0 deletions

Godot-Scenetree.md renommé en Scenetree.md

Fichier renommé sans modifications

ZZ's Avatar ZZ a révisé ce gist 1785851879. Aller à la révision

1 file changed, 245 insertions

network_graph3.json(fichier créé)

@@ -0,0 +1,245 @@
1 + {
2 + "@context": "https://schema.org",
3 + "@graph": [
4 + {
5 + "@id": "#alice_chen",
6 + "name": "Alice Chen\nCEO",
7 + "jobTitle": "Chief Executive Officer",
8 + "knows": [
9 + {"@id": "#bob_smith"}, {"@id": "#carol_white"}, {"@id": "#dave_brown"}
10 + ]
11 + },
12 + {
13 + "@id": "#bob_smith",
14 + "name": "Bob Smith\nCTO",
15 + "jobTitle": "Chief Technology Officer",
16 + "knows": [
17 + {"@id": "#alice_chen"}, {"@id": "#eve_johnson"}, {"@id": "#frank_miller"}, {"@id": "#george_davis"}
18 + ]
19 + },
20 + {
21 + "@id": "#carol_white",
22 + "name": "Carol White\nCFO",
23 + "jobTitle": "Chief Financial Officer",
24 + "knows": [
25 + {"@id": "#alice_chen"}, {"@id": "#dave_brown"}, {"@id": "#henry_wilson"}
26 + ]
27 + },
28 + {
29 + "@id": "#dave_brown",
30 + "name": "Dave Brown\nCOO",
31 + "jobTitle": "Chief Operating Officer",
32 + "knows": [
33 + {"@id": "#alice_chen"}, {"@id": "#henry_wilson"}, {"@id": "#frank_miller"}
34 + ]
35 + },
36 + {
37 + "@id": "#eve_johnson",
38 + "name": "Eve Johnson\nVP Engineering",
39 + "jobTitle": "Vice President of Engineering",
40 + "knows": [
41 + {"@id": "#bob_smith"}, {"@id": "#jack_anderson"}, {"@id": "#kelly_thomas"}, {"@id": "#liam_martin"}
42 + ]
43 + },
44 + {
45 + "@id": "#frank_miller",
46 + "name": "Frank Miller\nVP Sales",
47 + "jobTitle": "Vice President of Sales",
48 + "knows": [
49 + {"@id": "#dave_brown"}, {"@id": "#nora_lee"}, {"@id": "#oscar_clark"}, {"@id": "#mia_garcia"}
50 + ]
51 + },
52 + {
53 + "@id": "#george_davis",
54 + "name": "George Davis\nVP Product",
55 + "jobTitle": "Vice President of Product",
56 + "knows": [
57 + {"@id": "#bob_smith"}, {"@id": "#mia_garcia"}, {"@id": "#liam_martin"}, {"@id": "#paul_lewis"}
58 + ]
59 + },
60 + {
61 + "@id": "#henry_wilson",
62 + "name": "Henry Wilson\nHead of HR",
63 + "jobTitle": "Human Resources Director",
64 + "knows": [
65 + {"@id": "#carol_white"}, {"@id": "#quinn_adams"}, {"@id": "#rachel_king"}
66 + ]
67 + },
68 + {
69 + "@id": "#jack_anderson",
70 + "name": "Jack Anderson\nLead Dev",
71 + "jobTitle": "Senior Software Engineer",
72 + "knows": [
73 + {"@id": "#eve_johnson"}, {"@id": "#kelly_thomas"}, {"@id": "#paul_lewis"}, {"@id": "#liam_martin"}
74 + ]
75 + },
76 + {
77 + "@id": "#kelly_thomas",
78 + "name": "Kelly Thomas\nQA Lead",
79 + "jobTitle": "Quality Assurance Manager",
80 + "knows": [
81 + {"@id": "#eve_johnson"}, {"@id": "#jack_anderson"}, {"@id": "#quinn_adams"}
82 + ]
83 + },
84 + {
85 + "@id": "#liam_martin",
86 + "name": "Liam Martin\nProduct Manager",
87 + "jobTitle": "Senior Product Manager",
88 + "knows": [
89 + {"@id": "#eve_johnson"}, {"@id": "#george_davis"}, {"@id": "#jack_anderson"}, {"@id": "#mia_garcia"}
90 + ]
91 + },
92 + {
93 + "@id": "#mia_garcia",
94 + "name": "Mia Garcia\nUX Designer",
95 + "jobTitle": "Lead UX/UI Designer",
96 + "knows": [
97 + {"@id": "#frank_miller"}, {"@id": "#george_davis"}, {"@id": "#liam_martin"}
98 + ]
99 + },
100 + {
101 + "@id": "#nora_lee",
102 + "name": "Nora Lee\nSales Rep",
103 + "jobTitle": "Enterprise Sales Account Executive",
104 + "knows": [
105 + {"@id": "#frank_miller"}, {"@id": "#oscar_clark"}
106 + ]
107 + },
108 + {
109 + "@id": "#oscar_clark",
110 + "name": "Oscar Clark\nSales Rep",
111 + "jobTitle": "Account Manager",
112 + "knows": [
113 + {"@id": "#frank_miller"}, {"@id": "#nora_lee"}
114 + ]
115 + },
116 + {
117 + "@id": "#paul_lewis",
118 + "name": "Paul Lewis\nData Scientist",
119 + "jobTitle": "Staff Data Scientist",
120 + "knows": [
121 + {"@id": "#george_davis"}, {"@id": "#jack_anderson"}, {"@id": "#eve_johnson"}
122 + ]
123 + },
124 + {
125 + "@id": "#quinn_adams",
126 + "name": "Quinn Adams\nIntern",
127 + "jobTitle": "Engineering Intern",
128 + "knows": [
129 + {"@id": "#henry_wilson"}, {"@id": "#kelly_thomas"}
130 + ]
131 + },
132 + {
133 + "@id": "#rachel_king",
134 + "name": "Rachel King\nIntern",
135 + "jobTitle": "Marketing Intern",
136 + "knows": [
137 + {"@id": "#henry_wilson"}, {"@id": "#frank_miller"}
138 + ]
139 + },
140 + {
141 + "@id": "#steve_ross",
142 + "name": "Steve Ross\nBackend Dev",
143 + "jobTitle": "Software Engineer II",
144 + "knows": [
145 + {"@id": "#jack_anderson"}, {"@id": "#paul_lewis"}
146 + ]
147 + },
148 + {
149 + "@id": "#tina_turner",
150 + "name": "Tina Turner\nFrontend Dev",
151 + "jobTitle": "Software Engineer II",
152 + "knows": [
153 + {"@id": "#jack_anderson"}, {"@id": "#mia_garcia"}
154 + ]
155 + },
156 + {
157 + "@id": "#ursula_march",
158 + "name": "Ursula March\nAccountant",
159 + "jobTitle": "Senior Accountant",
160 + "knows": [
161 + {"@id": "#carol_white"}
162 + ]
163 + },
164 + {
165 + "@id": "#victor_stone",
166 + "name": "Victor Stone\nRecruiter",
167 + "jobTitle": "Technical Recruiter",
168 + "knows": [
169 + {"@id": "#henry_wilson"}, {"@id": "#eve_johnson"}
170 + ]
171 + },
172 + {
173 + "@id": "#wendy_wilson",
174 + "name": "Wendy Wilson\nOffice Mgr",
175 + "jobTitle": "Office Manager",
176 + "knows": [
177 + {"@id": "#henry_wilson"}, {"@id": "#dave_brown"}
178 + ]
179 + },
180 + {
181 + "@id": "#xavier_mercer",
182 + "name": "Xavier Mercer\nSecurity",
183 + "jobTitle": "IT Security Specialist",
184 + "knows": [
185 + {"@id": "#bob_smith"}, {"@id": "#eve_johnson"}
186 + ]
187 + },
188 + {
189 + "@id": "#yara_helmy",
190 + "name": "Yara Helmy\nCompliance",
191 + "jobTitle": "Legal & Compliance Officer",
192 + "knows": [
193 + {"@id": "#carol_white"}, {"@id": "#alice_chen"}
194 + ]
195 + },
196 + {
197 + "@id": "#zack_berry",
198 + "name": "Zack Berry\nSupport Lead",
199 + "jobTitle": "Customer Success Manager",
200 + "knows": [
201 + {"@id": "#frank_miller"}, {"@id": "#eve_johnson"}, {"@id": "#liam_martin"}
202 + ]
203 + },
204 + {
205 + "@id": "#amy_pond",
206 + "name": "Amy Pond\nSupport",
207 + "jobTitle": "Tier 2 Support Specialist",
208 + "knows": [
209 + {"@id": "#zack_berry"}, {"@id": "#jack_anderson"}
210 + ]
211 + },
212 + {
213 + "@id": "#brian_cox",
214 + "name": "Brian Cox\nDevOps",
215 + "jobTitle": "Infrastructure Engineer",
216 + "knows": [
217 + {"@id": "#eve_johnson"}, {"@id": "#xavier_mercer"}
218 + ]
219 + },
220 + {
221 + "@id": "#chloe_law",
222 + "name": "Chloe Law\nLegal",
223 + "jobTitle": "General Counsel",
224 + "knows": [
225 + {"@id": "#yara_helmy"}, {"@id": "#alice_chen"}, {"@id": "#carol_white"}
226 + ]
227 + },
228 + {
229 + "@id": "#derek_ryusaki",
230 + "name": "Derek Ryusaki\nArchitect",
231 + "jobTitle": "Solutions Architect",
232 + "knows": [
233 + {"@id": "#bob_smith"}, {"@id": "#jack_anderson"}, {"@id": "#liam_martin"}
234 + ]
235 + },
236 + {
237 + "@id": "#elaine_benese",
238 + "name": "Elaine Benese\nAdmin",
239 + "jobTitle": "Executive Assistant",
240 + "knows": [
241 + {"@id": "#alice_chen"}, {"@id": "#wendy_wilson"}
242 + ]
243 + }
244 + ]
245 + }

ZZ's Avatar ZZ a révisé ce gist 1785851842. Aller à la révision

2 files changed, 342 insertions

Godot-Scenetree.md(fichier créé)

@@ -0,0 +1,5 @@
1 + ```
2 + ┖╴GraphNode
3 + ┠╴ColorRect
4 + ┃ ┖╴Label
5 + ```

MainGraphLoader.gd(fichier créé)

@@ -0,0 +1,337 @@
1 + extends Node2D
2 +
3 + const GRAPH_FILE_PATH = "res://network_graph3.json"
4 + var graph_node_scene: PackedScene = preload("res://GraphNode.tscn")
5 + #@onready var status_label: Label = $ColorRect/Label
6 + @onready var status_label: RichTextLabel = $ColorRect/Label
7 + var _stable_frames: int = 0
8 +
9 + # ==========================================
10 + # PHYSICS TUNING & MOBILE OPTIMIZATIONS
11 + # ==========================================
12 + const REPULSION_STRENGTH: float = 45000.0
13 + const SPRING_STRENGTH: float = 2.5
14 + const REST_LENGTH: float = 330.0
15 + const DAMPING: float = 0.95
16 + const MAX_SPEED: float = 500.0 # Mobile safety cap
17 + #const PHYSICS_SLEEP_THRESHOLD: float = 1.10 # Stops sim when stable (saves mobile CPU/battery)
18 + const PHYSICS_SLEEP_THRESHOLD: float = 250.0 # Avg force per node allowed before sleeping
19 + const STABLE_FRAME_REQUIREMENT: int = 60 # Must stay stable for ~1.5s (at 60fps) to sleep
20 +
21 + # State & Caching
22 + var spawned_nodes: Dictionary = {}
23 + var node_ids: Array[String] = []
24 + var relationships: Array = []
25 + var lines: Array[Line2D] = []
26 + var node_colors: Dictionary = {}
27 +
28 + var viewport_size: Vector2i = Vector2i.ZERO
29 + var physics_active: bool = true
30 +
31 + # FPS & Metrics Tracking
32 + var _last_metrics_update: float = 0.0
33 + const METRICS_UPDATE_INTERVAL: float = 0.2 # Update UI 5x/sec to prevent GC spikes
34 + var _total_node_count: int = 0
35 + var _total_edge_count: int = 0
36 +
37 + # ✅ NEW: Store benchmark results for the dashboard
38 + var _benchmark_read_ms: float = 0.0
39 + var _benchmark_parse_ms: float = 0.0
40 +
41 + # ==========================================
42 + # INITIALIZATION
43 + # ==========================================
44 + func _ready() -> void:
45 + viewport_size = get_viewport_rect().size
46 + get_window().size_changed.connect(_on_window_resized)
47 +
48 + # FIX: Enable BBCode parsing so [color=...] tags render as actual colors
49 + status_label.bbcode_enabled = true
50 +
51 + var json_data: Dictionary = load_json_ld(GRAPH_FILE_PATH)
52 + if json_data.is_empty():
53 + push_warning("JSON-LD failed to load or is empty.")
54 + return
55 +
56 + var graph_array = json_data.get("@graph", [])
57 + var nodes_map: Dictionary = {}
58 +
59 + for item in graph_array:
60 + if item is Dictionary and item.has("@id"):
61 + nodes_map[item["@id"]] = item
62 +
63 + build_graph_network(nodes_map)
64 +
65 + _total_node_count = spawned_nodes.size()
66 + _total_edge_count = relationships.size()
67 +
68 + benchmark_json_ld()
69 + print_rich("[color=cyan]Graph Loaded: ", str(_total_node_count), " nodes, ", str(_total_edge_count), " edges.[/color]")
70 +
71 + # ==========================================
72 + # CORE FUNCTIONS
73 + # ==========================================
74 + func _process(delta: float) -> void:
75 + if physics_active:
76 + _run_physics_step(min(delta, 0.03))
77 +
78 + _update_lines()
79 + _update_metrics(delta, Time.get_ticks_msec())
80 +
81 + # ==========================================
82 + # INPUT HANDLING (Mouse + Touch)
83 + # ==========================================
84 + func _unhandled_input(event: InputEvent) -> void:
85 + var screen_pos = Vector2.ZERO
86 + var is_click_like = false
87 +
88 + if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
89 + screen_pos = event.position
90 + is_click_like = true
91 + elif event is InputEventScreenTouch:
92 + screen_pos = event.position
93 + is_click_like = true
94 +
95 + if not is_click_like:
96 + return
97 +
98 + if event.is_pressed():
99 + _attempt_grab_node(screen_pos, true)
100 + else:
101 + _attempt_release_drag()
102 +
103 + func _on_window_resized() -> void:
104 + viewport_size = get_viewport_rect().size
105 +
106 + # ==========================================
107 + # PHYSICS & UPDATE LOOPS
108 + # ==========================================
109 + func _run_physics_step(dt: float) -> void:
110 + var total_force_magnitude: float = 0.0
111 + var node_count = node_ids.size()
112 +
113 + for i in range(node_count):
114 + var id_a = node_ids[i]
115 + var node_a = spawned_nodes[id_a]
116 + if node_a.is_dragging: continue
117 +
118 + var pos_a = node_a.position
119 + var vel_a = node_a.velocity
120 +
121 + for j in range(i + 1, node_count):
122 + var id_b = node_ids[j]
123 + var node_b = spawned_nodes[id_b]
124 + if node_b.is_dragging: continue
125 +
126 + var dir = pos_a - node_b.position
127 + var dist_sq = dir.dot(dir)
128 + if dist_sq < 1.0 or dist_sq > REPULSION_STRENGTH * 2.0: continue
129 +
130 + var dist = sqrt(dist_sq)
131 + var force = (REPULSION_STRENGTH / dist_sq) * dt
132 + var f_vec = dir.normalized() * force
133 +
134 + vel_a += f_vec
135 + node_b.velocity -= f_vec if not node_b.is_dragging else Vector2.ZERO
136 +
137 + total_force_magnitude += absf(force)
138 +
139 + for rel in relationships:
140 + var node_a = rel[0]
141 + var node_b = rel[1]
142 +
143 + var dir = node_b.position - node_a.position
144 + var dist = dir.length()
145 + if dist < 0.1: continue
146 +
147 + var displacement = dist - REST_LENGTH
148 + var force_vec = dir.normalized() * (SPRING_STRENGTH * displacement) * dt
149 +
150 + if not node_a.is_dragging: node_a.velocity += force_vec
151 + if not node_b.is_dragging: node_b.velocity -= force_vec
152 +
153 + total_force_magnitude += absf(SPRING_STRENGTH * displacement)
154 +
155 + for id in node_ids:
156 + var node = spawned_nodes[id]
157 + if node.is_dragging: continue
158 +
159 + node.position += node.velocity * dt
160 + node.velocity *= DAMPING
161 +
162 + if node.velocity.length() > MAX_SPEED:
163 + node.velocity = node.velocity.normalized() * MAX_SPEED
164 +
165 + var gp = node.global_position
166 + gp.x = clampf(gp.x, 50.0, float(viewport_size.x - 200))
167 + gp.y = clampf(gp.y, 50.0, float(viewport_size.y - 100))
168 + node.global_position = gp
169 +
170 + # ==========================================
171 + # STABILIZATION & AUTO-SLEEP LOGIC
172 + # ==========================================
173 + var avg_force_per_node = total_force_magnitude / max(node_count, 1.0)
174 +
175 + if avg_force_per_node < PHYSICS_SLEEP_THRESHOLD:
176 + _stable_frames += 1
177 + else:
178 + _stable_frames = 0
179 + if _stable_frames >= STABLE_FRAME_REQUIREMENT:
180 + physics_active = false
181 +
182 +
183 +
184 +
185 + func _update_lines() -> void:
186 + for i in range(relationships.size()):
187 + var line = lines[i]
188 + var rel = relationships[i]
189 + var offset = Vector2(75.0, 25.0)
190 +
191 + line.points = [
192 + Vector2(rel[0].position + offset),
193 + Vector2(rel[1].position + offset)
194 + ]
195 +
196 + # ✅ UPDATED: Injects benchmark data into the persistent dashboard
197 + func _update_metrics(_delta: float, current_time_ms: int) -> void:
198 + if current_time_ms - _last_metrics_update < METRICS_UPDATE_INTERVAL * 1000.0:
199 + return
200 +
201 + var fps = Engine.get_frames_per_second()
202 + var frame_time = 1000.0 / fps if fps > 0 else 0.0
203 + var phys_color = "#7ee787" if physics_active else "#e74c3c"
204 + var phys_status = "ACTIVE" if physics_active else "SLEEPING"
205 +
206 + status_label.text = ("[color=#7ee787]▮ MOBILE DASHBOARD[/color]\n" +
207 + "FPS: [color=cyan]%d[/color] | Frame: [color=orange]%.2f ms[/color]\n" +
208 + "Nodes: %d | Edges: %d\n" +
209 + "Physics: [color=%s]%s[/color]\n" +
210 + "[color=#ffdd57]📊 BENCHMARK:[/color] Read: %.1f ms | Parse: %.1f ms") \
211 + % [fps, frame_time, _total_node_count, _total_edge_count,
212 + phys_color, phys_status, _benchmark_read_ms, _benchmark_parse_ms]
213 +
214 +
215 + _last_metrics_update = current_time_ms
216 +
217 + # ==========================================
218 + # DRAG SYSTEM
219 + # ==========================================
220 + func _attempt_grab_node(screen_pos: Vector2, press: bool) -> void:
221 + if not press: return
222 +
223 + var best_dist: float = 50.0
224 + var grabbed_id: String = ""
225 +
226 + for id in node_ids:
227 + var node = spawned_nodes[id]
228 + var dist = (node.position - screen_pos).length()
229 + if dist < best_dist:
230 + best_dist = dist
231 + grabbed_id = id
232 +
233 + if not grabbed_id.is_empty():
234 + spawned_nodes[grabbed_id].is_dragging = true
235 +
236 + #func _attempt_release_drag() -> void:
237 + #for node in spawned_nodes.values():
238 + #if node.is_dragging:
239 + #node.is_dragging = false
240 + #physics_active = true
241 + func _attempt_release_drag() -> void:
242 + for node in spawned_nodes.values():
243 + if node.is_dragging:
244 + node.is_dragging = false
245 + physics_active = true
246 + _stable_frames = 0 # ✅ Reset stabilization counter on interaction
247 +
248 + # ==========================================
249 + # BUILDERS & UTILITIES
250 + # ==========================================
251 + func build_graph_network(nodes_map: Dictionary) -> void:
252 + var keys = nodes_map.keys()
253 + var total_nodes = keys.size()
254 + var center = get_viewport_rect().size * 0.5
255 +
256 + for i in range(total_nodes):
257 + var node_id = keys[i]
258 + var node_data = nodes_map[node_id]
259 + var new_visual_node = graph_node_scene.instantiate()
260 +
261 + if not new_visual_node.has_method("setup"):
262 + new_visual_node.set_script(preload("res://GraphNode.gd"))
263 +
264 + add_child(new_visual_node)
265 +
266 + var angle = (TAU / total_nodes) * i
267 + var radius = 400.0
268 + new_visual_node.position = center + Vector2.from_angle(angle) * radius
269 + new_visual_node.position += Vector2(randf_range(-50, 50), randf_range(-50, 50))
270 + new_visual_node.setup(node_id, node_data.get("name", "Unknown"))
271 +
272 + spawned_nodes[node_id] = new_visual_node
273 + node_ids.append(node_id)
274 + node_colors[node_id] = _get_unique_color(node_id)
275 +
276 + for node_id in keys:
277 + var node_data = nodes_map[node_id]
278 + if not node_data.has("knows"): continue
279 +
280 + var raw_connections = node_data["knows"]
281 + var connection_list: Array = raw_connections if raw_connections is Array else [raw_connections]
282 +
283 + for conn in connection_list:
284 + if conn is Dictionary and conn.has("@id") and spawned_nodes.has(conn["@id"]):
285 + relationships.append([spawned_nodes[node_id], spawned_nodes[conn["@id"]]])
286 +
287 + var line = Line2D.new()
288 + line.width = 5.0
289 + line.z_index = -1
290 +
291 + var grad = Gradient.new()
292 + grad.set_color(0, node_colors[node_id])
293 + grad.set_color(1, node_colors[conn["@id"]])
294 + line.gradient = grad
295 +
296 + add_child(line)
297 + lines.append(line)
298 +
299 + func _get_unique_color(id_string: String) -> Color:
300 + var h = hash(id_string)
301 + return Color8(
302 + max(abs(h) % 256, 128),
303 + max((abs(h) >> 8) % 256, 128),
304 + max((abs(h) >> 16) % 256, 128)
305 + )
306 +
307 + func load_json_ld(path: String) -> Dictionary:
308 + if not FileAccess.file_exists(path):
309 + push_error("JSON-LD file not found at: " + path)
310 + return {}
311 +
312 + var file = FileAccess.open(path, FileAccess.READ)
313 + var json_string = file.get_as_text()
314 + file.close()
315 + var parsed_data = JSON.parse_string(json_string)
316 + return parsed_data if parsed_data is Dictionary else {}
317 +
318 + # ✅ UPDATED: Saves timing data to class variables instead of just printing
319 + func benchmark_json_ld() -> void:
320 + if not FileAccess.file_exists(GRAPH_FILE_PATH):
321 + return
322 +
323 + var start_time = Time.get_ticks_msec()
324 + var file = FileAccess.open(GRAPH_FILE_PATH, FileAccess.READ)
325 + var json_string = file.get_as_text()
326 + file.close()
327 + _benchmark_read_ms = float(Time.get_ticks_msec() - start_time)
328 +
329 + start_time = Time.get_ticks_msec()
330 + var _parsed = JSON.parse_string(json_string)
331 + _benchmark_parse_ms = float(Time.get_ticks_msec() - start_time)
332 +
333 + #print_rich("[color=yellow]📊 BENCHMARK: Read: %.1f ms | Parse: %.1f ms[/color]" % [_benchmark_read_ms, _benchmark_parse_ms])
334 + print_rich("[color=yellow]📊 BENCHMARK: Read: {read} ms | Parse: {parse} [/color]".format({
335 + "read": "%0.1f" % _benchmark_read_ms,
336 + "parse": "%0.1f" % _benchmark_parse_ms
337 + }))
Plus récent Plus ancien