|
2 | 2 | import matplotlib as mpl |
3 | 3 | import numpy as np |
4 | 4 | import scipy as sp |
5 | | -from tqdm.contrib.concurrent import process_map |
| 5 | +from tqdm import tqdm |
| 6 | +import multiprocessing as mp |
6 | 7 | import itertools |
7 | 8 |
|
8 | | -import cr_tissue as crf |
9 | | - |
10 | | - |
11 | | -def get_area(middle, vertices) -> float: |
12 | | - area = 0.0 |
13 | | - |
14 | | - for n in range(len(vertices)): |
15 | | - x = vertices[n] - middle |
16 | | - y = vertices[(n + 1) % len(vertices)] - middle |
17 | | - area += 0.5 * np.abs(x[1] * y[0] - x[0] * y[1]) |
18 | | - return area |
19 | | - |
20 | | - |
21 | | -def is_circle_in_convex_polygon(middle, radius, polygon): |
22 | | - """ |
23 | | - Checks if a circle is entirely contained within a convex polygon. |
24 | | - :param polygon: List of tuples [(x1, y1), (x2, y2), ...] |
25 | | - """ |
26 | | - n = len(polygon) |
27 | | - |
28 | | - for i in range(n): |
29 | | - # Current edge vertices |
30 | | - p1 = polygon[i] |
31 | | - p2 = polygon[(i + 1) % n] # Wraps around to the first point |
32 | | - |
33 | | - # 1. Check if center is on the 'inside' of the edge using cross product |
34 | | - # For CCW polygons, the point must be to the left of the edge |
35 | | - edge_dx = p2[0] - p1[0] |
36 | | - edge_dy = p2[1] - p1[1] |
37 | | - point_dx = middle[0] - p1[0] |
38 | | - point_dy = middle[1] - p1[1] |
39 | | - |
40 | | - # # Z-component of cross product |
41 | | - cross_product = edge_dx * point_dy - edge_dy * point_dx |
42 | | - |
43 | | - # # If cross_product < 0, the center is outside the convex hull |
44 | | - # if cross_product < 0: |
45 | | - # return False |
46 | | - |
47 | | - # 2. Check distance from center to the infinite line of the edge |
48 | | - # Distance from point (x0, y0) to line Ax + By + C = 0 |
49 | | - # Line eq: (y1-y2)x + (x2-x1)y + (x1y2 - x2y1) = 0 |
50 | | - dist = abs(cross_product) / np.sqrt(edge_dx**2 + edge_dy**2) |
51 | | - |
52 | | - if dist < radius: |
53 | | - return False |
54 | | - |
55 | | - return True |
56 | | - |
57 | | - |
58 | | -def min_max_exact_dist_point_to_segment(p, a, b, dist): |
59 | | - """ |
60 | | - Calculates the minimum distance between point P(px, py) |
61 | | - and line segment AB defined by points A(ax, ay) and B(bx, by). |
62 | | - """ |
63 | | - # Vector AB |
64 | | - dx = b - a |
65 | | - |
66 | | - # If A and B are the same point, distance is just P to A |
67 | | - d = np.linalg.norm(dx) ** 2 |
68 | | - if d == 0: |
69 | | - return np.linalg.norm(p - a), a, a, [a] |
70 | | - |
71 | | - # Calculate the projection of P onto the line AB |
72 | | - # t is the "parameter" along the line: t=0 is A, t=1 is B |
73 | | - t = np.dot((p - a), dx) / d |
74 | | - |
75 | | - # Clamp t to the range [0, 1] to stay on the segment |
76 | | - t = np.clip(t, 0, 1) |
77 | | - |
78 | | - # Find the closest point on the segment |
79 | | - closest = a + t * dx |
80 | | - |
81 | | - d1 = np.linalg.norm(a - p) |
82 | | - d2 = np.linalg.norm(b - p) |
83 | | - furthest = a if d1 > d2 else b |
84 | | - min_dist = np.linalg.norm(closest - p) |
85 | | - exact = [] |
86 | | - if d1 >= dist >= min_dist: |
87 | | - h = (dist - min_dist) / (d1 - min_dist) |
88 | | - exact.append(closest + h * (a - closest)) |
89 | | - if d2 >= dist >= min_dist: |
90 | | - h = (dist - min_dist) / (d2 - min_dist) |
91 | | - exact.append(closest + h * (b - closest)) |
92 | | - # Return the distance from P to the closest point |
93 | | - return min_dist, closest, furthest, exact |
94 | | - |
95 | | - |
96 | | -def determine_new_radius(middle, vertices, radius): |
97 | | - # Construct new polygon |
98 | | - radius_new = radius |
99 | | - new_polygon = [] |
100 | | - for n in range(len(vertices)): |
101 | | - v1 = vertices[n] |
102 | | - v2 = vertices[(n + 1) % len(vertices)] |
103 | | - _, closest, furthest, exact = min_max_exact_dist_point_to_segment( |
104 | | - middle, v1, v2, radius_new |
105 | | - ) |
106 | | - new_polygon.extend(exact) |
107 | | - |
108 | | - return np.array(new_polygon) |
| 9 | +import cr_tissue as crt |
109 | 10 |
|
110 | 11 |
|
111 | 12 | def __construct_polygon(path, resolution): |
@@ -134,8 +35,8 @@ def __construct_polygon(path, resolution): |
134 | 35 |
|
135 | 36 | def save_snapshot(iteration, domain_size, result, resolution=30): |
136 | 37 | fig, ax = plt.subplots(figsize=(8, 8)) |
137 | | - for middle, path, species in result[iteration]: |
138 | | - color = mpl.colormaps["Set1"](species) |
| 38 | + for _, path, area_ratio in result[iteration]: |
| 39 | + color = mpl.colormaps["Spectral"](area_ratio) |
139 | 40 | if len(path) > 0: |
140 | 41 | polygon = __construct_polygon(path, resolution) |
141 | 42 | ax.add_patch( |
@@ -163,55 +64,61 @@ def __pool_save_snapshot_helper(args): |
163 | 64 |
|
164 | 65 |
|
165 | 66 | if __name__ == "__main__": |
166 | | - settings = crf.SimulationSettings() |
| 67 | + settings = crt.SimulationSettings() |
| 68 | + |
| 69 | + domain_size = 500 |
| 70 | + domain_size_start = 200 |
| 71 | + n_agents = 700 |
167 | 72 |
|
168 | | - settings.n_plants = 40 |
169 | | - settings.n_threads = 1 |
170 | 73 | settings.dt = 0.5 |
171 | | - settings.t_max = 11_000.0 |
172 | | - settings.save_interval = 50.0 |
173 | | - settings.domain_size = 200.0 |
174 | | - |
175 | | - settings.target_area = 80 |
176 | | - settings.force_strength = 0.05 |
177 | | - settings.force_strength_weak = -0.001 |
178 | | - settings.force_strength_species = 0.03 |
179 | | - settings.force_relative_cutoff = 5.0 |
180 | | - settings.damping_constant = 1.0 |
181 | | - settings.cell_diffusion_constant = 0.002 |
| 74 | + settings.t_max = 20_000.0 |
| 75 | + settings.save_interval = 200.0 |
| 76 | + settings.domain_size = domain_size |
| 77 | + settings.n_voxels = 50 |
| 78 | + settings.approximation_steps = 10 |
| 79 | + |
| 80 | + radius = 5.0 |
| 81 | + target_area = np.pi * radius**2 |
| 82 | + target_perimeter = 2 * np.pi * radius * 1.0 |
182 | 83 |
|
183 | 84 | sampler = sp.stats.qmc.LatinHypercube(d=2, seed=10) |
184 | 85 | domain_size = settings.domain_size |
185 | | - samples = sampler.random(300) |
186 | | - samples = sp.stats.qmc.scale(samples, 0.05 * domain_size, 0.95 * domain_size) |
| 86 | + samples = sampler.random(n_agents) |
| 87 | + dlow = domain_size / 2 - domain_size_start / 2 |
| 88 | + dhigh = domain_size / 2 + domain_size_start / 2 |
| 89 | + samples = sp.stats.qmc.scale(samples, dlow, dhigh) |
187 | 90 |
|
188 | 91 | # samples = np.array( |
189 | 92 | # [ |
190 | 93 | # [0.49 * domain_size, 0.49 * domain_size], |
191 | 94 | # [0.49 * domain_size, 0.51 * domain_size], |
192 | 95 | # [0.51 * domain_size, 0.49 * domain_size], |
193 | 96 | # [0.51 * domain_size, 0.51 * domain_size], |
| 97 | + # [0.55 * domain_size, 0.50 * domain_size], |
194 | 98 | # ] |
195 | 99 | # ) |
196 | 100 |
|
197 | | - species = np.zeros(samples.shape[0], dtype=int) |
198 | | - species[::3] = 1 |
199 | | - species[1::3] = 2 |
| 101 | + agents = [] |
| 102 | + for pos in samples: |
| 103 | + agent = crt.Agent( |
| 104 | + pos, |
| 105 | + force_area=0.0001, |
| 106 | + force_perimeter=0.0001, |
| 107 | + force_dist=0.1, |
| 108 | + min_dist=0.6 * radius, |
| 109 | + target_area=target_area, |
| 110 | + target_perimeter=target_perimeter, |
| 111 | + damping=1.0, |
| 112 | + diffusion_constant=0.0001, |
| 113 | + ) |
| 114 | + agents.append(agent) |
200 | 115 |
|
201 | | - result = crf.run_simulation( |
202 | | - settings, |
203 | | - plant_points=samples, |
204 | | - plant_species=species, |
205 | | - ) |
| 116 | + result = crt.run_simulation(settings, agents) |
206 | 117 | print() |
207 | 118 |
|
208 | 119 | arglist = zip( |
209 | 120 | result, itertools.repeat(settings.domain_size), itertools.repeat(result) |
210 | 121 | ) |
211 | 122 |
|
212 | | - r = process_map(__pool_save_snapshot_helper, arglist, workers=-1) |
213 | | - # pool = mp.Pool(30) |
214 | | - # _ = list(tqdm(pool.imap(__pool_save_snapshot_helper, arglist), total=len(result))) |
215 | | - |
216 | | - # for iteration in tqdm(result, desc="Rendering Snapshots"): |
217 | | - # save_snapshot(iteration, settings.domain_size, result) |
| 123 | + pool = mp.Pool() |
| 124 | + _ = list(tqdm(pool.imap(__pool_save_snapshot_helper, arglist), total=len(result))) |
0 commit comments