Skip to content

Commit 99309ea

Browse files
committed
Add base lpy file
1 parent 9449c71 commit 99309ea

File tree

4 files changed

+338
-536
lines changed

4 files changed

+338
-536
lines changed

base_lpy.lpy

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
"""
2+
Shared L-System driver for Envy, UFO, and future trellis architectures.
3+
Configure via extern variables passed to Lsystem(..., variables).
4+
"""
5+
import os
6+
import sys
7+
import time
8+
import numpy as np
9+
import random as rd
10+
11+
# Ensure repository root is importable regardless of working directory
12+
CURRENT_DIR = os.path.dirname(__file__)
13+
if CURRENT_DIR not in sys.path:
14+
sys.path.append(CURRENT_DIR)
15+
16+
from stochastic_tree import Support, BasicWood, TreeBranch, BasicWoodConfig
17+
from helper import *
18+
from dataclasses import dataclass
19+
20+
# -----------------------------------------------------------------------------
21+
# EXTERNALLY CONFIGURABLE PATHS
22+
# -----------------------------------------------------------------------------
23+
# Override these externs via Lsystem(..., variables) to point at a specific
24+
# architecture without editing this shared file.
25+
extern(
26+
prototype_dict_path = "examples.Envy.Envy_prototypes.basicwood_prototypes",
27+
trunk_class_path = "examples.Envy.Envy_prototypes.Trunk",
28+
simulation_config_class_path = "examples.Envy.Envy_simulation.EnvySimulationConfig",
29+
point_generator_path = "examples.Envy.Envy_simulation.generate_points_v_trellis",
30+
energy_function_path = "examples.Envy.Envy_simulation.get_energy_mat",
31+
decide_function_path = "examples.Envy.Envy_simulation.decide_guide",
32+
tie_function_path = "examples.Envy.Envy_simulation.tie",
33+
prune_function_path = "examples.Envy.Envy_simulation.prune",
34+
axiom_pitch = 0.0,
35+
axiom_yaw = 0.0,
36+
)
37+
38+
# Resolve extern-provided dotted paths so the rest of the script can operate on
39+
# concrete classes/functions exactly like the Envy/UFO drivers do.
40+
basicwood_prototypes = resolve_attr(prototype_dict_path)
41+
Trunk = resolve_attr(trunk_class_path)
42+
SimulationConfigClass = resolve_attr(simulation_config_class_path)
43+
point_generator_fn = resolve_attr(point_generator_path)
44+
get_energy_mat = resolve_attr(energy_function_path)
45+
decide_guide = resolve_attr(decide_function_path)
46+
tie = resolve_attr(tie_function_path)
47+
prune = resolve_attr(prune_function_path)
48+
49+
simulation_config = SimulationConfigClass()
50+
main_trunk = Trunk(copy_from = basicwood_prototypes['trunk'])
51+
52+
# Build trellis geometry and cadence numbers up front so callbacks stay simple.
53+
trellis_support = Support(
54+
point_generator_fn(simulation_config),
55+
simulation_config.support_num_wires,
56+
simulation_config.support_spacing_wires,
57+
simulation_config.support_trunk_wire_point,
58+
)
59+
tying_interval_iterations = simulation_config.num_iteration_tie
60+
pruning_interval_iterations = simulation_config.num_iteration_prune
61+
main_trunk.tying.guide_target = trellis_support.trunk_wire
62+
63+
# Track parent/child relationships for tying/pruning decisions.
64+
branch_hierarchy = {main_trunk.name: []}
65+
enable_color_labeling = simulation_config.label
66+
67+
# L-Py callbacks delegate to the Python helpers so architectures share logic.
68+
def StartEach(lstring):
69+
"""Proxy to shared tying preparation logic."""
70+
start_each_common(lstring, branch_hierarchy, trellis_support, main_trunk)
71+
72+
73+
def EndEach(lstring):
74+
"""Proxy to shared tying/pruning orchestration logic."""
75+
return end_each_common(
76+
lstring,
77+
branch_hierarchy,
78+
trellis_support,
79+
tying_interval_iterations,
80+
pruning_interval_iterations,
81+
simulation_config,
82+
main_trunk,
83+
getIterationNb,
84+
get_energy_mat,
85+
decide_guide,
86+
tie,
87+
prune,
88+
)
89+
90+
# =============================================================================
91+
# L-SYSTEM GRAMMAR DEFINITION
92+
# =============================================================================
93+
# This section defines the formal grammar for the tree growth simulation.
94+
# The L-System uses modules (symbols) to represent different plant components
95+
# and their growth behaviors.
96+
97+
# -----------------------------------------------------------------------------
98+
# MODULE DECLARATIONS
99+
# -----------------------------------------------------------------------------
100+
# Define the vocabulary of symbols used in the L-System grammar.
101+
# Each module represents a different type of plant component or operation.
102+
module Attractors # Trellis support structure that guides branch growth
103+
module grow_object # Growing plant parts (trunk, branches, spurs) with length/thickness
104+
module bud # Dormant buds that can break to produce new branches
105+
module branch # Branch segments in the L-System string
106+
module WoodStart # Starting point of wood segments (used for tying operations)
107+
108+
# -----------------------------------------------------------------------------
109+
# GLOBAL L-SYSTEM PARAMETERS
110+
# -----------------------------------------------------------------------------
111+
# Create a growth guide curve for the initial trunk development
112+
trunk_guide_curve = create_bezier_curve(x_range = (-1, 1), y_range = (-1, 1), z_range = (0, 10), seed_value=time.time())
113+
114+
# -----------------------------------------------------------------------------
115+
# AXIOM (STARTING STRING)
116+
# -----------------------------------------------------------------------------
117+
# The initial L-System string that begins the simulation.
118+
# Starts with the trellis attractors, sets up the trunk guide curve,
119+
# and initializes the trunk growth with proper orientation.
120+
Axiom: Attractors(trellis_support)SetGuide(trunk_guide_curve, main_trunk.growth.max_length)[@GcGetPos(main_trunk.location.start)WoodStart(ParameterSet(type = main_trunk))&(axiom_pitch)/(axiom_yaw)grow_object(main_trunk)GetPos(main_trunk.location.end)@Ge]
121+
122+
# -----------------------------------------------------------------------------
123+
# DERIVATION PARAMETERS
124+
# -----------------------------------------------------------------------------
125+
# Set the maximum number of derivation steps for the L-System
126+
derivation length: simulation_config.derivation_length
127+
128+
# -----------------------------------------------------------------------------
129+
# PRODUCTION RULES
130+
# -----------------------------------------------------------------------------
131+
# Define how each module type evolves during each derivation step.
132+
# These rules control the growth, branching, and development of the tree.
133+
134+
production:
135+
136+
# GROW_OBJECT PRODUCTION RULE
137+
# Handles the growth of plant segments (trunk, branches, spurs)
138+
# Determines whether to continue growing, stop, or produce buds
139+
grow_object(plant_segment) :
140+
if plant_segment == None:
141+
# Null object - terminate this branch
142+
produce *
143+
if plant_segment.length >= plant_segment.growth.max_length:
144+
# Maximum length reached - stop growing this segment
145+
nproduce *
146+
else:
147+
# Continue growing - update segment properties
148+
nproduce SetContour(plant_segment.contour)
149+
#Update internal state of the plant segment
150+
plant_segment.grow_one()
151+
#Update physical representation
152+
if enable_color_labeling:
153+
# Add color visualization if labeling is enabled -- Can this be moved to the start of building the segment?
154+
r, g, b = plant_segment.info.color
155+
nproduce SetColor(r,g,b)
156+
#Produce internode segments (n cylinders per growth step)
157+
n_cylinders = int(plant_segment.growth.growth_length / plant_segment.growth.cylinder_length)
158+
for i in range(n_cylinders):
159+
nproduce I(plant_segment.growth.cylinder_length, plant_segment.growth.thickness, plant_segment)
160+
#Produce bud (after all cylinders in this growth step)
161+
if plant_segment.pre_bud_rule(plant_segment, simulation_config):
162+
for generated in plant_segment.post_bud_rule(plant_segment, simulation_config):
163+
nproduce new(generated[0], *generated[1])
164+
165+
if should_bud(plant_segment, simulation_config):
166+
# Age-based bud production for lateral branching
167+
nproduce bud(ParameterSet(type = plant_segment, num_buds = 0))
168+
169+
if plant_segment.post_bud_rule(plant_segment, simulation_config):
170+
for generated in plant_segment.post_bud_rule(plant_segment, simulation_config):
171+
nproduce new(generated[0], *generated[1])
172+
173+
produce grow_object(plant_segment)
174+
175+
# BUD PRODUCTION RULE
176+
# Controls bud break and branch initiation
177+
# Determines when buds activate to produce new branches
178+
bud(bud_parameters) :
179+
if bud_parameters.type.is_bud_break(bud_parameters.num_buds):
180+
# Bud break condition met - create new branch
181+
182+
new_branch = bud_parameters.type.create_branch()
183+
if new_branch == None:
184+
# Branch creation failed - terminate
185+
produce *
186+
# Register new branch in parent-child relationship tracking
187+
branch_hierarchy[new_branch.name] = []
188+
branch_hierarchy[bud_parameters.type.name].append(new_branch)
189+
# Update branch counters
190+
bud_parameters.num_buds+=1
191+
bud_parameters.type.info.num_branches+=1
192+
193+
if hasattr(new_branch, 'curve_x_range'):
194+
# Curved branches: set up custom growth guide curve
195+
curve = create_bezier_curve(x_range=new_branch.curve_x_range, y_range=new_branch.curve_y_range, z_range=new_branch.curve_z_range, seed_value=time.time())
196+
nproduce[@GcSetGuide(curve, new_branch.growth.max_length)
197+
else:
198+
# Straight branches: use default orientation
199+
nproduce [@Gc
200+
# Produce new branch with random orientation and growth object
201+
nproduce @RGetPos(new_branch.location.start)WoodStart(ParameterSet(type = new_branch))/(rd.random()*360)&(rd.random()*360)grow_object(new_branch)GetPos(new_branch.location.end)@Ge]bud(bud_parameters)
202+
203+
# -----------------------------------------------------------------------------
204+
# GEOMETRIC INTERPRETATION (HOMOMORPHISM)
205+
# -----------------------------------------------------------------------------
206+
# Map abstract L-System modules to concrete 3D geometry for rendering.
207+
# These rules define how the symbolic representation becomes visual.
208+
homomorphism:
209+
210+
# Internode segments become cylinders with length and radius
211+
I(a,r,o) --> F(a,r)
212+
# Branch segments also become cylinders
213+
S(a,r,o) --> F(a,r)
214+
215+
# -----------------------------------------------------------------------------
216+
# ATTRACTOR VISUALIZATION
217+
# -----------------------------------------------------------------------------
218+
# Additional production rules for displaying trellis attractor points
219+
production:
220+
Attractors(trellis_support):
221+
# Display enabled attractor points as visual markers
222+
points_to_display = trellis_support.attractor_grid.get_enabled_points()
223+
if len(points_to_display) > 0:
224+
produce [,(3) @g(PointSet(points_to_display,width=simulation_config.attractor_point_width))]
225+

0 commit comments

Comments
 (0)