RDG-Networks 0.3.7__tar.gz → 0.3.9__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/PKG-INFO +10 -2
  2. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_Networks.egg-info/PKG-INFO +10 -2
  3. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_Networks.egg-info/SOURCES.txt +2 -1
  4. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_Networks.egg-info/entry_points.txt +1 -1
  5. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_networks/__init__.py +6 -6
  6. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_networks/thickness/Classes.py +2 -1
  7. rdg_networks-0.3.9/RDG_networks/thickness/__init__.py +19 -0
  8. rdg_networks-0.3.9/RDG_networks/thickness/generate_line_segments_thickness.py +644 -0
  9. rdg_networks-0.3.7/RDG_networks/thickness/generate_line_segments_thickness.py → rdg_networks-0.3.9/RDG_networks/thickness/generate_line_segments_thickness_correct.py +11 -20
  10. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_networks/thickness/generate_line_segments_thickness_static.py +1 -1
  11. rdg_networks-0.3.7/RDG_networks/thickness/generate_line_segments_thickness_orientation.py → rdg_networks-0.3.9/RDG_networks/thickness/orientate_network.py +56 -88
  12. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/setup.py +2 -2
  13. rdg_networks-0.3.7/RDG_networks/thickness/__init__.py +0 -19
  14. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/LICENSE.txt +0 -0
  15. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_Networks.egg-info/dependency_links.txt +0 -0
  16. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_Networks.egg-info/requires.txt +0 -0
  17. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_Networks.egg-info/top_level.txt +0 -0
  18. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_networks/Classes.py +0 -0
  19. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_networks/draw_segments.py +0 -0
  20. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_networks/generate_line_network.py +0 -0
  21. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_networks/generate_line_segments.py +0 -0
  22. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_networks/generate_line_segments_dynamic.py +0 -0
  23. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_networks/generate_line_segments_static.py +0 -0
  24. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_networks/get_intersection_segments.py +0 -0
  25. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_networks/sample_in_polygon.py +0 -0
  26. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_networks/save_to_stl.py +0 -0
  27. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/RDG_networks/thickness/sample_in_polygon.py +0 -0
  28. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/README.md +0 -0
  29. {rdg_networks-0.3.7 → rdg_networks-0.3.9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: RDG-Networks
3
- Version: 0.3.7
3
+ Version: 0.3.9
4
4
  Summary: Most of the code from the RDG Networks project
5
5
  Home-page: https://github.com/NiekMooij/RDG_networks
6
6
  Author: Niek Mooij
@@ -14,6 +14,14 @@ Requires-Dist: numpy
14
14
  Requires-Dist: scipy
15
15
  Requires-Dist: shapely
16
16
  Requires-Dist: typing
17
+ Dynamic: author
18
+ Dynamic: author-email
19
+ Dynamic: classifier
20
+ Dynamic: description
21
+ Dynamic: home-page
22
+ Dynamic: license
23
+ Dynamic: requires-dist
24
+ Dynamic: summary
17
25
 
18
26
  ## Overview
19
27
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: RDG-Networks
3
- Version: 0.3.7
3
+ Version: 0.3.9
4
4
  Summary: Most of the code from the RDG Networks project
5
5
  Home-page: https://github.com/NiekMooij/RDG_networks
6
6
  Author: Niek Mooij
@@ -14,6 +14,14 @@ Requires-Dist: numpy
14
14
  Requires-Dist: scipy
15
15
  Requires-Dist: shapely
16
16
  Requires-Dist: typing
17
+ Dynamic: author
18
+ Dynamic: author-email
19
+ Dynamic: classifier
20
+ Dynamic: description
21
+ Dynamic: home-page
22
+ Dynamic: license
23
+ Dynamic: requires-dist
24
+ Dynamic: summary
17
25
 
18
26
  ## Overview
19
27
 
@@ -20,6 +20,7 @@ RDG_networks/save_to_stl.py
20
20
  RDG_networks/thickness/Classes.py
21
21
  RDG_networks/thickness/__init__.py
22
22
  RDG_networks/thickness/generate_line_segments_thickness.py
23
- RDG_networks/thickness/generate_line_segments_thickness_orientation.py
23
+ RDG_networks/thickness/generate_line_segments_thickness_correct.py
24
24
  RDG_networks/thickness/generate_line_segments_thickness_static.py
25
+ RDG_networks/thickness/orientate_network.py
25
26
  RDG_networks/thickness/sample_in_polygon.py
@@ -6,10 +6,10 @@ generate_line_segments = RDG_networks.generate_line_segments:main
6
6
  generate_line_segments_dynamic = RDG_networks.generate_line_segments_dynamic:main
7
7
  generate_line_segments_static = RDG_networks.generate_line_segments_static:main
8
8
  generate_line_segments_thickness = RDG_networks.thickness.generate_line_segments_thickness:main
9
- generate_line_segments_thickness_orientation = RDG_networks.thickness.generate_line_segments_dynamic_orientation:main
10
9
  generate_line_segments_thickness_static = RDG_networks.generate_line_segments_thickness_static:main
11
10
  get_alignment_mean = RDG_networks.thickness.get_alignment_mean:main
12
11
  get_intersection_segments = RDG_networks.get_intersection_segments:main
12
+ orientate_network = RDG_networks.thickness.orientate_network:main
13
13
  rotate_network = RDG_networks.thickness.rotate_network:main
14
14
  save_to_stl = RDG_networks.save_to_stl:main
15
15
  translate_network = RDG_networks.thickness.translate_network:main
@@ -8,17 +8,17 @@ from .generate_line_segments_dynamic import generate_line_segments_dynamic
8
8
  from .generate_line_segments_static import generate_line_segments_static
9
9
  from .draw_segments import draw_segments
10
10
  from .thickness.generate_line_segments_thickness import generate_line_segments_thickness
11
- from .thickness.generate_line_segments_thickness_orientation import generate_line_segments_thickness_orientation
11
+ from .thickness.orientate_network import orientate_network
12
12
  from .thickness.generate_line_segments_thickness_static import generate_line_segments_thickness_static
13
- from .thickness.generate_line_segments_thickness_orientation import translate_network
14
- from .thickness.generate_line_segments_thickness_orientation import clip_network
15
- from .thickness.generate_line_segments_thickness_orientation import rotate_network
16
- from .thickness.generate_line_segments_thickness_orientation import get_alignment_mean
13
+ from .thickness.orientate_network import translate_network
14
+ from .thickness.orientate_network import clip_network
15
+ from .thickness.orientate_network import rotate_network
16
+ from .thickness.orientate_network import get_alignment_mean
17
17
  from .save_to_stl import save_to_stl
18
18
 
19
19
  __all__ = ['generate_line_segments',
20
20
  'generate_line_segments_thickness',
21
- 'generate_line_segments_thickness_orientation',
21
+ 'orientate_network',
22
22
  'translate_network',
23
23
  'clip_network',
24
24
  'rotate_network',
@@ -95,7 +95,7 @@ class Polygon:
95
95
  vertices (List[Tuple[float, float]]): A list of (x, y) coordinates representing the vertices of the polygon.
96
96
  """
97
97
 
98
- def __init__(self, vertices: List[tuple], middle_segment=None):
98
+ def __init__(self, vertices: List[tuple], middle_segment=None, neighbors=None):
99
99
  """
100
100
  Initializes a Polygon instance with the provided vertices.
101
101
 
@@ -104,6 +104,7 @@ class Polygon:
104
104
  """
105
105
  self.vertices = vertices
106
106
  self.middle_segment = middle_segment
107
+ self.neighbors = neighbors
107
108
 
108
109
  def area(self) -> float:
109
110
  """
@@ -0,0 +1,19 @@
1
+ # __init__.py
2
+
3
+ from .generate_line_segments_thickness import generate_line_segments_thickness
4
+ from .orientate_network import orientate_network
5
+ from .generate_line_segments_thickness_static import generate_line_segments_thickness_static
6
+ from .orientate_network import translate_network
7
+ from .orientate_network import clip_network
8
+ from .orientate_network import rotate_network
9
+ from .orientate_network import get_alignment_mean
10
+
11
+ __all__ = [
12
+ 'generate_line_segments_thickness',
13
+ 'orientate_network',
14
+ 'generate_line_segments_thickness_static',
15
+ 'translate_network',
16
+ 'clip_network',
17
+ 'rotate_network',
18
+ 'get_alignment_mean'
19
+ ]
@@ -0,0 +1,644 @@
1
+ import math
2
+ import numpy as np
3
+ import random
4
+ from typing import List, Dict, Tuple, Union, Optional
5
+ from shapely.geometry import Polygon as Polygon_Shapely, LineString
6
+
7
+ from .Classes import Line, LineSegment, Polygon
8
+ from .sample_in_polygon import sample_in_polygon, is_inside_polygon
9
+
10
+ def doLinesIntersect(line1: Line, line2: Line, box_size = 1) -> Tuple[bool, Union[Tuple[float, float], None]]:
11
+ """
12
+ Check if two lines intersect and return the intersection point.
13
+
14
+ Args:
15
+ - line1 (Line): The first line segment.
16
+ - line2 (Line): The second line segment.
17
+ - box_size (float): The size of the bounding box. Defaults to 1.
18
+
19
+ Returns:
20
+ - intersect (bool): True if the lines intersect, False otherwise.
21
+ - intersection_point (tuple or None): The intersection point (x, y) if lines intersect, None otherwise.
22
+ """
23
+ x1, y1 = line1.location
24
+ v1, w1 = line1.direction
25
+
26
+ x2, y2 = line2.location
27
+ v2, w2 = line2.direction
28
+
29
+ determinant = v1 * w2 - v2 * w1
30
+
31
+ if determinant == 0:
32
+ return False, (None, None)
33
+
34
+ t1 = ((x2 - x1) * w2 - (y2 - y1) * v2) / determinant
35
+ t2 = ((x2 - x1) * w1 - (y2 - y1) * v1) / determinant
36
+
37
+ intersect_x = x1 + v1 * t1
38
+ intersect_y = y2 + w2 * t2
39
+
40
+
41
+ if -1e-6 < intersect_x < box_size + 1e-6 and -1e-6 < intersect_y < box_size + 1e-6:
42
+ return True, (intersect_x, intersect_y)
43
+ else:
44
+ return False, (None, None)
45
+
46
+ def doSegmentsIntersect(
47
+ segment1: LineSegment,
48
+ segment2: LineSegment,
49
+ box_size = 1
50
+ ) -> Tuple[bool, Tuple[Optional[float], Optional[float]]]:
51
+ """
52
+ Determines if two line segments intersect and returns the intersection point if they do.
53
+
54
+ Args:
55
+ segment1 (LineSegment): The first line segment.
56
+ segment2 (LineSegment): The second line segment.
57
+
58
+ Returns:
59
+ Tuple[bool, Tuple[Optional[float], Optional[float]]]:
60
+ - A boolean indicating whether the segments intersect.
61
+ - A tuple of the x and y coordinates of the intersection point if they intersect,
62
+ otherwise (None, None).
63
+ """
64
+
65
+ # Create line equations based on the segments' start and end points
66
+ line1 = Line(location=segment1.start, direction=np.array(segment1.end) - np.array(segment1.start))
67
+ line2 = Line(location=segment2.start, direction=np.array(segment2.end) - np.array(segment2.start))
68
+
69
+ # Check if the infinite extensions of the two lines intersect
70
+ intersect, (intersect_x, intersect_y) = doLinesIntersect(line1, line2, box_size)
71
+
72
+ # If no intersection, return False
73
+ if not intersect:
74
+ return False, (None, None)
75
+
76
+ # Check if the intersection point is within the bounds of both segments in the x-direction
77
+ xcheck = (
78
+ (segment1.end[0] <= intersect_x <= segment1.start[0]
79
+ or segment1.start[0] <= intersect_x <= segment1.end[0]
80
+ or abs(intersect_x - segment1.end[0]) < 1e-6
81
+ or abs(intersect_x - segment1.start[0]) < 1e-6)
82
+ and
83
+ (segment2.end[0] <= intersect_x <= segment2.start[0]
84
+ or segment2.start[0] <= intersect_x <= segment2.end[0]
85
+ or abs(intersect_x - segment2.end[0]) < 1e-6
86
+ or abs(intersect_x - segment2.start[0]) < 1e-6)
87
+ )
88
+
89
+ # Check if the intersection point is within the bounds of both segments in the y-direction
90
+ ycheck = (
91
+ (segment1.end[1] <= intersect_y <= segment1.start[1]
92
+ or segment1.start[1] <= intersect_y <= segment1.end[1]
93
+ or abs(intersect_y - segment1.end[1]) < 1e-6
94
+ or abs(intersect_y - segment1.start[1]) < 1e-6)
95
+ and
96
+ (segment2.end[1] <= intersect_y <= segment2.start[1]
97
+ or segment2.start[1] <= intersect_y <= segment2.end[1]
98
+ or abs(intersect_y - segment2.end[1]) < 1e-6
99
+ or abs(intersect_y - segment2.start[1]) < 1e-6)
100
+ )
101
+
102
+ # If the intersection point lies within the bounds of both segments, return True with the intersection point
103
+ if xcheck and ycheck:
104
+ return True, (intersect_x, intersect_y)
105
+
106
+ # Otherwise, return False and no intersection point
107
+ return False, (None, None)
108
+
109
+ def pick_item_with_probability(
110
+ polygon_arr: Dict[str, Dict[str, object]]
111
+ ) -> Tuple[str, Dict[str, object]]:
112
+ """
113
+ Randomly selects an item from the polygon array with a probability proportional to the area of the polygons.
114
+
115
+ Args:
116
+ polygon_arr (Dict[str, Dict[str, object]]):
117
+ A dictionary where keys are polygon identifiers (e.g., 'p1', 'p2') and values are dictionaries containing polygon properties,
118
+ including an 'area' key that stores the area of the polygon.
119
+
120
+ Returns:
121
+ Tuple[str, Dict[str, object]]:
122
+ - The identifier of the selected polygon.
123
+ - The corresponding polygon data (dictionary) containing its properties.
124
+ """
125
+
126
+ # Calculate the total weight (sum of areas of all polygons)
127
+ max_weight = sum(pol['area'] for pol in polygon_arr.values())
128
+
129
+ # Generate a random threshold between 0 and the total weight
130
+ threshold = random.uniform(0, max_weight)
131
+ cumulative_weight = 0
132
+
133
+ # Iterate through the polygons, accumulating weights
134
+ for item, pol in polygon_arr.items():
135
+ weight = pol['area']
136
+ cumulative_weight += weight
137
+
138
+ # Return the polygon when the cumulative weight surpasses the threshold
139
+ if cumulative_weight >= threshold:
140
+ return item, pol
141
+
142
+ def get_location_and_direction(
143
+ polygon_arr: Dict[str, Dict[str, object]],
144
+ thickness: float,
145
+ angle: float,
146
+ nucleation_point: Tuple[float, float] = None,
147
+ min_distance: float = 0,
148
+ max_attempts: int = 1000
149
+ ) -> Union[Tuple[str, Dict[str, object], Tuple[float, float], np.ndarray, np.ndarray], bool]:
150
+ """
151
+ Attempts to find a valid location and direction within a polygon for placing a new segment. The direction can either be randomly
152
+ chosen (uniformly) or from a specified list of angles. It ensures that the segment lies within the polygon's bounds given the
153
+ specified thickness.
154
+
155
+ Args:
156
+ polygon_arr (Dict[str, Dict[str, object]]):
157
+ A dictionary where the keys are polygon identifiers and the values are dictionaries containing polygon properties, including 'vertices'.
158
+ thickness (float):
159
+ The thickness of the segment that needs to fit inside the polygon.
160
+ max_attempts (int, optional):
161
+ The maximum number of attempts to find a valid location and direction. Defaults to 1000.
162
+ angle (float):
163
+ A float indicating the angle of the new segment.
164
+ nucleation_point (Tuple[float, float], optional):
165
+ predified nucleation point for the segment. Defaults to None.
166
+ min_distance (float, optional):
167
+ the minimum distance between two lines. Defaults to 0.
168
+
169
+ Returns:
170
+ Union[Tuple[str, Dict[str, object], Tuple[float, float], np.ndarray, np.ndarray], bool]:
171
+ - If a valid location and direction are found, returns a tuple containing:
172
+ - The polygon ID (`str`).
173
+ - The polygon data (`Dict[str, object]`).
174
+ - The new location as a tuple of floats (`Tuple[float, float]`).
175
+ - The direction vector as a numpy array (`np.ndarray`).
176
+ - The perpendicular vector to the direction as a numpy array (`np.ndarray`).
177
+ - The nucleation point [x,y] of the segment
178
+ - The angle of the segment.
179
+ - Returns `False` if no valid location and direction are found after the maximum attempts.
180
+ """
181
+ direction = (np.cos(angle), np.sin(angle))
182
+ direction = np.array(direction) / np.linalg.norm(direction)
183
+
184
+ # Try to find a valid location and direction up to max_attempts
185
+ attempt = 0
186
+ while attempt < max_attempts:
187
+ polygon_id, polygon = pick_item_with_probability(polygon_arr)
188
+
189
+ # Sample a location within the polygon
190
+ #check if nucleation point is given
191
+ if nucleation_point is not None:
192
+ location_new = nucleation_point
193
+ else:
194
+ location_new = sample_in_polygon(polygon['vertices'])
195
+
196
+ # Compute the perpendicular vector to the direction
197
+ perpendicular = np.array([direction[1], -direction[0]])
198
+ perpendicular = perpendicular / np.linalg.norm(perpendicular)
199
+
200
+ # Ensure the perpendicular vector is oriented consistently (y-component is non-negative)
201
+ if perpendicular[1] < 0:
202
+ perpendicular = -perpendicular
203
+
204
+ # Compute the positions for the segment with thickness, shifted by half-thickness along the perpendicular direction
205
+ p1 = np.array(location_new) + (thickness/2 + min_distance) * perpendicular
206
+ p2 = np.array(location_new) - (thickness/2 + min_distance) * perpendicular
207
+
208
+ # Check if both endpoints of the segment are inside the polygon
209
+ if is_inside_polygon(polygon['vertices'], p1) and is_inside_polygon(polygon['vertices'], p2):
210
+ return polygon_id, polygon, location_new, direction, perpendicular, angle
211
+
212
+ attempt += 1
213
+
214
+ # If no valid location and direction is found, return False
215
+ return False
216
+
217
+ def get_polygons(polygon_id, polygon_arr, neighbor1_1, neighbor1_2, vertex_begin_1, vertex_end_1, neighbor2_1, neighbor2_2, vertex_begin_2, vertex_end_2, segment_new_id_1, segment_new_id_2):
218
+ # Extract vertices and cycle (faces) of the original polygon
219
+ vertices = polygon_arr[polygon_id]['vertices']
220
+ cycle = polygon_arr[polygon_id]['faces']
221
+
222
+ # Get first cycle and vertices
223
+ index_start_1, index_end_1 = (cycle.index(neighbor1_1), cycle.index(neighbor1_2))
224
+ if index_start_1 < index_end_1:
225
+ cycle1 = [segment_new_id_1] + cycle[index_start_1:index_end_1+1]
226
+ vertices1 = [vertex_begin_1] + vertices[index_start_1:index_end_1] + [vertex_end_1]
227
+ else:
228
+ cycle1 = [segment_new_id_1] + cycle[index_start_1:] + cycle[:index_end_1+1]
229
+ vertices1 = [vertex_begin_1] + vertices[index_start_1:] + vertices[:index_end_1] + [vertex_end_1]
230
+
231
+ # Get second cycle and vertices
232
+ index_start_2, index_end_2 = (cycle.index(neighbor2_2), cycle.index(neighbor2_1))
233
+ if index_start_2 < index_end_2:
234
+ cycle2 = [segment_new_id_2] + cycle[index_start_2:index_end_2+1]
235
+ vertices2 = [vertex_end_2] + vertices[index_start_2:index_end_2] + [vertex_begin_2]
236
+ else:
237
+ cycle2 = [segment_new_id_2] + cycle[index_start_2:] + cycle[:index_end_2+1]
238
+ vertices2 = [vertex_end_2] + vertices[index_start_2:] + vertices[:index_end_2] + [vertex_begin_2]
239
+
240
+ # Get middle cycle and vertices
241
+ cycle0 = [neighbor1_1, segment_new_id_1, neighbor1_2]
242
+ vertices0 = [vertex_begin_1, vertex_end_1]
243
+
244
+ index_start_0, index_end_0 = (cycle.index(neighbor1_2), cycle.index(neighbor2_2))
245
+ if index_start_0 < index_end_0:
246
+ cycle0 = cycle0 + cycle[index_start_0:index_end_0+1]
247
+ vertices0 = vertices0 + vertices[index_start_0:index_end_0]
248
+
249
+ elif index_start_0 > index_end_0:
250
+ cycle0 = cycle0 + cycle[index_start_0:] + cycle[:index_end_0+1]
251
+ vertices0 = vertices0 + vertices[index_start_0:] + vertices[:index_end_0]
252
+
253
+ cycle0 = cycle0 + [segment_new_id_2]
254
+ vertices0 = vertices0 + [vertex_end_2] + [vertex_begin_2]
255
+
256
+ index_start_0, index_end_0 = (cycle.index(neighbor2_1), cycle.index(neighbor1_1))
257
+ if index_start_0 < index_end_0:
258
+ cycle0 = cycle0 + cycle[index_start_0:index_end_0+1]
259
+ vertices0 = vertices0 + vertices[index_start_0:index_end_0]
260
+
261
+ elif index_start_0 > index_end_0:
262
+ cycle0 = cycle0 + cycle[index_start_0:] + cycle[:index_end_0+1]
263
+ vertices0 = vertices0 + vertices[index_start_0:] + vertices[:index_end_0]
264
+
265
+ return cycle0, vertices0, cycle1, vertices1, cycle2, vertices2
266
+
267
+ def get_new_segment(
268
+ line_segments_to_check: List[LineSegment],
269
+ location: Tuple[float, float],
270
+ direction: Tuple[float, float],
271
+ id: Optional[int] = None,
272
+ box_size: float = 1
273
+ ) -> LineSegment:
274
+ """
275
+ Creates a new line segment by extending a given location in a specified direction and
276
+ determines its neighbors by checking intersections with other line segments.
277
+
278
+ Args:
279
+ line_segments_to_check (List[LineSegment]): List of existing line segments to check for intersections.
280
+ location (Tuple[float, float]): The starting point (x, y) for the new line segment.
281
+ direction (Tuple[float, float]): The direction vector in which to extend the line segment.
282
+ id (Optional[int]): Optional ID for the new line segment. If not provided, defaults to None.
283
+ box_size(optional[Int]]): The size of the box. Defaults to 1.
284
+
285
+ Returns:
286
+ LineSegment: A new line segment object with its neighbors based on intersections.
287
+ """
288
+
289
+ # Create a temporary line segment extending from the location in both directions
290
+ s_temp = LineSegment(start=np.array(location) - 10*np.sqrt(2)*box_size * np.array(direction), end=np.array(location) + 10*np.sqrt(2)*box_size * np.array(direction))
291
+
292
+ intersection_points = []
293
+
294
+ # Check for intersections with existing line segments
295
+ for segment in line_segments_to_check:
296
+ intersect, (intersect_x, intersect_y) = doSegmentsIntersect(s_temp, segment, box_size)
297
+
298
+ if intersect:
299
+ segment_length = math.sqrt(
300
+ (location[0] - intersect_x) ** 2
301
+ + (location[1] - intersect_y) ** 2
302
+ )
303
+ intersection_points.append(
304
+ {"id": segment.id, "point": (intersect_x, intersect_y), "segment_length": segment_length}
305
+ )
306
+
307
+ # Divide intersections into ones behind and in front of the new line
308
+ intersections_b = [intersection for intersection in intersection_points if intersection["point"][0] < location[0]]
309
+ intersections_f = [intersection for intersection in intersection_points if intersection["point"][0] > location[0]]
310
+
311
+ if not intersections_b or not intersections_f:
312
+ intersections_b = [intersection for intersection in intersection_points if intersection["point"][1] < location[1]]
313
+ intersections_f = [intersection for intersection in intersection_points if intersection["point"][1] > location[1]]
314
+
315
+ # Determine the closest intersections for segment start and end
316
+ s_start = min(intersections_b, key=lambda x: x["segment_length"])
317
+ s_end = min(intersections_f, key=lambda x: x["segment_length"])
318
+ start, end = s_start['point'], s_end['point']
319
+ start_id, end_id = s_start['id'], s_end['id']
320
+
321
+ # Ensure the start comes before the end
322
+ if start[0] > end[0]:
323
+ start, end = end, start
324
+ start_id, end_id = end_id, start_id
325
+
326
+ # Create a new line segment and assign neighbors
327
+ neighbors_initial = {start_id: start, end_id: end}
328
+ segment_new = LineSegment(start=start, end=end, id=id, neighbors_initial=neighbors_initial, neighbors=neighbors_initial)
329
+
330
+ return segment_new
331
+
332
+ def update_data(
333
+ segments_dict: Dict[int, LineSegment],
334
+ polygon_arr: Dict[str, Dict[str, object]],
335
+ polygon_id: str,
336
+ segment_thickness_dict: Dict[int, Polygon],
337
+ vertices0: List[Tuple[float, float]],
338
+ vertices1: List[Tuple[float, float]],
339
+ vertices2: List[Tuple[float, float]],
340
+ cycle0: List[int],
341
+ cycle1: List[int],
342
+ cycle2: List[int],
343
+ neighbor1_1: int,
344
+ neighbor1_2: int,
345
+ neighbor2_1: int,
346
+ neighbor2_2: int,
347
+ vertex_begin_1: Tuple[float, float],
348
+ vertex_end_1: Tuple[float, float],
349
+ vertex_begin_2: Tuple[float, float],
350
+ vertex_end_2: Tuple[float, float],
351
+ id_1: int,
352
+ id_2: int
353
+ ) -> Tuple[Dict[int, LineSegment], Dict[str, Dict[str, object]], Dict[int, Polygon]]:
354
+ """
355
+ Updates the segments, polygons, and segment thickness dictionaries by adding new data derived
356
+ from provided vertices and neighbor information.
357
+
358
+ Args:
359
+ segments_dict (Dict[int, LineSegment]): A dictionary of segments with segment ID as the key.
360
+ polygon_arr (Dict[str, Dict[str, object]]): A dictionary of polygons with polygon ID as the key.
361
+ polygon_id (str): The ID of the polygon being updated.
362
+ segment_thickness_dict (Dict[int, Polygon]): A dictionary mapping thickness information to polygon objects.
363
+ vertices0 (List[Tuple[float, float]]): Vertices of the base polygon.
364
+ vertices1 (List[Tuple[float, float]]): Vertices of the first new polygon.
365
+ vertices2 (List[Tuple[float, float]]): Vertices of the second new polygon.
366
+ cycle0 (List[int]): List of face indices for the base polygon.
367
+ cycle1 (List[int]): List of face indices for the first new polygon.
368
+ cycle2 (List[int]): List of face indices for the second new polygon.
369
+ neighbor1_1 (int): ID of the first neighbor of the first segment.
370
+ neighbor1_2 (int): ID of the second neighbor of the first segment.
371
+ neighbor2_1 (int): ID of the first neighbor of the second segment.
372
+ neighbor2_2 (int): ID of the second neighbor of the second segment.
373
+ vertex_begin_1 (Tuple[float, float]): Starting vertex of the first segment.
374
+ vertex_end_1 (Tuple[float, float]): Ending vertex of the first segment.
375
+ vertex_begin_2 (Tuple[float, float]): Starting vertex of the second segment.
376
+ vertex_end_2 (Tuple[float, float]): Ending vertex of the second segment.
377
+ id_1 (int): ID of the first new segment.
378
+ id_2 (int): ID of the second new segment.
379
+
380
+ Returns:
381
+ Tuple[Dict[int, LineSegment], Dict[str, Dict[str, object]], Dict[int, Polygon]]:
382
+ - Updated dictionary of line segments.
383
+ - Updated dictionary of polygons.
384
+ - Updated dictionary of segment thickness.
385
+ """
386
+
387
+ # Update polygon_arr (a dictionary of polygons)
388
+ polygon_new_1 = {
389
+ f'p{len(polygon_arr) + 1}': {
390
+ 'vertices': vertices1,
391
+ 'area': Polygon(vertices=vertices1).area(),
392
+ 'faces': cycle1
393
+ }
394
+ }
395
+ polygon_new_2 = {
396
+ polygon_id: {
397
+ 'vertices': vertices2,
398
+ 'area': Polygon(vertices=vertices2).area(),
399
+ 'faces': cycle2
400
+ }
401
+ }
402
+ polygon_arr.update(polygon_new_1)
403
+ polygon_arr.update(polygon_new_2)
404
+
405
+ # Update the segments_dict for the first segment
406
+ neighbors_initial_1 = {
407
+ neighbor1_1: vertex_begin_1,
408
+ neighbor1_2: vertex_end_1
409
+ }
410
+ segment_new_1 = LineSegment(
411
+ start=vertex_begin_1,
412
+ end=vertex_end_1,
413
+ id=id_1,
414
+ neighbors_initial=neighbors_initial_1,
415
+ neighbors=neighbors_initial_1
416
+ )
417
+ segments_dict[segment_new_1.id] = segment_new_1
418
+ segments_dict[neighbor1_1].neighbors[id_1] = vertex_begin_1
419
+ segments_dict[neighbor1_2].neighbors[id_1] = vertex_end_1
420
+
421
+ # Update the segments_dict for the second segment
422
+ neighbors_initial_2 = {
423
+ neighbor2_1: vertex_begin_2,
424
+ neighbor2_2: vertex_end_2
425
+ }
426
+ segment_new_2 = LineSegment(
427
+ start=vertex_begin_2,
428
+ end=vertex_end_2,
429
+ id=id_2,
430
+ neighbors_initial=neighbors_initial_2,
431
+ neighbors=neighbors_initial_2
432
+ )
433
+ segments_dict[segment_new_2.id] = segment_new_2
434
+ segments_dict[neighbor2_1].neighbors[id_2] = vertex_begin_2
435
+ segments_dict[neighbor2_2].neighbors[id_2] = vertex_end_2
436
+
437
+ # Update the segment_thickness_dict with the base polygon
438
+ neighbors = cycle0.copy()
439
+ neighbors.remove(f'{len(segment_thickness_dict)+1}_1')
440
+ neighbors.remove(f'{len(segment_thickness_dict)+1}_2')
441
+ neighbors = [ i[:-2] if i not in ['b1', 'b2', 'b3', 'b4'] else i for i in neighbors ]
442
+ neighbors = list(set(neighbors))
443
+
444
+ segment_thickness_dict[len(segment_thickness_dict) + 1] = Polygon(vertices=vertices0, neighbors=neighbors)
445
+
446
+ for n in neighbors:
447
+ if n in ['b1', 'b2', 'b3', 'b4']:
448
+ continue
449
+
450
+ else:
451
+ segment_thickness_dict[int(n)].neighbors.append(len(segment_thickness_dict) + 1)
452
+
453
+ return segments_dict, polygon_arr, segment_thickness_dict
454
+
455
+ def add_line_segment(
456
+ segments_dict: Dict[int, LineSegment],
457
+ polygon_arr: Dict[str, Dict[str, object]],
458
+ segment_thickness_dict: Dict[int, Polygon],
459
+ angle: float,
460
+ thickness: float = 0,
461
+ nucleation_point: Tuple[float, float] = None,
462
+ min_distance: float = 0,
463
+ box_size: float = 1,
464
+ max_attempts: int = 1000
465
+ ) -> Union[Tuple[Dict[int, LineSegment], Dict[str, Dict[str, object]], Dict[int, Polygon], List[float], float], bool]:
466
+ """
467
+ Adds a new line segment to the segments and polygon data structures, with a given thickness and angle distribution.
468
+
469
+ Args:
470
+ segments_dict (Dict[int, LineSegment]): A dictionary containing the current line segments.
471
+ polygon_arr (Dict[str, Dict[str, object]]): A dictionary containing the current polygons and their properties.
472
+ segment_thickness_dict (Dict[int, Polygon]): A dictionary storing the thickness information mapped to polygons.
473
+ thickness (float): The thickness of the new segment to be added. Defaults to 0.
474
+ angles (str): The angle distribution method. Defaults to 'uniform'.
475
+ nucleation_point (Tuple[float, float]): A predefined nucleation point for the new segment. Defaults to None.
476
+ min_distance (float): The minimum distance between two lines. Defaults to 0.
477
+ box_size (float): The size of the box. Defaults to 1.
478
+ max_attempts (int): The maximum number of attempts to find a valid location and direction. Defaults to 1000.
479
+
480
+ Returns:
481
+ Union[Tuple[Dict[int, LineSegment], Dict[str, Dict[str, object]], Dict[int, Polygon]], List[float], float, bool]:
482
+ - A tuple containing the updated segments dictionary, polygon dictionary, and thickness dictionary,
483
+ or False if no valid location for the new segment is found.
484
+ -nucleation point in a list [x,y] and the angle of the segment in radians.
485
+ """
486
+
487
+ # Get a valid location and direction, or return False if none is found
488
+ loc = get_location_and_direction(polygon_arr, angle=angle, thickness=thickness, nucleation_point=nucleation_point, min_distance=min_distance, max_attempts=max_attempts)
489
+ if loc:
490
+ polygon_id, polygon, location_new, direction_new, perpendicular, angle_new = loc
491
+ else:
492
+ print('No valid location found')
493
+ return False
494
+
495
+ # Get the borders of the new segment with the given thickness
496
+ line_segments_to_check = [segments_dict[segment] for segment in polygon['faces']]
497
+ middle_segment = get_new_segment(line_segments_to_check, location=location_new, direction=direction_new, box_size=box_size)
498
+ s1 = get_new_segment(line_segments_to_check, location=np.array(location_new) + thickness * perpendicular / 2, direction=direction_new,box_size=box_size)
499
+ s2 = get_new_segment(line_segments_to_check, location=np.array(location_new) - thickness * perpendicular / 2, direction=direction_new, box_size=box_size)
500
+
501
+ # Extract neighbor information and segment vertices
502
+ neighbor1_1, neighbor1_2 = list(s1.neighbors.keys())
503
+ vertex_begin_1, vertex_end_1 = list(s1.neighbors.values())
504
+ neighbor2_1, neighbor2_2 = list(s2.neighbors.keys())
505
+ vertex_begin_2, vertex_end_2 = list(s2.neighbors.values())
506
+ id_1 = str(int((len(segments_dict.keys()) - 2) / 2)) + '_1'
507
+ id_2 = str(int((len(segments_dict.keys()) - 2) / 2)) + '_2'
508
+
509
+ # Get the resulting polygons after splitting
510
+ cycle0, vertices0, cycle1, vertices1, cycle2, vertices2 = get_polygons(
511
+ polygon_id,
512
+ polygon_arr,
513
+ neighbor1_1,
514
+ neighbor1_2,
515
+ vertex_begin_1,
516
+ vertex_end_1,
517
+ neighbor2_1,
518
+ neighbor2_2,
519
+ vertex_begin_2=vertex_begin_2,
520
+ vertex_end_2=vertex_end_2,
521
+ segment_new_id_1=id_1,
522
+ segment_new_id_2=id_2
523
+ )
524
+
525
+ # Update all relevant data structures
526
+ segments_dict, polygon_arr, segment_thickness_dict = update_data(
527
+ segments_dict,
528
+ polygon_arr,
529
+ polygon_id,
530
+ segment_thickness_dict,
531
+ vertices0,
532
+ vertices1,
533
+ vertices2,
534
+ cycle0,
535
+ cycle1,
536
+ cycle2,
537
+ neighbor1_1,
538
+ neighbor1_2,
539
+ neighbor2_1,
540
+ neighbor2_2,
541
+ vertex_begin_1,
542
+ vertex_end_1,
543
+ vertex_begin_2,
544
+ vertex_end_2,
545
+ id_1,
546
+ id_2
547
+ )
548
+
549
+ # Associate the middle segment with the newly created thickness entry
550
+ segment_thickness_dict[list(segment_thickness_dict.keys())[-1]].middle_segment = middle_segment
551
+
552
+ return segments_dict, polygon_arr, segment_thickness_dict, location_new, angle_new
553
+
554
+ def generate_line_segments_thickness(
555
+ size: int,
556
+ thickness_arr: List[float],
557
+ angles: str = 'uniform',
558
+ config: List[List[float]] = None,
559
+ epsilon: float = 0,
560
+ box_size: float = 1
561
+ ) -> Tuple[Dict[str, LineSegment], Dict[str, Dict[str, object]], Dict[int, Polygon], np.ndarray]:
562
+ """
563
+ Generates a specified number of line segments and updates the polygon and segment thickness dictionaries.
564
+
565
+ Args:
566
+ size (int): The number of line segments to generate.
567
+ thickness_arr (List[float]): A list containing the thickness values for each segment to be generated.
568
+ angles (str): Angle used in the generation of the segments.
569
+ config (List[List[float]]): A list of configurations for the nucleation points and angles.
570
+ epsilon (float): the minimum distance between two line.
571
+ box_size (float): the size of the box.
572
+
573
+ Returns:
574
+ Tuple[Dict[str, LineSegment], Dict[str, Dict[str, object]], Dict[int, Polygon]]:
575
+ - Updated dictionary of line segments.
576
+ - Updated dictionary of polygons.
577
+ - Updated dictionary of segment thicknesses.
578
+ - Array of the nucleation points and angles [x,y,theta].
579
+ """
580
+
581
+ # Initialize border segments for a square and its polygon representation
582
+ borders = [
583
+ LineSegment((box_size, 0), (0, 0), id='b1', neighbors_initial={'b2': (0, 0), 'b4': (box_size, 0)}, neighbors={'b2': (0, 0), 'b4': (box_size, 0)}),
584
+ LineSegment((0, box_size), (0, 0), id='b2', neighbors_initial={'b1': (0, 0), 'b3': (0, box_size)}, neighbors={'b1': (0, 0), 'b3': (0, box_size)}),
585
+ LineSegment((0, box_size), (box_size, box_size), id='b3', neighbors_initial={'b2': (0, box_size), 'b4': (box_size, box_size)}, neighbors={'b2': (0, box_size), 'b4': (box_size, box_size)}),
586
+ LineSegment((box_size, box_size), (box_size, 0), id='b4', neighbors_initial={'b1': (box_size, 0), 'b3': (box_size, box_size)}, neighbors={'b1': (box_size, 0), 'b3': (box_size, box_size)})
587
+ ]
588
+
589
+ polygon_arr = {
590
+ 'p1': {
591
+ 'vertices': [(0, 0), (0, box_size), (box_size, box_size), (box_size, 0)],
592
+ 'area': box_size**2,
593
+ 'faces': ['b1', 'b2', 'b3', 'b4']
594
+ }
595
+ }
596
+
597
+ segments = borders
598
+ segments_dict = {segment.id: segment for segment in segments}
599
+ segment_thickness_dict = {}
600
+ generated_config = []
601
+
602
+ if config is not None and size > len(config):
603
+ print("The size of the configuration is smaller than the size of the segments. Generated a network of the same size as the configuration.")
604
+ size = len(config)
605
+
606
+ jammed = False
607
+ for i in range(size):
608
+ if config:
609
+ nucleation_point = config[i]['location']
610
+ angles = [config[i]['angle']]
611
+ else:
612
+ nucleation_point = None
613
+
614
+ output = add_line_segment(segments_dict,
615
+ polygon_arr,
616
+ segment_thickness_dict,
617
+ thickness=thickness_arr[i],
618
+ min_distance = epsilon,
619
+ nucleation_point = nucleation_point,
620
+ angle=angles[i],
621
+ box_size=box_size)
622
+ if output:
623
+ segments_dict, polygon_arr, segment_thickness_dict, location, angle = output
624
+ generated_config.append({ 'location': location, 'angle': angle, 'thickness': thickness_arr[i] })
625
+
626
+ else:
627
+ if config:
628
+ print('Configuration not possible. Point is skipped.')
629
+ else:
630
+ print(f"Stopped at iteration {len(segment_thickness_dict)}, could not find a valid segment position.")
631
+ jammed = True
632
+ break
633
+
634
+ # Uncomment the following line if you want progress feedback
635
+ percentage = np.round(i / size * 100, 3)
636
+ print(f'generate_segments: {percentage}% done', end='\r')
637
+
638
+ data_dict = {'segments_dict': segments_dict,
639
+ 'polygon_arr': polygon_arr,
640
+ 'segment_thickness_dict': segment_thickness_dict,
641
+ 'jammed': jammed,
642
+ 'generated_config': generated_config}
643
+
644
+ return data_dict
@@ -142,10 +142,10 @@ def pick_item_with_probability(
142
142
  def get_location_and_direction(
143
143
  polygon_arr: Dict[str, Dict[str, object]],
144
144
  thickness: float,
145
+ angle: float,
145
146
  nucleation_point: Tuple[float, float] = None,
146
147
  min_distance: float = 0,
147
- max_attempts: int = 1000,
148
- angles: Union[str, List[float]] = 'uniform'
148
+ max_attempts: int = 1000
149
149
  ) -> Union[Tuple[str, Dict[str, object], Tuple[float, float], np.ndarray, np.ndarray], bool]:
150
150
  """
151
151
  Attempts to find a valid location and direction within a polygon for placing a new segment. The direction can either be randomly
@@ -159,8 +159,8 @@ def get_location_and_direction(
159
159
  The thickness of the segment that needs to fit inside the polygon.
160
160
  max_attempts (int, optional):
161
161
  The maximum number of attempts to find a valid location and direction. Defaults to 1000.
162
- angles (Union[str, List[float]], optional):
163
- A string ('uniform' for random directions) or a list of angles (in radians) to choose the direction from. Defaults to 'uniform'.
162
+ angle (float):
163
+ A float indicating the angle of the new segment.
164
164
  nucleation_point (Tuple[float, float], optional):
165
165
  predified nucleation point for the segment. Defaults to None.
166
166
  min_distance (float, optional):
@@ -178,16 +178,8 @@ def get_location_and_direction(
178
178
  - The angle of the segment.
179
179
  - Returns `False` if no valid location and direction are found after the maximum attempts.
180
180
  """
181
-
182
- # Generate a new direction based on the angles parameter
183
- if angles == 'uniform':
184
- angle_new = random.uniform(-np.pi, np.pi)
185
- direction = (np.cos(angle_new), np.sin(angle_new))
186
- direction = direction / np.linalg.norm(direction) # Normalize the direction vector
187
- else:
188
- angle_new = random.choice(angles)
189
- direction = (np.cos(angle_new), np.sin(angle_new))
190
- direction = np.array(direction) / np.linalg.norm(direction)
181
+ direction = (np.cos(angle), np.sin(angle))
182
+ direction = np.array(direction) / np.linalg.norm(direction)
191
183
 
192
184
  # Try to find a valid location and direction up to max_attempts
193
185
  attempt = 0
@@ -215,7 +207,7 @@ def get_location_and_direction(
215
207
 
216
208
  # Check if both endpoints of the segment are inside the polygon
217
209
  if is_inside_polygon(polygon['vertices'], p1) and is_inside_polygon(polygon['vertices'], p2):
218
- return polygon_id, polygon, location_new, direction, perpendicular, angle_new
210
+ return polygon_id, polygon, location_new, direction, perpendicular, angle
219
211
 
220
212
  attempt += 1
221
213
 
@@ -451,7 +443,7 @@ def add_line_segment(
451
443
  segments_dict: Dict[int, LineSegment],
452
444
  polygon_arr: Dict[str, Dict[str, object]],
453
445
  segment_thickness_dict: Dict[int, Polygon],
454
- angles: str = 'uniform',
446
+ angle: float,
455
447
  thickness: float = 0,
456
448
  nucleation_point: Tuple[float, float] = None,
457
449
  min_distance: float = 0,
@@ -480,7 +472,7 @@ def add_line_segment(
480
472
  """
481
473
 
482
474
  # Get a valid location and direction, or return False if none is found
483
- loc = get_location_and_direction(polygon_arr, thickness, nucleation_point, min_distance, max_attempts=max_attempts, angles=angles)
475
+ loc = get_location_and_direction(polygon_arr, angle=angle, thickness=thickness, nucleation_point=nucleation_point, min_distance=min_distance, max_attempts=max_attempts)
484
476
  if loc:
485
477
  polygon_id, polygon, location_new, direction_new, perpendicular, angle_new = loc
486
478
  else:
@@ -560,8 +552,7 @@ def generate_line_segments_thickness(
560
552
  Args:
561
553
  size (int): The number of line segments to generate.
562
554
  thickness_arr (List[float]): A list containing the thickness values for each segment to be generated.
563
- angles (str): The angle distribution method for generating segments. Defaults to 'uniform'.
564
- List[float]: list of angles in radians.
555
+ angles (str): Angle used in the generation of the segments.
565
556
  config (List[List[float]]): A list of configurations for the nucleation points and angles.
566
557
  epsilon (float): the minimum distance between two line.
567
558
  box_size (float): the size of the box.
@@ -615,7 +606,7 @@ def generate_line_segments_thickness(
615
606
  thickness=thickness_arr[i],
616
607
  min_distance = epsilon,
617
608
  nucleation_point = nucleation_point,
618
- angles=angles,
609
+ angle=angles[i],
619
610
  box_size=box_size)
620
611
  if output:
621
612
  segments_dict, polygon_arr, segment_thickness_dict, location, angle = output
@@ -105,7 +105,7 @@ def seeds(number_of_lines, radius = 0.015, number_of_trials = 10000):
105
105
  if trial <= number_of_trials:
106
106
  nucleation_points += [new_points]
107
107
  angles = [0, np.pi/4, np.pi/2, 3*np.pi/4]
108
- angle_new = random.uniform(0, 2*np.pi) #random.choice(angles)#np.pi #random.uniform(0, 2*np.pi)
108
+ angle_new = random.uniform(0, 2*np.pi)
109
109
  angle += [angle_new]
110
110
  Line[line_id] = [ new_points ,angle_new]
111
111
  line_id += 1
@@ -1,8 +1,8 @@
1
1
  import numpy as np
2
+ import math
2
3
  from typing import List, Dict, Tuple
3
4
  from shapely.geometry import Polygon as Polygon_Shapely
4
5
  from shapely.geometry import LineString, box
5
- from concurrent.futures import ProcessPoolExecutor
6
6
  from .Classes import LineSegment, Polygon
7
7
 
8
8
  def rotate(point, center, rotation_matrix):
@@ -13,7 +13,9 @@ def rotate(point, center, rotation_matrix):
13
13
  rotation_matrix: 2x2 numpy array representing the rotation matrix
14
14
  """
15
15
  translated_point = point - center
16
- rotated_point = np.dot(rotation_matrix, translated_point)
16
+
17
+ # rotated_point = np.dot(rotation_matrix, translated_point)
18
+ rotated_point = rotation_matrix@translated_point
17
19
  final_point = rotated_point + center
18
20
 
19
21
  return final_point
@@ -30,16 +32,22 @@ def angle_between(v1, v2):
30
32
 
31
33
  def get_alignment_mean(line_vector_arr, director):
32
34
  """Get the mean alignment."""
33
- S_all = []
35
+ S_all = 0
36
+ total_mass = 0
34
37
  for item in line_vector_arr:
35
38
  line_vector = item['line_vector']
39
+ vector_diff = np.array(line_vector[1]) - np.array(line_vector[0])
40
+
36
41
  area = item['area']
37
- P2 = 0.5*(3*(np.cos(angle_between(line_vector, director)))**2-1)
38
- S_all.append(P2*area)
42
+ align = math.cos(angle_between(vector_diff, director))**2
43
+ S_all += align*area
44
+ total_mass += area
39
45
 
40
- return float(np.mean(S_all))
46
+ output = S_all / total_mass
41
47
 
42
- def compute_alignment_for_angle(
48
+ return output
49
+
50
+ def compute_alignment(
43
51
  angle: float,
44
52
  segment_thickness_dict: dict[str, Polygon],
45
53
  director: np.ndarray,
@@ -69,7 +77,7 @@ def compute_alignment_for_angle(
69
77
  tuple[float, float]
70
78
  A tuple where the first element is the input angle and the second element is the computed alignment value.
71
79
  """
72
- box_center = (np.array(box_measurements[0]) + np.array(box_measurements[2])) / 2
80
+ box_center = np.array((box_measurements[0]) + np.array(box_measurements[2])) / 2
73
81
 
74
82
  # Rotate network
75
83
  segment_thickness_dict_new = rotate_network(segment_thickness_dict, rotate_angle=angle, box_center=box_center)
@@ -79,13 +87,38 @@ def compute_alignment_for_angle(
79
87
 
80
88
  line_vectors = [
81
89
  {'line_vector': [seg.middle_segment.start, seg.middle_segment.end], 'area': seg.area()}
82
- for seg in segment_thickness_dict_new.values()
90
+ for seg in segment_thickness_dict_new.values() if seg.middle_segment is not None
83
91
  ]
84
92
 
85
93
  alignment = get_alignment_mean(line_vectors, director)
86
-
94
+
87
95
  return angle, alignment
88
96
 
97
+ def get_max_alignment(
98
+ segment_thickness_dict: dict,
99
+ director: np.ndarray,
100
+ box_measurements: list[float],
101
+ grid_points: int = 360
102
+ ) -> float:
103
+ """Find the angle with the maximum alignment using parallel processing."""
104
+ # Create a list of angles to evaluate
105
+ angles = np.linspace(0, np.pi, grid_points)
106
+
107
+ results = []
108
+ for a in angles:
109
+ result = compute_alignment(a, segment_thickness_dict, director, box_measurements)
110
+ results.append(result)
111
+
112
+ # Find the angle with the maximum alignment
113
+ max_alignment = 0
114
+ max_angle = None
115
+ for angle, alignment in results:
116
+ if alignment > max_alignment:
117
+ max_alignment = alignment
118
+ max_angle = angle
119
+
120
+ return max_angle
121
+
89
122
  def rotate_network(
90
123
  segment_thickness_dict: dict[str, Polygon],
91
124
  rotate_angle: float,
@@ -137,7 +170,7 @@ def rotate_network(
137
170
  middle_segment_new = LineSegment(start=start, end=end)
138
171
 
139
172
  # Store the rotated segment in the new dictionary
140
- segment_thickness_dict_new[id] = Polygon(vertices=vertices_new, middle_segment=middle_segment_new)
173
+ segment_thickness_dict_new[id] = Polygon(vertices=vertices_new, middle_segment=middle_segment_new, neighbors=segment.neighbors)
141
174
 
142
175
  return segment_thickness_dict_new
143
176
 
@@ -204,7 +237,7 @@ def clip_network(
204
237
  middle_segment_new = LineSegment(start=start, end=end)
205
238
 
206
239
  # Create a new clipped polygon with updated vertices and middle segment
207
- pol_new = Polygon(vertices=vertices_new, middle_segment=middle_segment_new)
240
+ pol_new = Polygon(vertices=vertices_new, middle_segment=middle_segment_new, neighbors=segment.neighbors)
208
241
  pol_new.sort_vertices() # Ensure vertices are sorted
209
242
  segment_thickness_dict_new[id] = pol_new
210
243
 
@@ -252,78 +285,16 @@ def translate_network(
252
285
  middle_segment_new = LineSegment(start=start, end=end)
253
286
 
254
287
  # Store the translated segment in the new dictionary
255
- segment_thickness_dict_new[id] = Polygon(vertices=vertices_new, middle_segment=middle_segment_new)
288
+ segment_thickness_dict_new[id] = Polygon(vertices=vertices_new, middle_segment=middle_segment_new, neighbors=segment.neighbors)
256
289
 
257
290
  return segment_thickness_dict_new
258
291
 
259
- def get_alignment_mean(line_vector_arr, director):
260
- """Get the mean alignment."""
261
- S_all = []
262
- for item in line_vector_arr:
263
- line_vector = item['line_vector']
264
- area = item['area']
265
- P2 = 0.5*(3*(np.cos(angle_between(line_vector, director)))**2-1)
266
- S_all.append(P2*area)
267
-
268
- return float(np.mean(S_all))
269
-
270
- def compute_alignment_for_angle(
271
- segment_thickness_dict: dict,
272
- angle: float,
273
- box_center,
274
- director: np.ndarray,
275
- ) -> tuple[float, float]:
276
- """Compute the alignment for a given angle."""
277
-
278
- # Rotate the segment network for the given angle
279
- segment_thickness_dict_rotated = rotate_network(segment_thickness_dict, rotate_angle=angle, box_center=box_center)
280
-
281
- # Create line vectors from the rotated segments
282
- line_vectors = []
283
- for s in segment_thickness_dict_rotated.values():
284
- line_vectors.append({'line_vector': np.array([s.middle_segment.start, s.middle_segment.end]), 'area': s.area()})
285
-
286
- # Compute the alignment for the current angle
287
- alignment = get_alignment_mean(line_vectors, director)
288
- return angle, alignment
289
-
290
- def get_max_alignment_angle(
291
- segment_thickness_dict: dict,
292
- director: np.ndarray,
293
- box_measurements: list[float],
294
- grid_points: int = 360
295
- ) -> float:
296
- """Find the angle with the maximum alignment using parallel processing."""
297
-
298
- # Create a list of angles to evaluate
299
- angles = np.linspace(0, 2 * np.pi, grid_points)
300
-
301
- # Use ProcessPoolExecutor for parallel computation of alignment
302
- with ProcessPoolExecutor() as executor:
303
- # Submit tasks to the pool for each angle
304
- results = list(executor.map(
305
- compute_alignment_for_angle,
306
- [segment_thickness_dict] * len(angles), # Same segment dictionary for all angles
307
- angles, # Different angles
308
- [box_measurements] * len(angles), # Same box measurements for all angles
309
- [director] * len(angles) # Same director for all angles
310
- ))
311
-
312
- # Find the angle with the maximum alignment
313
- max_alignment = 0
314
- max_angle = 0
315
- for angle, alignment in results:
316
- if alignment > max_alignment:
317
- max_alignment = alignment
318
- max_angle = angle
319
-
320
- return max_angle
321
-
322
- def generate_line_segments_thickness_orientation(
292
+ def orientate_network(
323
293
  data_dict: Dict[str, dict],
324
294
  orientation: List[int],
325
295
  grid_points: int = 360,
326
- box_measurements: List[Tuple[float, float]] = [(0, 0), (0, 1), (1, 1), (1, 0)]
296
+ box_measurements: List[Tuple[float, float]] = [(0, 0), (0, 1), (1, 1), (1, 0)],
297
+ director: np.ndarray = np.array([0, 1])
327
298
  ) -> List[Dict[str, dict]]:
328
299
  """
329
300
  Generates a set of networks of line segments with different thicknesses and orientations, and clips them to fit
@@ -354,36 +325,33 @@ def generate_line_segments_thickness_orientation(
354
325
 
355
326
  # Extract the segment thickness dictionary from the input data
356
327
  segment_thickness_dict = data_dict['segment_thickness_dict']
357
-
358
- # Define the director vector along the y-axis
359
- director = np.array([0, 1])
360
328
 
361
329
  # Find the angle that aligns the network most with the y-axis
362
- max_angle = get_max_alignment_angle(segment_thickness_dict, director, box_measurements, grid_points)
363
-
330
+ max_angle = get_max_alignment(segment_thickness_dict, director, box_measurements, grid_points)
331
+
364
332
  # Store the initial unmodified configuration
365
333
  output = [{'orientation': 'original', 'data_dict': data_dict}]
366
334
 
367
335
  # Loop through each given orientation, rotate, clip, and translate the network
368
336
  for o in orientation:
369
337
  # Compute the rotation angle for the current orientation relative to max alignment
370
- rotate_angle = o - max_angle
338
+ rotate_angle = -max_angle + o
371
339
 
372
340
  # Rotate the network by the computed angle
373
- segment_thickness_dict_new = rotate_network(segment_thickness_dict, rotate_angle=rotate_angle, box_center=box_center)
341
+ segment_thickness_dict_rotated = rotate_network(segment_thickness_dict, rotate_angle=rotate_angle, box_center=box_center)
374
342
 
375
343
  # Clip the rotated network to fit within the bounding box
376
- segment_thickness_dict_new = clip_network(segment_thickness_dict_new, box_measurements=box_measurements)
344
+ segment_thickness_dict_clipped = clip_network(segment_thickness_dict_rotated, box_measurements=box_measurements)
377
345
 
378
346
  # Translate the clipped network to start at the origin (0,0)
379
347
  translation_vector = -np.array(box_measurements[0])
380
- segment_thickness_dict_new = translate_network(segment_thickness_dict_new, translation_vector)
348
+ segment_thickness_dict_translated = translate_network(segment_thickness_dict_clipped, translation_vector)
381
349
 
382
350
  # Prepare a new data dictionary with the transformed segment information
383
351
  data_dict_new = {
384
352
  'segments_dict': None,
385
353
  'polygon_arr': None,
386
- 'segment_thickness_dict': segment_thickness_dict_new,
354
+ 'segment_thickness_dict': segment_thickness_dict_translated,
387
355
  'jammed': None,
388
356
  'generated_config': None
389
357
  }
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='RDG-Networks',
5
- version='0.3.7',
5
+ version='0.3.9',
6
6
  author='Niek Mooij',
7
7
  author_email='mooij.niek@gmail.com',
8
8
  description='Most of the code from the RDG Networks project',
@@ -26,7 +26,7 @@ setup(
26
26
  'generate_line_segments=RDG_networks.generate_line_segments:main',
27
27
  'generate_line_segments_thickness=RDG_networks.thickness.generate_line_segments_thickness:main',
28
28
 
29
- 'generate_line_segments_thickness_orientation=RDG_networks.thickness.generate_line_segments_dynamic_orientation:main',
29
+ 'orientate_network=RDG_networks.thickness.orientate_network:main',
30
30
 
31
31
  'translate_network=RDG_networks.thickness.translate_network:main',
32
32
  'clip_network=RDG_networks.thickness.clip_network:main',
@@ -1,19 +0,0 @@
1
- # __init__.py
2
-
3
- from .generate_line_segments_thickness import generate_line_segments_thickness
4
- from .generate_line_segments_thickness_orientation import generate_line_segments_thickness_orientation
5
- from .generate_line_segments_thickness_static import generate_line_segments_thickness_static
6
- from .generate_line_segments_thickness_orientation import translate_network
7
- from .generate_line_segments_thickness_orientation import clip_network
8
- from .generate_line_segments_thickness_orientation import rotate_network
9
- from .generate_line_segments_thickness_orientation import get_alignment_mean
10
-
11
- __all__ = [
12
- 'generate_line_segments_thickness',
13
- 'generate_line_segments_thickness_orientation',
14
- 'generate_line_segments_thickness_static',
15
- 'translate_network',
16
- 'clip_network',
17
- 'rotate_network',
18
- 'get_alignment_mean'
19
- ]
File without changes
File without changes
File without changes