Skip to content

Commit dfa6069

Browse files
authored
Add files via upload
Updates to comments, correction of long-term bug which caused some V-Systems to fail. Updated grammar descriptions to include shrinkage and expantion of vessels.
1 parent 02c30aa commit dfa6069

5 files changed

Lines changed: 221 additions & 268 deletions

File tree

analyseGrammar.py

Lines changed: 46 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
DEGREES_TO_RADIANS = pi / 180
77

88
def branching_turtle_to_coords(turtle_program, d0, theta=20., phi=20.):
9+
910
'''
1011
Working with discontinuous paths i.e. tree formation
1112
'+' : postive rotation by (deg)
@@ -22,87 +23,83 @@ def branching_turtle_to_coords(turtle_program, d0, theta=20., phi=20.):
2223
Returns
2324
tuple: A list of tuples which provide the coordinates of the branches
2425
'''
25-
26-
# Initialize variables
27-
saved_states = list() # Stack for saving and restoring states
28-
stateSize = 10 # Number of elements in each state tuple
29-
dx = 0 # x displacement
30-
dy = 0 # y displacement
31-
dz = 0 # z displacement
32-
lseg = 1. # Length of each segment
33-
rim = 400 # Rim radius for proximity calculation
34-
35-
# Choose the starting direction randomly
26+
saved_states = list()
27+
stateSize = 10
28+
dx = 0
29+
dy = 0
30+
dz = 0
31+
lseg = 1.
32+
rim = 400
33+
3634
startidx = 3#random.randint(1,3)
3735
if startidx == 1:
3836
state = (1., 0.1, 0.1, 0, 0, d0, lseg, dx, dy, dz)
3937
elif startidx == 2:
4038
state = (0.1, 1., 0, 0, 0, d0, lseg, dx, dy, dz)
4139
else:
4240
state = (0.1, 0.1, 1., 0, 0, d0, lseg, dx, dy, dz)
41+
4342
yield state
4443

45-
index = 0 # Keeps track of the index of the command being executed
46-
origin = (0.1, 0.1, 1.) # Origin point for proximity calculation
44+
index = 0
45+
origin = (0.1, 0.1, 1.)
4746

48-
# Loop through each command in the turtle program
4947
for command in turtle_program:
50-
# Get the current state of the turtle
5148
x, y, z, alpha, beta, diam, lseg, dx, dy, dz = state
5249

53-
# If the command is a move forward command
50+
5451
if command.lower() in 'abcdefghijs': # Move forward (matches a-j and A-J)
52+
# segment start
53+
5554

56-
# Start a new segment
5755
if command.islower():
58-
# Get the length and diameter of the segment from the brackets
5956
lseg, tdiam = eval_brackets(index, turtle_program)
60-
# Calculate the displacement vector based on the current direction and segment length
61-
dx, dy, dz = rotate(p=beta*DEGREES_TO_RADIANS,
62-
r=alpha*DEGREES_TO_RADIANS,
63-
v=normalise(np.array([x,y,z]),lseg))
64-
# If the diameter is specified, update the current diameter
65-
if tdiam > 0.0:
66-
diam = tdiam
67-
# Move the turtle forward by the displacement vector
57+
dx, dy, dz = rotate(pitch_angle=beta*DEGREES_TO_RADIANS,
58+
roll_angle=alpha*DEGREES_TO_RADIANS,
59+
vector=normalise(np.array([x,y,z]),lseg))
60+
61+
if tdiam > 0.0: diam = tdiam
62+
63+
#dx, dy, dz, alpha = proximity(state,origin,rim)
64+
6865
x += dx
6966
y += dy
7067
z += dz
7168

72-
# Save the current state
7369
state = (x, y, z, alpha, beta, diam, lseg, dx, dy, dz)
70+
71+
# segment end
7472
yield state
7573

76-
elif command == '+': # Turn clockwise
77-
phi, _ = eval_brackets(index, turtle_program) # Get the angle to rotate by
78-
state = (x, y, z, alpha + phi, beta, diam, lseg, dx, dy, dz) # Update the state with new angle
74+
elif command == '+': # Turn clockwise
75+
phi, _ = eval_brackets(index, turtle_program)
76+
state = (x, y, z, alpha + phi, beta, diam, lseg, dx, dy, dz)
7977

80-
elif command == '-': # Turn counterclockwise
81-
phi, _ = eval_brackets(index, turtle_program) # Get the angle to rotate by
82-
state = (x, y, z, alpha - phi, beta, diam, lseg, dx, dy, dz) # Update the state with new angle
78+
elif command == '-': # Turn counterclockwise
79+
phi, _ = eval_brackets(index, turtle_program)
80+
state = (x, y, z, alpha - phi, beta, diam, lseg, dx, dy, dz)
8381

84-
elif command == '/': # Turn around y-axis
85-
theta, _ = eval_brackets(index, turtle_program) # Get the angle to rotate by
86-
state = (x, y, z, alpha, beta + theta, diam, lseg, dx, dy, dz) # Update the state with new angle
82+
elif command == '/':
83+
theta, _ = eval_brackets(index, turtle_program)
84+
state = (x, y, z, alpha, beta + theta, diam, lseg, dx, dy, dz)
8785

88-
elif command == '[': # Remember current state
89-
saved_states.append(state) # Push the current state onto the stack
86+
elif command == '[': # Remember current state
87+
saved_states.append(state)
9088

91-
elif command == ']': # Return to previous state
92-
state = saved_states.pop() # Pop the previous state from the stack and update the current state
89+
elif command == ']': # Return to previous state
90+
state = saved_states.pop()
9391

94-
# Yield a tuple of NaN values to indicate the start of a new branch
95-
nanValues = []
96-
for i in range(stateSize): nanValues.append(float('nan'))
97-
yield tuple(nanValues)
92+
nanValues = []
93+
for i in range(stateSize): nanValues.append(float('nan'))
94+
yield tuple(nanValues)
9895

99-
x, y, z, alpha, beta, diam, lseg, dx, dy, dz = state # Update the current state with previous state
100-
yield state # Yield the current state
96+
x, y, z, alpha, beta, diam, lseg, dx, dy, dz = state
97+
yield state
10198

102-
index += 1 # Increment the command index
103-
# Note: silently ignore unknown commands
99+
index += 1
104100

105101
def eval_brackets(index, turtle):
102+
106103
"""
107104
Extracts values within brackets in the turtle program and evaluates them to return tuple of values.
108105
@@ -145,7 +142,6 @@ def eval_brackets(index, turtle):
145142
if neg1 == 1: aa *= -1 # if neg1 flag is set, negate first value
146143
return aa, 0.0
147144

148-
149145
def randomposneg():
150146
"""
151147
Return either 1 or -1 with a 50/50 probability.
@@ -155,8 +151,6 @@ def randomposneg():
155151
"""
156152
return 1 if random.random() < 0.5 else -1
157153

158-
159-
160154
def raddist(origin, location, shell=80, core=False):
161155
"""
162156
Calculate the distance between two points and check if it falls within a specified range.
@@ -181,8 +175,6 @@ def raddist(origin, location, shell=80, core=False):
181175
# check if location is outside the shell
182176
return distance > shell
183177

184-
185-
186178
def proximity(state: tuple, origin: np.ndarray, rim: float) -> tuple:
187179
"""
188180
Calculates the proximity of a point to a given origin within a specified rim.
@@ -244,5 +236,4 @@ def posneg(value: float) -> float:
244236
if value >= 0.:
245237
return 1.
246238
else:
247-
return -1.
248-
239+
return -1.

libGenerator.py

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import ast
2-
import random
31
import numpy as np
2+
import random
43
from numpy import math as npm
54

65
default={"k": 3,
@@ -19,16 +18,12 @@ def setProperties(properties):
1918
Returns:
2019
None
2120
22-
Raises:
23-
None
2421
"""
25-
26-
# Check if properties is None and assign default value
27-
if properties is None:
22+
if properties == None:
2823
properties = default
2924

