Skip to content

Instantly share code, notes, and snippets.

@typoman
Created October 14, 2024 12:38
Show Gist options
  • Save typoman/5f90d24b560b6685102313e822efc119 to your computer and use it in GitHub Desktop.
Save typoman/5f90d24b560b6685102313e822efc119 to your computer and use it in GitHub Desktop.
drawbot random circles on a line grid
import math
import cmath
import random
def distance(p1: tuple, p2: tuple):
"""
Calculate the Euclidean distance between two points.
Args:
p1 (tuple): The first point as a tuple of (x, y) coordinates.
p2 (tuple): The second point as a tuple of (x, y) coordinates.
Returns:
float: The Euclidean distance between the two points.
"""
return math.hypot(p1[0] - p2[0], p1[1] - p2[1])
def getAngle(p1, p2):
"""
Calculates the angle between two points in a 2D plane.
Args:
p1 (tuple): The coordinates of the first point as a tuple of two numbers.
p2 (tuple): The coordinates of the second point as a tuple of two numbers.
Returns:
float: The angle in radians between the two points.
Raises:
TypeError: If either p1 or p2 is not a tuple of two numbers.
"""
return math.atan2(p1[1] - p2[1], p1[0] - p2[0])
def gaussian_weight(distance, sigma):
"""
Calculates the Gaussian weight for a given distance and standard deviation.
The gaussian_weight function controls how quickly a value fades away as you
move further from the center. As you move away from the center, the weight
value decreases, causing the value to fade in a smooth non linear rate. The
sigma value determines the rate of this decrease: a small sigma means a
sharp, narrow fade that drops off quickly, while a large sigma means a
softer, wider gradient that fades more gradually.
Args:
distance (float): The distance from the center (0 on x axis)
sigma (float): The standard deviation of the Gaussian distribution.
Returns:
float: The Gaussian weight.
Raises:
TypeError: If distance or sigma is not a number.
# Typical usage with positive distance and sigma
>>> gaussian_weight(1.0, 2.0)
0.8824969025845955
# Zero distance
>>> gaussian_weight(0.0, 2.0)
1.0
# Zero sigma, with the added small value
>>> gaussian_weight(1.0, 0.0)
0.0
# Negative distance, which is mathematically valid
>>> gaussian_weight(-1.0, 2.0)
0.8824969025845955
# Negative sigma, which is mathematically invalid
>>> gaussian_weight(1.0, -2.0)
0.0
# Large distance
>>> gaussian_weight(10.0, 2.0)
3.726653172078671e-06
# Large sigma
>>> gaussian_weight(1.0, 10.0)
0.9950124791926823
"""
sigma = max(sigma, 1e-6) # Add a small value to sigma to avoid division by zero
return math.exp(-distance**2 / (2 * sigma**2))
def draw_vector_spiral(gridSize, spacing, centers, powers):
"""
Draw vector spirals on the grid based on multiple center points.
Args:
gridSize (int): The size of the grid.
spacing (int): The spacing between grid points.
centers (list): A list of tuples representing the center points of the spirals.
"""
for x in range(gridSize):
for y in range(gridSize):
with savedState():
current_point = (x * spacing, y * spacing)
distances = [distance(current_point, center) for center in centers]
angles = [getAngle(current_point, center) for center in centers]
# Calculate the weights using a Gaussian distribution
# min_distance = min(distances)
# sigma = min_distance * 1.5 # Adjust the sigma value to control the gaussian spread of the weights
# I remove the sigma that was based on distance and made it random taken from the arg powers.
# This makes it possible for a center to have much bigger influence on a large area, but
# if sigma is based on the closest center point (`min_distance`) then it would always be a
# smooth transition between the center points. Now some points have much bigger influence
# beyond the min_distance. Increase the `powers` value to see its effect.
weights = [gaussian_weight(d, p) for d, p in zip(distances, powers)]
# Normalize the weights
sum_weights = sum(weights)
weights = [weight / sum_weights for weight in weights]
# Convert the angles to complex numbers
complex_numbers = [cmath.exp(1j * angle) for angle in angles]
# Calculate the weighted average of the complex numbers
weighted_complex = sum(complex_number * weight for complex_number, weight in zip(complex_numbers, weights))
# Calculate the angle of the weighted average complex number
weighted_angle = math.degrees(cmath.phase(weighted_complex))
translate(current_point[0], current_point[1])
rotate(weighted_angle + 90, (spacing * 0.4, 0))
line((0, 0), (spacing * 0.8, 0))
def generate_random_tuples(min_val, max_val, tuple_length, num_tuples):
"""
Generate a list of tuples containing random integer values.
Args:
- min_val (int): Minimum value for the random integers.
- max_val (int): Maximum value for the random integers.
- tuple_length (int): Length of each tuple.
- num_tuples (int): Number of tuples to generate.
Returns:
- list[tuple[int, ...]]: A list of tuples containing random integers.
"""
return [tuple(random.randint(min_val, max_val) for _ in range(tuple_length)) for _ in range(num_tuples)]
def random_floats(n, max_value=1):
floats = [random.random() * max_value for _ in range(n)]
return floats
size(600, 600)
fill(1)
rect(0, 0, 600, 600)
strokeWidth(3.5)
stroke(0, 0, 0)
translate(0, 5)
n_points = 100
centers = generate_random_tuples(0, 600, 2, n_points)
powers = random_floats(n_points, 10)
draw_vector_spiral(60, 10, centers, powers)
# -----save-----
import os
from datetime import datetime
import subprocess
fn = os.path.split(os.path.realpath(__file__))[1] + datetime.now().strftime("%Y|%m|%d-%H%M%S")
datetime.now().strftime("%Y|%m|%d-%H%M%S")
saveImage(f"{fn}.png")
subprocess.call(['open', "%s.pdf" %fn])
@typoman
Copy link
Author

typoman commented Oct 14, 2024

sample render:
drawbot-circel-grid-multi
edit:

This code made in drawbot explores the creation of a noise with spiral effect on a grid of objects in 2D space. Initially, I attempted to make all lines point towards a single center point, then added a 90-degree rotation to create a circular pattern. To create a more complex effect, I introduced multiple center points and used a Gaussian distribution to transition between their rotations. Although the result is visually interesting, it resembles a circle rather than a spiral. My goal remains to generate a spiral effect on a grid, similar to a wind map with arrows indicating direction, but with a more pronounced spiral pattern. Further experimentation is needed to achieve this desired outcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment