# Near Grid / Quase grade (v2021_05) - Alexandre B A Villares
# Running on Processing Python mode, or pyp5js/pyodide in the browser

from itertools import product
from random import sample
change_b = False

def setup():
    global grid, points_a, points_b
    size(700, 700)
    grid = list(product(range(100, width, 125), repeat=2))
    points_a = sample(grid, 24)
    points_b = sample(grid, 24)
    for _ in range(12):
        Element()

def draw():
    global change_b
    background(240)
    t = brutal_sigmoid(radians(millis() / 100 % 360))
    
    points = iter(lerp_tuple(points_a, points_b, t))        
    for e in Element.elements:
        e_start, e_end = next(points), next(points)
        e.update(e_start, e_end)            
    for e in Element.elements:
        e.display()

    if t == 0 and change_b:
        points_b[:] = sample(grid, 24)
        change_b = False
    elif t == 1: 
        points_a[:] = sample(grid, 24)
        change_b = True
 
class Element:

    elements = []
    
    def __init__(self):
        self.elements.append(self)
        
    def update(self, e_start, e_end, divisions=16):
        self.points = [lerp_tuple(e_start, e_end, i / float(divisions))
                       for i in range(divisions)] + [e_end]
        
    def display(self):
        for self_p in self.points :
            for other in reversed(self.elements):
                if other == self:
                    break
                for other_p in other.points:
                    d = dist(self_p[0], self_p[1], other_p[0], other_p[1])
                    if  d <= 125:
                        stroke(0, 0, d, 155 - d)
                        line(self_p[0], self_p[1], other_p[0], other_p[1]) 
            fill(0)
            noStroke()
            circle(self_p[0], self_p[1], 4)
       
def lerp_tuple(a, b, t):   
    return tuple(lerp_tuple(ca, cb, t) if isinstance(ca, tuple)
                 else lerp(ca, cb, t)             
                 for ca, cb in zip(a, b))

def brutal_sigmoid(angle):
    m = cos(angle) * 10
    r = 1 / (1 + exp(-m))            
    if r < 0.001: return 0
    elif r > 0.999: return 1
    else: return r