30-
# Define global variables based on the input dictionary
3125
global k, epsilon, randmarg, sigma, stochparams
26+
3227
k = properties['k']
3328
epsilon = properties['epsilon']
3429
randmarg = properties['randmarg']
@@ -49,59 +44,52 @@ def calParam(text, params):
4944
'''
5045

5146
txt = text[:]
52-
for i in params:
53-
txt = txt.replace(i, str(params[i]))
54-
try:
55-
result = ast.literal_eval(txt)
56-
return str(params['co'] / result)
57-
except:
58-
return 'Error: Invalid expression'
47+
for i in params: txt = txt.replace(i, str(params[i]))
48+
49+
return str(params['co'] / eval(txt))
5950

6051
def calBifurcation(d0):
61-
62-
'''
52+
'''
6353
Calculates the diameters and angles of bifurcation given an input diameter
6454
65-
Parameters:
55+
Args:
6656
d0 (float): input diameter
6757
6858
Returns:
6959
resp (dict): a dictionary containing the calculated values for d1, d2, d0, th1, th2, and co
7060
'''
61+
7162
resp = {}
72-
73-
# Calculate optimal diameter and adjust if stochastic parameter is set to True
63+
7464
dOpti = d0 / 2 ** (1.0 / k)
75-
if stochparams:
76-
d1 = abs(np.random.normal(dOpti, dOpti / sigma))
77-
else:
78-
d1 = dOpti # Optimal diameter
79-
80-
# Ensure that d1 is not greater than d0
81-
if d1 >= d0:
82-
d1 = dOpti
83-
84-
# Calculate second diameter using the first diameter and the rate of symmetry between daughters
65+
if stochparams: d1 = abs(np.random.normal(dOpti, dOpti / sigma))
66+
else: d1 = dOpti # Optimal diameter
67+
68+
if d1 >= d0: d1 = dOpti # Elimate possibility of d1 being greater than d0
69+
70+
d2 = (d0 ** k - d1 ** k) ** (1.0 / k) # Calculate second diameter
71+
# alpha = abs(np.random.uniform(1., 0.25)) * (d2 / d1) # Rate of symmetry of daughters (=1 symmetrical ?)
8572
alpha = d2 / d1
86-
d2 = (d0 ** k - d1 ** k) ** (1.0 / k)
87-
88-
# Equations which mimic bifurcation angles in the human body (Liu et al. (2010) and Zamir et al. (1988))
73+
74+
'''
75+
Equations which mimic bifurcation angles in the human body
76+
Liu et al. (2010) and Zamir et al. (1988)
77+
'''
8978
xtmp = (1 + alpha * alpha * alpha) ** (4.0 / 3) + 1 - alpha ** 4
9079
xtmpb = 2 * ((1 + alpha * alpha * alpha ) ** (2.0 / 3))
9180
a1 = npm.acos(xtmp / xtmpb)
9281

9382
xtmp = (1 + alpha * alpha * alpha) ** (4.0 / 3) + (alpha ** 4) - 1
9483
xtmpb = 2 * alpha * alpha * ((1 + alpha * alpha * alpha) ** (2.0/3))
9584
a2 = npm.acos(xtmp / xtmpb)
96-
97-
# Store calculated values in a dictionary and return
85+
9886
resp["d1"] = d1
9987
resp["d2"] = d2
10088
resp["d0"] = d0
10189
resp["th1"] = a1 * 180 / npm.pi
10290
resp["th2"] = a2 * 180 / npm.pi
10391
resp["co"] = getLength(d0)
104-
92+
10593
return resp
10694

10795
def getLength(d0):
@@ -114,6 +102,6 @@ def getLength(d0):
114102
Returns:
115103
float: The length of the branch.
116104
"""
117-
c0 = d0 * epsilon # calculate c0 based on the parent branch diameter and epsilon
118-
119-
return np.random.uniform(c0, randmarg * 2)
105+
c0 = d0 * epsilon
106+
# abs(np.random.normal(50,10))
107+
return np.random.uniform(c0 - randmarg, c0 + randmarg)

0 commit comments

Comments
 (0)