Python in Natural Spaces
VersoriumX
Research by Travis Jerome Goff
Explanation: Fun Code experiments using Python!
- Grid: We use a simple 2D list (list of lists) to represent our canvas, filled initially with a background character (
.). - Logarithmic Spiral:
- The
generate_logarithmic_spiralfunction takes parametersa(initial size) andb(growth rate/tightness). These control the underlying numerical sequence. - It iterates through angles (
theta) from 0 up tomax_theta. - In each step, it calculates the radius
rusing the formular = a * exp(b * theta). This(r, theta)pair is part of our numerical sequence defining the shape. - Polar coordinates
(r, theta)are converted to Cartesian coordinates(x, y)relative to a center point. - These
(x, y)coordinates are rounded to the nearest integer grid indices and the chosensymbolis placed there. - By changing
a,b, ormax_theta(generating a similar numerical sequence), we get spirals of different sizes, tightness, or extent, as shown in the examples.
- The
- Simple Branching:
- The
generate_simple_branchfunction simulates growth segment by segment. - It maintains a current position
(x, y)andangle. - In each step (
segment), it modifies theangleslightly randomly (controlled byangle_variation_deg) and potentially adds a directional bias (like the slight upwarddrift). This generates a sequence of changing angles. - It calculates the next point based on the current
angleandlength. This generates a sequence of(x, y)positions. - It places the
symbolalong the path from the old point to the new point. - Changing parameters like
lengthorangle_variation_degcreates branches that look different (straighter, more wiggly, longer steps), demonstrating how similar underlying generation rules (sequences) lead to varied, but related, shapes.
Okay, let's simulate Python code that generates symbolic representations of naturally occurring shapes based on numerical sequences.
The core idea is to use mathematical formulas or iterative processes that generate sequences of numbers (like coordinates, angles, or lengths). These numbers are then mapped onto a 2D grid, where symbols are placed to visually represent the shape. We'll demonstrate this with two examples: a logarithmic spiral (like a Nautilus shell) and a simple branching structure (like a tree or vein).
Concept:
- Numerical Sequence Generation: Create sequences of numbers using mathematical rules often found in nature (e.g., related to exponential growth for spirals, recursive rules for branching/fractals).
- Mapping to Coordinates: Convert these numbers into (x, y) coordinates. For spirals, this often involves polar coordinates (radius, angle). For branching, it involves steps and turns.
- Discretization: Map the continuous or high-resolution (x, y) coordinates onto a discrete grid (like a character matrix).
- Symbolic Representation: Place characters (like '*', '#', 'o') at the mapped grid locations.
- Variation: Show that slightly changing the parameters of the sequence generation (creating "similar numerical sequences") leads to variations in the resulting symbolic shape.
import math
import random
# --- Grid Utilities ---
def create_grid(width, height, fill_char='.'):
"""Creates a 2D grid (list of lists) filled with fill_char."""
return [[fill_char for _ in range(width)] for _ in range(height)]
def print_grid(grid):
"""Prints the 2D grid to the console."""
for row in grid:
print(" ".join(row))
print("-" * (len(grid[0]) * 2 - 1)) # Separator
def set_symbol(grid, x, y, symbol='*'):
"""Places a symbol on the grid if coordinates are valid."""
height = len(grid)
width = len(grid[0])
# Round coordinates to nearest integer grid cell
col = round(x)
row = round(y)
# Check bounds
if 0 <= row < height and 0 <= col < width:
grid[row][col] = symbol
# --- Shape Generation ---
def generate_logarithmic_spiral(grid, center_x, center_y, a, b, max_theta, steps, symbol='@'):
"""
Generates points for a logarithmic spiral r = a * exp(b * theta)
and places symbols on the grid.
Args:
grid: The 2D list representing the grid.
center_x, center_y: The grid coordinates for the spiral's center.
a: Scale factor for the spiral.
b: Controls the tightness/rate of growth of the spiral arms.
A positive 'b' makes it grow outwards counter-clockwise.
A negative 'b' makes it grow outwards clockwise.
max_theta: The maximum angle (in radians) to generate the spiral up to.
steps: Number of points to calculate along the spiral.
symbol: The character to use for drawing the spiral.
"""
print(f"\nGenerating Spiral: a={a:.2f}, b={b:.2f}, max_theta={max_theta:.2f} ({steps} steps)")
# The numerical sequence here is the sequence of (r, theta) pairs,
# derived from the sequence of theta values.
for i in range(steps + 1):
theta = (i / steps) * max_theta
# Sequence generation: Calculate radius based on angle
r = a * math.exp(b * theta)
# Mapping to coordinates: Convert polar (r, theta) to Cartesian (x, y)
# Relative to the spiral origin (0,0)
rel_x = r * math.cos(theta)
rel_y = r * math.sin(theta) # Use negative sin for typical screen coordinates (y increases downwards)
# Discretization: Map relative (x, y) to grid coordinates
grid_x = center_x + rel_x
grid_y = center_y + rel_y
# Symbolic Representation: Place symbol on grid
set_symbol(grid, grid_x, grid_y, symbol)
def generate_simple_branch(grid, start_x, start_y, initial_angle_deg, segments, length, angle_variation_deg, symbol='#'):
"""
Generates a simple branching structure using iterative steps.
Args:
grid: The 2D list representing the grid.
start_x, start_y: Starting coordinates on the grid.
initial_angle_deg: Starting angle in degrees (0=right, 90=down, 180=left, 270=up).
segments: Number of growth segments.
length: Length of each segment.
angle_variation_deg: Max random change in angle per segment.
symbol: The character to use for drawing the branch.
"""
print(f"\nGenerating Branch: start=({start_x},{start_y}), angle={initial_angle_deg}, segs={segments}, len={length:.2f}, angle_var={angle_variation_deg:.2f}")
x, y = float(start_x), float(start_y)
angle_rad = math.radians(initial_angle_deg)
# The numerical sequence here is the sequence of (x, y, angle) states,
# generated iteratively.
set_symbol(grid, x, y, symbol) # Start point
for _ in range(segments):
# Sequence generation: Update angle randomly
angle_change = math.radians(random.uniform(-angle_variation_deg, angle_variation_deg))
# Add a slight upward drift bias (pull towards -90 degrees / 270 degrees)
drift = math.radians(-5) # Small bias upwards
angle_rad += angle_change + drift
# Mapping to coordinates: Calculate next position based on current angle and length
delta_x = length * math.cos(angle_rad)
delta_y = length * math.sin(angle_rad) # Negative sin for screen coords is handled by angle convention
# Simulate growth by drawing points along the segment (optional, smoother look)
steps_per_segment = max(1, int(length * 2)) # Draw more points for longer segments
for i in range(1, steps_per_segment + 1):
interp_x = x + delta_x * (i / steps_per_segment)
interp_y = y + delta_y * (i / steps_per_segment)
# Discretization & Symbolic Representation
set_symbol(grid, interp_x, interp_y, symbol)
# Update position for the next segment
x += delta_x
y += delta_y
# Simple boundary check (stop if it goes too far off grid)
height = len(grid)
width = len(grid[0])
if not (0 <= x < width and 0 <= y < height):
#print("Branch went off grid.")
break
# --- Simulation ---
GRID_WIDTH = 60
GRID_HEIGHT = 30
# --- Example 1: Logarithmic Spirals ---
print("--- Generating Logarithmic Spirals ---")
# Spiral 1 (Baseline)
grid1 = create_grid(GRID_WIDTH, GRID_HEIGHT)
center_x1 = GRID_WIDTH // 3
center_y1 = GRID_HEIGHT // 2
# Sequence Parameters: a=0.5, b=0.15, max_theta=4*pi
generate_logarithmic_spiral(grid1, center_x1, center_y1, a=0.5, b=0.15, max_theta=4 * math.pi, steps=200, symbol='*')
print_grid(grid1)
# Spiral 2 (Similar sequence - tighter spiral, different symbol)
grid2 = create_grid(GRID_WIDTH, GRID_HEIGHT)
center_x2 = GRID_WIDTH * 2 // 3
center_y2 = GRID_HEIGHT // 2
# Sequence Parameters: a=0.5, b=0.20 (tighter), max_theta=4*pi
generate_logarithmic_spiral(grid2, center_x2, center_y2, a=0.5, b=0.20, max_theta=4 * math.pi, steps=200, symbol='@')
print_grid(grid2)
# Spiral 3 (Similar sequence - larger scale, fewer turns)
grid3 = create_grid(GRID_WIDTH, GRID_HEIGHT)
center_x3 = GRID_WIDTH // 2
center_y3 = GRID_HEIGHT // 2
# Sequence Parameters: a=1.0 (larger), b=0.15, max_theta=3*pi (less turn)
generate_logarithmic_spiral(grid3, center_x3, center_y3, a=1.0, b=0.15, max_theta=3 * math.pi, steps=150, symbol='O')
print_grid(grid3)
# --- Example 2: Simple Branching Structures ---
print("\n--- Generating Simple Branching Structures ---")
# Branch 1 (Baseline)
grid4 = create_grid(GRID_WIDTH, GRID_HEIGHT)
start_x1 = GRID_WIDTH // 3
start_y1 = GRID_HEIGHT - 2 # Start near bottom
# Sequence Parameters: initial_angle=-90 (up), segments=100, length=0.8, angle_variation=25
generate_simple_branch(grid4, start_x1, start_y1, initial_angle_deg=-90, segments=80, length=0.8, angle_variation_deg=25, symbol='#')
print_grid(grid4)
# Branch 2 (Similar sequence - less random angle, longer segments)
grid5 = create_grid(GRID_WIDTH, GRID_HEIGHT)
start_x2 = GRID_WIDTH * 2 // 3
start_y2 = GRID_HEIGHT - 2 # Start near bottom
# Sequence Parameters: initial_angle=-90 (up), segments=60, length=1.0 (longer), angle_variation=15 (less random)
generate_simple_branch(grid5, start_x2, start_y2, initial_angle_deg=-90, segments=60, length=1.0, angle_variation_deg=15, symbol='+')
print_grid(grid5)This simulation shows how abstract numerical sequences, defined by relatively simple mathematical rules often observed in natural processes, can be translated into recognizable symbolic shapes. The "similarity" in the sequences (slight parameter changes) produces visually related but distinct output shapes.


Comments
Post a Comment