import __main__ BaseNode = __main__.BaseNode QtGui = __main__.QtGui import numpy as np import cv2 class FitzHughNagumoNode(BaseNode): """ Simulates a FitzHugh-Nagumo neuron model. """ NODE_CATEGORY = "AI / Physics" NODE_COLOR = QtGui.QColor(100, 180, 180) def __init__(self, a=0.7, b=0.8, tau=12.5): super().__init__() self.node_title = "FHN Neuron" self.inputs = {'pain_stimulus': 'signal'} self.outputs = { 'pain_out': 'signal', 'stability_metric': 'signal' } self.a = float(a) self.b = float(b) self.tau = float(tau) self.v = 0.0 # Membrane potential ("pain") self.w = 0.0 # Recovery variable ("stability") self.dt = 0.1 # Simulation time step def step(self): # Get input current I = self.get_blended_input('pain_stimulus', 'sum') or 0.0 # Model equations (Euler integration) dv = self.v - (self.v**3 / 3) - self.w + I dw = (self.v + self.a - self.b * self.w) / self.tau self.v += dv * self.dt self.w += dw * self.dt # Clamp values to prevent explosion self.v = np.clip(self.v, -5, 5) self.w = np.clip(self.w, -5, 5) def get_output(self, port_name): if port_name == 'pain_out': return self.v if port_name == 'stability_metric': return self.w return None def get_display_image(self): w, h = 256, 128 img = np.zeros((h, w, 3), dtype=np.uint8) # Map v and w to screen v_y = int(h/2 - (self.v / 3.0) * (h/2)) w_y = int(h/2 - (self.w / 3.0) * (h/2)) cv2.circle(img, (w//2, v_y), 8, (0, 255, 255), -1) # 'v' (pain) cv2.circle(img, (w//2, w_y), 4, (255, 0, 0), -1) # 'w' (stability) cv2.line(img, (0, h//2), (w, h//2), (50,50,50), 1) cv2.putText(img, f"Pain (v): {self.v:.2f}", (5, 15), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 255), 1) cv2.putText(img, f"Stability (w): {self.w:.2f}", (5, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 0, 0), 1) return QtGui.QImage(img.data, w, h, w*3, QtGui.QImage.Format.Format_RGB888) def get_config_options(self): return [ ("a", "a", self.a, None), ("b", "b", self.b, None), ("tau", "tau", self.tau, None) ]