Py.Cafe

jackparmer/

dash-l-system-fractals

Random L-System Fractals Generator

DocsPricing
  • app.py
  • requirements.txt
app.py
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
import dash
from dash import dcc, html
from dash.dependencies import Input, Output
import plotly.express as px
import numpy as np
from PIL import Image, ImageDraw
import random

app = dash.Dash(__name__)

# Define L-system rules
rules = {
    'F': lambda: random.choice(['F-F++F-F', 'F+F-F-F+F']),
    'X': lambda: random.choice(['F-[[X]+X]+F[+FX]-X', 'F-[[X]+X]+F[+FX]+X'])
}

# Generate L-system string
def generate_l_system(axiom, rules, iterations):
    for _ in range(iterations):
        axiom = ''.join(rules.get(c, lambda: c)() for c in axiom)
    return axiom

# Draw L-system
def draw_l_system(axiom, length, angle):
    stack = []
    position = (400, 800)
    direction = -np.pi / 2
    lines = []
    
    for command in axiom:
        if command == 'F':
            new_position = (position[0] + length * np.cos(direction),
                            position[1] + length * np.sin(direction))
            lines.append((position, new_position))
            position = new_position
        elif command == '+':
            direction += angle
        elif command == '-':
            direction -= angle
        elif command == '[':
            stack.append((position, direction))
        elif command == ']':
            position, direction = stack.pop()
    
    return lines

# Convert lines to image
def lines_to_image(lines, img_size=(800, 800)):
    image = Image.new('RGB', img_size, (255, 255, 255))
    draw = ImageDraw.Draw(image)
    
    for line in lines:
        draw.line([line[0], line[1]], fill=(0, 0, 0), width=2)
    
    return image

# Layout of the app
app.layout = html.Div(style={'backgroundColor': '#ffffff', 'color': '#333333', 'fontFamily': 'Georgia, serif'}, children=[
    html.H1('Random L-System Fractals', style={'textAlign': 'center', 'fontWeight': 'bold', 'marginTop': '20px'}),
    dcc.Graph(id='l-system-graph', style={'height': '80vh'}),
    html.Div([
        html.Button('Generate L-System Fractal', id='generate-button', n_clicks=0)
    ], style={'textAlign': 'center', 'margin': '20px'}),
])

@app.callback(
    Output('l-system-graph', 'figure'),
    [Input('generate-button', 'n_clicks')]
)
def update_l_system(n_clicks):
    if n_clicks == 0:
        return dash.no_update
    
    axiom = random.choice(['F', 'X'])
    iterations = random.randint(3, 5)
    angle = random.choice([np.pi / 6, np.pi / 4, np.pi / 3])
    
    l_system_string = generate_l_system(axiom, rules, iterations)
    lines = draw_l_system(l_system_string, length=5, angle=angle)
    image = lines_to_image(lines)
    
    # Convert PIL image to numpy array
    img_array = np.array(image)
    
    # Convert numpy array to plotly figure
    fig = px.imshow(img_array)
    fig.update_layout(coloraxis_showscale=False)
    
    return fig

if __name__ == '__main__':
    app.run_server(debug=True)