Skip to content

Commit 1818d4d

Browse files
committed
Add example Envy
1 parent a2070f0 commit 1818d4d

File tree

10 files changed

+731
-2
lines changed

10 files changed

+731
-2
lines changed

examples/Envy/Envy.lpy

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
"""
2+
Tying, Pruning and labelling Envy architecture trees
3+
"""
4+
import sys
5+
sys.path.append('../../')
6+
from stochastic_tree import Support, BasicWood, TreeBranch, BasicWoodConfig
7+
import time
8+
from helper import *
9+
import numpy as np
10+
import random as rd
11+
from examples.Envy.Envy_prototypes import basicwood_prototypes, Trunk
12+
from examples.Envy.Envy_simulation import EnvySimulationConfig, generate_points_v_trellis, get_energy_mat, decide_guide, tie, prune
13+
from dataclasses import dataclass
14+
import time
15+
16+
#init
17+
main_trunk = Trunk(copy_from = basicwood_prototypes['trunk'])
18+
19+
# Create simulation configuration
20+
simulation_config = EnvySimulationConfig()
21+
22+
23+
24+
trellis_support = Support(generate_points_v_trellis(simulation_config), simulation_config.support_num_wires, simulation_config.support_spacing_wires, simulation_config.support_trunk_wire_point)
25+
tying_interval_iterations = simulation_config.num_iteration_tie
26+
pruning_interval_iterations = simulation_config.num_iteration_prune
27+
main_trunk.tying.guide_target = trellis_support.trunk_wire
28+
29+
def StartEach(lstring):
30+
"""
31+
Initialize tying updates for trunk and branches at the start of each iteration.
32+
33+
This function is called at the beginning of each L-System derivation iteration
34+
to prepare the tree structure for potential tying operations. It ensures that
35+
both the trunk and all child branches are ready to participate in the tying
36+
process by updating their tying status.
37+
38+
The function performs two main tasks:
39+
1. Updates the trunk's tying status if it has a wire target and hasn't been updated
40+
2. Updates all child branches' tying status if they haven't been updated
41+
42+
Args:
43+
lstring: The current L-System string (not used in this function but required
44+
by L-Py's callback interface)
45+
46+
Returns:
47+
None: Modifies global tree structures in-place
48+
49+
Note:
50+
This function relies on global variables: branch_hierarchy, trellis_support, main_trunk.
51+
It should be called automatically by L-Py at the start of each iteration.
52+
"""
53+
global branch_hierarchy, trellis_support, main_trunk
54+
55+
# Update trunk tying status if it has a wire target and needs updating
56+
if trellis_support.trunk_wire and not main_trunk.tying.tie_updated:
57+
main_trunk.tie_update()
58+
59+
# Update tying status for all child branches that need it
60+
for branch in branch_hierarchy[main_trunk.name]:
61+
if not branch.tying.tie_updated:
62+
branch.tie_update()
63+
64+
65+
def EndEach(lstring):
66+
"""
67+
Perform tying and pruning operations at the end of each L-System iteration.
68+
69+
This function is the main orchestration point for the tree training simulation.
70+
It executes tying operations (assigning branches to trellis wires) and pruning
71+
operations based on configurable iteration intervals. The tying process uses
72+
energy-based optimization to find optimal branch-to-wire assignments.
73+
74+
The function performs the following sequence when tying is scheduled:
75+
1. Updates the trunk's guide target (ties trunk first)
76+
2. Calculates energy matrix for all branch-wire combinations
77+
3. Uses greedy optimization to assign branches to lowest-energy wires
78+
4. Updates guide targets for all assigned branches
79+
5. Performs actual tying operations in the L-System string
80+
6. Prunes old branches if pruning iteration is reached
81+
82+
Args:
83+
lstring: The current L-System string containing modules and their parameters
84+
85+
Returns:
86+
The modified L-System string after tying and pruning operations
87+
88+
Note:
89+
This function relies on global variables and is called automatically by L-Py
90+
at the end of each derivation iteration. Tying occurs every tying_interval_iterations
91+
iterations, while pruning occurs every pruning_interval_iterations iterations.
92+
"""
93+
global branch_hierarchy, trellis_support, tying_interval_iterations
94+
95+
current_iteration = getIterationNb() + 1 #GetIterationNb is an L-Py function
96+
97+
# Check if this is a tying iteration
98+
if current_iteration % tying_interval_iterations == 0:
99+
# Tie trunk to its target wire first (one iteration before branches)
100+
if trellis_support.trunk_wire:
101+
main_trunk.update_guide(main_trunk.tying.guide_target)
102+
103+
# Calculate energy costs for optimal branch-to-wire assignments
104+
branches = branch_hierarchy[main_trunk.name]
105+
energy_matrix = get_energy_mat(branches, trellis_support, simulation_config)
106+
107+
# Perform greedy optimization to assign branches to wires
108+
decide_guide(energy_matrix, branches, trellis_support, simulation_config)
109+
110+
# Update guide targets for all assigned branches
111+
for branch in branches:
112+
branch.update_guide(branch.tying.guide_target)
113+
114+
# Execute tying operations in the L-System string
115+
while tie(lstring, simulation_config):
116+
pass # Continue until no more tying operations are possible
117+
118+
# Check if this is also a pruning iteration
119+
if current_iteration % pruning_interval_iterations == 0:
120+
# Prune branches until no more can be pruned
121+
while prune(lstring, simulation_config):
122+
pass
123+
124+
return lstring
125+
126+
127+
128+
branch_hierarchy = {}
129+
branch_hierarchy[main_trunk.name] = []
130+
enable_color_labeling = simulation_config.label
131+
# =============================================================================
132+
# L-SYSTEM GRAMMAR DEFINITION
133+
# =============================================================================
134+
# This section defines the formal grammar for the Envy tree growth simulation.
135+
# The L-System uses modules (symbols) to represent different plant components
136+
# and their growth behaviors.
137+
138+
# -----------------------------------------------------------------------------
139+
# MODULE DECLARATIONS
140+
# -----------------------------------------------------------------------------
141+
# Define the vocabulary of symbols used in the L-System grammar.
142+
# Each module represents a different type of plant component or operation.
143+
144+
module Attractors # Trellis support structure that guides branch growth
145+
module grow_object # Growing plant parts (trunk, branches, spurs) with length/thickness
146+
module bud # Dormant buds that can break to produce new branches
147+
module branch # Branch segments in the L-System string
148+
module WoodStart # Starting point of wood segments (used for tying operations)
149+
150+
# -----------------------------------------------------------------------------
151+
# GLOBAL L-SYSTEM PARAMETERS
152+
# -----------------------------------------------------------------------------
153+
# Create a growth guide curve for the initial trunk development
154+
trunk_guide_curve = create_bezier_curve(x_range = (-1, 1), y_range = (-1, 1), z_range = (0, 10), seed_value=time.time())
155+
156+
# -----------------------------------------------------------------------------
157+
# AXIOM (STARTING STRING)
158+
# -----------------------------------------------------------------------------
159+
# The initial L-System string that begins the simulation.
160+
# Starts with the trellis attractors, sets up the trunk guide curve,
161+
# and initializes the trunk growth with proper orientation.
162+
Axiom: Attractors(trellis_support)SetGuide(trunk_guide_curve, main_trunk.growth.max_length)[@GcGetPos(main_trunk.location.start)WoodStart(ParameterSet(type = main_trunk))grow_object(main_trunk)GetPos(main_trunk.location.end)@Ge]
163+
164+
# -----------------------------------------------------------------------------
165+
# DERIVATION PARAMETERS
166+
# -----------------------------------------------------------------------------
167+
# Set the maximum number of derivation steps for the L-System
168+
derivation length: simulation_config.derivation_length
169+
170+
# -----------------------------------------------------------------------------
171+
# PRODUCTION RULES
172+
# -----------------------------------------------------------------------------
173+
# Define how each module type evolves during each derivation step.
174+
# These rules control the growth, branching, and development of the tree.
175+
176+
production:
177+
178+
# GROW_OBJECT PRODUCTION RULE
179+
# Handles the growth of plant segments (trunk, branches, spurs)
180+
# Determines whether to continue growing, stop, or produce buds
181+
grow_object(plant_segment) :
182+
if plant_segment == None:
183+
# Null object - terminate this branch
184+
produce *
185+
if plant_segment.length >= plant_segment.growth.max_length:
186+
# Maximum length reached - stop growing this segment
187+
nproduce *
188+
else:
189+
# Continue growing - update segment properties
190+
nproduce SetContour(plant_segment.contour)
191+
#Update internal state of the plant segment
192+
plant_segment.grow_one()
193+
#Update physical representation
194+
if enable_color_labeling:
195+
# Add color visualization if labeling is enabled -- Can this be moved to the start of building the segment?
196+
r, g, b = plant_segment.info.color
197+
nproduce SetColor(r,g,b)
198+
#Produce internode segment
199+
nproduce I(plant_segment.growth.growth_length, plant_segment.growth.thickness, plant_segment)
200+
#Produce bud
201+
if plant_segment.pre_bud_rule(plant_segment, simulation_config):
202+
for module in plant_segment.post_bud_rule(plant_segment, simulation_config):
203+
nproduce new(module[0], *module[1])
204+
205+
if should_bud(plant_segment, simulation_config):
206+
# Age-based bud production for lateral branching
207+
nproduce bud(ParameterSet(type = plant_segment, num_buds = 0))
208+
209+
if plant_segment.post_bud_rule(plant_segment, simulation_config):
210+
for module in plant_segment.post_bud_rule(plant_segment, simulation_config):
211+
nproduce new(module[0], *module[1])
212+
213+
produce grow_object(plant_segment)
214+
215+
# BUD PRODUCTION RULE
216+
# Controls bud break and branch initiation
217+
# Determines when buds activate to produce new branches
218+
bud(bud_parameters) :
219+
if bud_parameters.type.is_bud_break(bud_parameters.num_buds):
220+
# Bud break condition met - create new branch
221+
222+
new_branch = bud_parameters.type.create_branch()
223+
if new_branch == None:
224+
# Branch creation failed - terminate
225+
produce *
226+
# Register new branch in parent-child relationship tracking
227+
branch_hierarchy[new_branch.name] = []
228+
branch_hierarchy[bud_parameters.type.name].append(new_branch)
229+
# Update branch counters
230+
bud_parameters.num_buds+=1
231+
bud_parameters.type.info.num_branches+=1
232+
233+
if hasattr(new_branch, 'curve_x_range'):
234+
# Curved branches: set up custom growth guide curve
235+
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())
236+
nproduce[@GcSetGuide(curve, new_branch.growth.max_length)
237+
else:
238+
# Straight branches: use default orientation
239+
nproduce [@Gc
240+
# Produce new branch with random orientation and growth object
241+
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)
242+
243+
244+
# -----------------------------------------------------------------------------
245+
# GEOMETRIC INTERPRETATION (HOMOMORPHISM)
246+
# -----------------------------------------------------------------------------
247+
# Map abstract L-System modules to concrete 3D geometry for rendering.
248+
# These rules define how the symbolic representation becomes visual.
249+
250+
homomorphism:
251+
252+
# Internode segments become cylinders with length and radius
253+
I(a,r,o) --> F(a,r)
254+
# Branch segments also become cylinders
255+
S(a,r,o) --> F(a,r)
256+
257+
# -----------------------------------------------------------------------------
258+
# ATTRACTOR VISUALIZATION
259+
# -----------------------------------------------------------------------------
260+
# Additional production rules for displaying trellis attractor points
261+
production:
262+
Attractors(trellis_support):
263+
# Display enabled attractor points as visual markers
264+
points_to_display = trellis_support.attractor_grid.get_enabled_points()
265+
if len(points_to_display) > 0:
266+
produce [,(3) @g(PointSet(points_to_display,width=simulation_config.attractor_point_width))]

0 commit comments

Comments
 (0)