File size: 4,109 Bytes
3bb804c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
"""

Bio-Tools: Utilities for the Artificial Life Ecosystem

------------------------------------------------------

1. Genomic Noise: Generates organic 1/f noise vectors for DNA seeding.

2. Vector Blender: Manually mix two DNA strands.

"""

import numpy as np

# --- STRICT COMPATIBILITY IMPORTS ---
import __main__
try:
    BaseNode = __main__.BaseNode
    QtGui = __main__.QtGui
except AttributeError:
    from PyQt6 import QtGui
    class BaseNode:
        def get_blended_input(self, name, mode): return None

class GenomicNoiseNode(BaseNode):
    """

    Generates 'Pink Noise' (1/f) vectors.

    Biological systems are rarely random; they are correlated.

    This creates DNA that looks more 'organic' and less like static.

    """
    NODE_CATEGORY = "Artificial Life"
    NODE_COLOR = QtGui.QColor(100, 150, 100) # Sage Green

    def __init__(self):
        super().__init__()
        self.node_title = "Genomic Noise"
        
        self.inputs = {
            'volatility': 'signal', # How fast the noise changes
            'roughness': 'signal'   # High frequency content
        }
        
        self.outputs = {
            'dna_spectrum': 'spectrum', # Vector output
            'value': 'signal'           # Single value
        }
        
        self.length = 128
        self.state = np.zeros(self.length)
        self.smooth_state = np.zeros(self.length)
        
        # Buffers
        self.out_spectrum = np.zeros(self.length)
        self.out_value = 0.0

    def step(self):
        vol = self.get_blended_input('volatility', 'mean')
        rough = self.get_blended_input('roughness', 'mean')
        
        if vol is None: vol = 0.1
        if rough is None: rough = 0.5
        
        # Generate new target noise
        target = np.random.randn(self.length) * rough
        
        # Smoothly interpolate (Brownian motion-ish)
        self.state = self.state * (1.0 - vol) + target * vol
        
        # Apply smoothing for "structure"
        # Simple moving average to simulate correlations
        kernel_size = 3
        self.smooth_state = np.convolve(self.state, np.ones(kernel_size)/kernel_size, mode='same')
        
        # Normalize to 0..1 range typically expected by DNA, 
        # but centered around 0 is also fine for phases.
        # Let's keep it raw but bounded slightly
        self.out_spectrum = np.clip(self.smooth_state, -2.0, 2.0)
        self.out_value = float(np.mean(np.abs(self.out_spectrum)))

    def get_output(self, name):
        if name == 'dna_spectrum': return self.out_spectrum
        if name == 'value': return self.out_value
        return None


class VectorMathNode(BaseNode):
    """

    Simple math for DNA vectors.

    """
    NODE_CATEGORY = "Artificial Life"
    NODE_COLOR = QtGui.QColor(100, 100, 150)

    def __init__(self):
        super().__init__()
        self.node_title = "Vector Math"
        
        self.inputs = {
            'vec_a': 'spectrum',
            'vec_b': 'spectrum',
            'op_mode': 'signal' # 0=Add, 1=Sub, 2=Mult
        }
        self.outputs = {
            'result': 'spectrum'
        }
        self.out_result = np.zeros(128)

    def step(self):
        a = self.get_blended_input('vec_a', 'mean')
        b = self.get_blended_input('vec_b', 'mean')
        mode = self.get_blended_input('op_mode', 'mean')
        
        if a is None: a = np.zeros(128)
        if b is None: b = np.zeros(128)
        
        # Resize to match
        target_len = max(len(a), len(b))
        if len(a) < target_len: a = np.resize(a, target_len)
        if len(b) < target_len: b = np.resize(b, target_len)
        
        if mode is None: mode = 0
        
        if mode < 0.5: # ADD
            res = a + b
        elif mode < 1.5: # SUB
            res = a - b
        else: # MULT
            res = a * b
            
        self.out_result = res

    def get_output(self, name):
        if name == 'result': return self.out_result
        return None