RDG-Networks 0.3.5__py3-none-any.whl → 0.3.7__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: RDG-Networks
3
- Version: 0.3.5
3
+ Version: 0.3.7
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
@@ -0,0 +1,22 @@
1
+ RDG_networks/Classes.py,sha256=_9X3JPHFAYYlaC8IZ_H9__sfz99G5l9UfPl65lL60_4,7977
2
+ RDG_networks/__init__.py,sha256=tKoHFugrHAkce3wyw62MvOzcSfoBr2tpSp6da7Y54LQ,1764
3
+ RDG_networks/draw_segments.py,sha256=U53N5GXmQHWKdM1Q1faP_EGKjc6enOu2mcsunzSFpP0,984
4
+ RDG_networks/generate_line_network.py,sha256=lJ4rhObim3WcEQoebomewRQKWNJC5phFyFYRW7qjXIg,1127
5
+ RDG_networks/generate_line_segments.py,sha256=QV8_k7q6TD5c7Hcb2Ms_apEdWYw4XdLr7rdJgh49v4Q,9004
6
+ RDG_networks/generate_line_segments_dynamic.py,sha256=GoIhGXYbcvjqR5BJCnkvAGp8QBpzsE1ZSbl2k9XAOGI,7531
7
+ RDG_networks/generate_line_segments_static.py,sha256=7KvHZi3krv-tAGydJR_gbMMmHKZ5azzrKcQe3fuWzCE,9265
8
+ RDG_networks/get_intersection_segments.py,sha256=mXB5qCy1oOps4Vu1mX6flW6v_4Xxc71YK41yOWjJX8o,2797
9
+ RDG_networks/sample_in_polygon.py,sha256=qpPpW-Da1vK8ZkVWMJ0zBsE8IgyMB619gCdybSkzKSQ,1605
10
+ RDG_networks/save_to_stl.py,sha256=St8kGw6wl8uOGx8KhrZhBfe89-mOfp5JKhz0dEDBvc0,3894
11
+ RDG_networks/thickness/Classes.py,sha256=gVe_q5Rh_1DBiJoZ8H0FyJ4xG-IAcespjUpUirxFfAA,8125
12
+ RDG_networks/thickness/__init__.py,sha256=jyyA7Bp519TkOGNSYDVxPKxCgO9vTYpQvpFytnIuqQs,892
13
+ RDG_networks/thickness/generate_line_segments_thickness.py,sha256=meLFptrXWt_povCTlEA0n_TYVfi_-HF9KUvW_tBpt4w,29194
14
+ RDG_networks/thickness/generate_line_segments_thickness_orientation.py,sha256=oTEwQkXRBuoHvEdIGU30p21e2QHW1UlmApzRO1s5c64,16821
15
+ RDG_networks/thickness/generate_line_segments_thickness_static.py,sha256=zvYkLZpmyl711Kr7LCEFbXeVUgxQuA1n9Z5jD8W2iXc,9021
16
+ RDG_networks/thickness/sample_in_polygon.py,sha256=nJ-yqfoCCGfC6_EpGL3L1t1LOYdqWZd-7v5bxy6th34,1849
17
+ RDG_Networks-0.3.7.dist-info/LICENSE.txt,sha256=BHUkX2GsdTr30sKmVZ1MLGR1njnx17EX_oUuuSVZZPE,598
18
+ RDG_Networks-0.3.7.dist-info/METADATA,sha256=A_gS2IRz_hQxrek9dkl2BawsJ6SKYuBxIUBCdhkjb58,2422
19
+ RDG_Networks-0.3.7.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
20
+ RDG_Networks-0.3.7.dist-info/entry_points.txt,sha256=DRd5hzsY9jAz5e_gkd3gNoIwqj6RAZEtISlV1qpsIE8,1038
21
+ RDG_Networks-0.3.7.dist-info/top_level.txt,sha256=4gUUYafD5Al9V8ZSiViVGYHpRMMCsCBcGgCNodk9Syg,13
22
+ RDG_Networks-0.3.7.dist-info/RECORD,,
@@ -1,10 +1,15 @@
1
1
  [console_scripts]
2
+ clip_network = RDG_networks.thickness.clip_network:main
2
3
  draw_segments = RDG_networks.draw_segments:main
3
4
  generate_line_network = RDG_networks.generate_line_network:main
4
5
  generate_line_segments = RDG_networks.generate_line_segments:main
5
6
  generate_line_segments_dynamic = RDG_networks.generate_line_segments_dynamic:main
6
- generate_line_segments_dynamic_orientation = RDG_networks.generate_line_segments_dynamic_orientation: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
+ generate_line_segments_thickness_static = RDG_networks.generate_line_segments_thickness_static:main
11
+ get_alignment_mean = RDG_networks.thickness.get_alignment_mean:main
9
12
  get_intersection_segments = RDG_networks.get_intersection_segments:main
13
+ rotate_network = RDG_networks.thickness.rotate_network:main
10
14
  save_to_stl = RDG_networks.save_to_stl:main
15
+ translate_network = RDG_networks.thickness.translate_network:main
RDG_networks/__init__.py CHANGED
@@ -9,13 +9,23 @@ 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
11
  from .thickness.generate_line_segments_thickness_orientation import generate_line_segments_thickness_orientation
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
12
17
  from .save_to_stl import save_to_stl
13
18
 
14
19
  __all__ = ['generate_line_segments',
15
20
  'generate_line_segments_thickness',
16
21
  'generate_line_segments_thickness_orientation',
17
- 'generate_line_segments_dynamic',
22
+ 'translate_network',
23
+ 'clip_network',
24
+ 'rotate_network',
25
+ 'get_alignment_mean',
18
26
  'generate_line_segments_static',
27
+ 'generate_line_segments_thickness_static',
28
+ 'generate_line_segments_dynamic',
19
29
  'generate_line_network',
20
30
  'get_intersection_segments',
21
31
  'draw_segments',
@@ -87,7 +87,7 @@ def merge_3d_meshes(mesh_list):
87
87
 
88
88
  return merged_mesh
89
89
 
90
- def save_to_stl(seg_thick_dict, thickness, name):
90
+ def save_to_stl(seg_thick_dict, thickness, name, frame_thickness = None):
91
91
  mesh_list = []
92
92
  for k,v in seg_thick_dict.items():
93
93
  p = []
@@ -98,6 +98,18 @@ def save_to_stl(seg_thick_dict, thickness, name):
98
98
  None
99
99
 
100
100
  mesh_list.append(polygon_to_3d_mesh(Polygon(p), thickness=thickness))
101
+
102
+ if frame_thickness != None:
103
+ t = frame_thickness
104
+ bottom = Polygon([ (0,0-t), (0,0),(1,0),(1,0-t)])
105
+ top = Polygon([(0,1),(0,1+t), (1,1+t), (1,1)])
106
+ right = Polygon([(1,0-t), (1,1+t), (1+t,1+t), (1+t,0-t)])
107
+ left = Polygon([(0-t,0-t),(0-t,1+t), (0,1+t), (0,0-t)])
108
+
109
+ f = [bottom,top, right, left]
110
+
111
+ for f_ in f:
112
+ mesh_list.append(polygon_to_3d_mesh(f_, thickness=thickness))
101
113
 
102
114
  merged_mesh = merge_3d_meshes(mesh_list)
103
115
 
@@ -103,7 +103,7 @@ class Polygon:
103
103
  vertices (List[Tuple[float, float]]): A list of (x, y) coordinates representing the vertices of the polygon.
104
104
  """
105
105
  self.vertices = vertices
106
- self.middle_line = middle_segment
106
+ self.middle_segment = middle_segment
107
107
 
108
108
  def area(self) -> float:
109
109
  """
@@ -151,7 +151,8 @@ class Polygon:
151
151
  dy = point[1] - reference_point[1]
152
152
  return np.arctan2(dy, dx)
153
153
 
154
- reference_point = min(self.vertices, key=lambda point: point[1])
154
+ # reference_point = min(self.vertices, key=lambda point: point[1])
155
+ reference_point = np.mean(self.vertices, axis=0)
155
156
  return sorted(self.vertices, key=lambda point: polar_angle(point, reference_point))
156
157
 
157
158
  def draw(self, ax: axes.Axes, color='purple', alpha=0.8):
@@ -2,8 +2,18 @@
2
2
 
3
3
  from .generate_line_segments_thickness import generate_line_segments_thickness
4
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
5
10
 
6
11
  __all__ = [
7
12
  'generate_line_segments_thickness',
8
- 'generate_line_segments_thickness_orientation'
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'
9
19
  ]
@@ -196,7 +196,7 @@ def get_location_and_direction(
196
196
 
197
197
  # Sample a location within the polygon
198
198
  #check if nucleation point is given
199
- if nucleation_point:
199
+ if nucleation_point is not None:
200
200
  location_new = nucleation_point
201
201
  else:
202
202
  location_new = sample_in_polygon(polygon['vertices'])
@@ -606,8 +606,8 @@ def generate_line_segments_thickness(
606
606
  angles = [config[i]['angle']]
607
607
  else:
608
608
  nucleation_point = None
609
- if angles != 'uniform':
610
- angles=[angles[i]]
609
+ # if angles != 'uniform':
610
+ # angles=[angles[i]]
611
611
 
612
612
  output = add_line_segment(segments_dict,
613
613
  polygon_arr,
@@ -619,13 +619,16 @@ def generate_line_segments_thickness(
619
619
  box_size=box_size)
620
620
  if output:
621
621
  segments_dict, polygon_arr, segment_thickness_dict, location, angle = output
622
- # generated_config += [[n_pts[0], n_pts[1], ang]]
623
622
  generated_config.append({ 'location': location, 'angle': angle, 'thickness': thickness_arr[i] })
624
623
 
625
- else:
626
- print(f"Stopped at iteration {len(segment_thickness_dict)}, could not find a valid segment position.")
627
- jammed = True
628
-
624
+ else:
625
+ if config:
626
+ print('Configuration not possible. Point is skipped.')
627
+ else:
628
+ print(f"Stopped at iteration {len(segment_thickness_dict)}, could not find a valid segment position.")
629
+ jammed = True
630
+ break
631
+
629
632
  # Uncomment the following line if you want progress feedback
630
633
  percentage = np.round(i / size * 100, 3)
631
634
  print(f'generate_segments: {percentage}% done', end='\r')
@@ -1,13 +1,11 @@
1
1
  import numpy as np
2
- from typing import List, Dict, Tuple, Union, Optional
3
- from shapely.geometry import Polygon as Polygon_Shapely, LineString
4
- import matplotlib.pyplot as plt
2
+ from typing import List, Dict, Tuple
3
+ from shapely.geometry import Polygon as Polygon_Shapely
4
+ from shapely.geometry import LineString, box
5
5
  from concurrent.futures import ProcessPoolExecutor
6
+ from .Classes import LineSegment, Polygon
6
7
 
7
- from .Classes import Line, LineSegment, Polygon
8
- from .generate_line_segments_dynamic_thickness import generate_line_segments_dynamic_thickness
9
-
10
- def rot(point, center, rotation_matrix):
8
+ def rotate(point, center, rotation_matrix):
11
9
  """
12
10
  Rotates a point around the center using the given rotation matrix.
13
11
  point: numpy array representing the point to rotate
@@ -41,140 +39,356 @@ def get_alignment_mean(line_vector_arr, director):
41
39
 
42
40
  return float(np.mean(S_all))
43
41
 
44
- def compute_alignment_for_angle(angle, segment_thickness_dict, director, box_size):
45
- """Helper function to compute alignment for a given angle."""
46
- rotation_matrix = np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]])
47
- center = np.array([box_size / 2, box_size / 2])
48
- line_vector_arr = []
49
-
50
- # Get all line vectors and areas
51
- for segment in segment_thickness_dict.values():
52
- m_pts = np.array([segment.middle_segment.start, segment.middle_segment.end])
53
- (start_rotated, end_rotated) = rot(m_pts, center, rotation_matrix)
54
- line_vector = np.array(end_rotated) - np.array(start_rotated)
55
- line_vector_arr.append({'line_vector': line_vector, 'area': segment.area()})
56
-
57
- # Calculate the alignment for this angle
58
- alignment = get_alignment_mean(line_vector_arr, director)
42
+ def compute_alignment_for_angle(
43
+ angle: float,
44
+ segment_thickness_dict: dict[str, Polygon],
45
+ director: np.ndarray,
46
+ box_measurements: list[list[float]]
47
+ ) -> tuple[float, float]:
48
+ """
49
+ Computes the alignment of a network of segments for a given rotation angle.
50
+
51
+ This function rotates a network of segments based on the specified angle, clips the network to fit within the
52
+ specified bounding box, and then calculates the alignment of the network relative to a director vector.
53
+
54
+ Parameters:
55
+ -----------
56
+ angle : float
57
+ The angle (in radians or degrees, depending on your implementation) by which to rotate the network of segments.
58
+ segment_thickness_dict : dict[str, object]
59
+ A dictionary where the keys are segment IDs (as strings) and the values are objects representing segments
60
+ (should include properties like `middle_segment` and `area()`).
61
+ director : np.ndarray
62
+ A numpy array representing the director vector, typically a unit vector used for calculating the alignment.
63
+ box_measurements : list[list[float]]
64
+ A list containing the measurements of the bounding box. It typically contains four corner points as
65
+ sublists, with each sublist representing [x, y] coordinates of a corner.
66
+
67
+ Returns:
68
+ --------
69
+ tuple[float, float]
70
+ A tuple where the first element is the input angle and the second element is the computed alignment value.
71
+ """
72
+ box_center = (np.array(box_measurements[0]) + np.array(box_measurements[2])) / 2
73
+
74
+ # Rotate network
75
+ segment_thickness_dict_new = rotate_network(segment_thickness_dict, rotate_angle=angle, box_center=box_center)
76
+
77
+ # Clip network
78
+ segment_thickness_dict_new = clip_network(segment_thickness_dict_new, box_measurements=box_measurements)
79
+
80
+ line_vectors = [
81
+ {'line_vector': [seg.middle_segment.start, seg.middle_segment.end], 'area': seg.area()}
82
+ for seg in segment_thickness_dict_new.values()
83
+ ]
84
+
85
+ alignment = get_alignment_mean(line_vectors, director)
86
+
59
87
  return angle, alignment
60
88
 
61
- def get_max_alignment_angle(segment_thickness_dict, director, box_size, grid_points=360):
62
- """Get the angle with the maximum alignment."""
63
- # Define the angles we will test
64
- angles = np.linspace(0, 2 * np.pi, grid_points)
89
+ def rotate_network(
90
+ segment_thickness_dict: dict[str, Polygon],
91
+ rotate_angle: float,
92
+ box_center: Tuple[float, float]
93
+ ) -> dict[str, object]:
94
+ """
95
+ Rotates a network of line segments around a given center point.
65
96
 
66
- with ProcessPoolExecutor() as executor:
67
- # Submit tasks for each angle in parallel, passing necessary arguments
68
- results = list(executor.map(
69
- compute_alignment_for_angle,
70
- angles,
71
- [segment_thickness_dict] * len(angles),
72
- [director] * len(angles),
73
- [box_size] * len(angles)
74
- ))
97
+ This function rotates each segment in the provided network by a specified angle around the center of a bounding box.
98
+ The segments are represented by their vertices and a middle segment, and both are transformed using a rotation matrix.
75
99
 
76
- # Find the angle with the maximum alignment
77
- max_angle, _ = max(results, key=lambda x: x[1])
100
+ Parameters:
101
+ -----------
102
+ segment_thickness_dict : dict[str, object]
103
+ A dictionary where the keys are segment IDs (as strings) and the values are segment objects. Each segment
104
+ object must have a `vertices` attribute (list of vertex coordinates) and a `middle_segment` attribute.
105
+ rotate_angle : float
106
+ The angle in radians by which to rotate the network of segments.
107
+ box_center : Tuple[float, float]
108
+ The (x, y) coordinates representing the center point around which to rotate the network.
78
109
 
79
- return max_angle
110
+ Returns:
111
+ --------
112
+ dict[str, object]
113
+ A new dictionary with rotated segments, where the keys are the same segment IDs and the values are the
114
+ transformed segment objects with updated vertices and middle segments.
115
+ """
116
+ # Define the center and rotation matrix for rotation
117
+ center = np.array([box_center[0], box_center[1]])
118
+ rotation_matrix = np.array([[np.cos(rotate_angle), -np.sin(rotate_angle)],
119
+ [np.sin(rotate_angle), np.cos(rotate_angle)]])
120
+
121
+ # Create a new dictionary to store the rotated segments
122
+ segment_thickness_dict_new = {}
123
+
124
+ # Iterate over each segment and apply the rotation
125
+ for id, segment in segment_thickness_dict.items():
126
+ vertices_new = []
127
+ # Rotate each vertex of the segment
128
+ for v in segment.vertices:
129
+ v_rotate = rotate(v, center, rotation_matrix)
130
+ vertices_new.append(v_rotate)
131
+
132
+ # Rotate the start and end points of the middle segment
133
+ start = rotate(segment.middle_segment.start, center, rotation_matrix)
134
+ end = rotate(segment.middle_segment.end, center, rotation_matrix)
135
+
136
+ # Create a new middle segment with rotated coordinates
137
+ middle_segment_new = LineSegment(start=start, end=end)
138
+
139
+ # Store the rotated segment in the new dictionary
140
+ segment_thickness_dict_new[id] = Polygon(vertices=vertices_new, middle_segment=middle_segment_new)
141
+
142
+ return segment_thickness_dict_new
143
+
144
+ def clip_network(
145
+ segment_thickness_dict: dict[str, Polygon],
146
+ box_measurements: list[list[float]]
147
+ ) -> dict[str, object]:
148
+ """
149
+ Clips the segments in the network to fit within a bounding box.
150
+
151
+ This function clips each segment in the network so that only the portions that lie inside the bounding box are retained.
152
+ The bounding box is represented as a polygon, and any segment that intersects the box is clipped to the intersection area.
153
+
154
+ Parameters:
155
+ -----------
156
+ segment_thickness_dict : dict[str, object]
157
+ A dictionary where the keys are segment IDs (as strings) and the values are segment objects. Each segment
158
+ object must have a `vertices` attribute (list of vertex coordinates) and a `middle_segment` attribute.
159
+ box_measurements : list[list[float]]
160
+ A list of 4 sublists, each representing the [x, y] coordinates of a corner of the bounding box.
161
+
162
+ Returns:
163
+ --------
164
+ dict[str, object]
165
+ A dictionary containing the clipped segments, where the keys are the same segment IDs and the values are the
166
+ clipped segment objects with updated vertices and middle segments.
167
+ """
168
+ # Create a Shapely Polygon from the bounding box measurements
169
+ box_new = Polygon_Shapely([
170
+ (box_measurements[0][0], box_measurements[0][1]),
171
+ (box_measurements[1][0], box_measurements[1][1]),
172
+ (box_measurements[2][0], box_measurements[2][1]),
173
+ (box_measurements[3][0], box_measurements[3][1])
174
+ ])
175
+
176
+ # Dictionary to store the clipped segments
177
+ segment_thickness_dict_new = {}
178
+
179
+ # Iterate over each segment
180
+ for id, segment in enumerate(segment_thickness_dict.values()):
181
+ vertices_new = []
182
+ vertices = segment.vertices
183
+
184
+ # Create a Shapely polygon for the segment's vertices
185
+ pol = Polygon_Shapely(vertices)
186
+
187
+ # Find the intersection between the segment's polygon and the bounding box
188
+ intersection = box_new.intersection(pol)
189
+ if not intersection.is_empty:
190
+ # If there is an intersection, retrieve the clipped vertices
191
+ vertices_new = list(intersection.exterior.coords)
192
+
193
+ if vertices_new:
194
+ # If new vertices exist, clip the middle segment as well
195
+ start = segment.middle_segment.start
196
+ end = segment.middle_segment.end
80
197
 
81
- def orientate_network(segment_thickness_dict, config, rotate_angle, box_size):
82
- # Rotate the orginal network
83
- rotation_matrix = np.array([[np.cos(rotate_angle), -np.sin(rotate_angle)],[np.sin(rotate_angle), np.cos(rotate_angle)]])
84
- center = np.array([box_size/2,box_size/2])
198
+ middle_segment_new = None
199
+ # Find the intersection between the middle segment and the bounding box
200
+ intersection = box_new.intersection(LineString([start, end]))
201
+ if not intersection.is_empty:
202
+ start = list(intersection.coords)[0]
203
+ end = list(intersection.coords)[-1]
204
+ middle_segment_new = LineSegment(start=start, end=end)
85
205
 
86
- config_angles = [ item['angle'] for item in config]
87
- orientated_config = []
206
+ # Create a new clipped polygon with updated vertices and middle segment
207
+ pol_new = Polygon(vertices=vertices_new, middle_segment=middle_segment_new)
208
+ pol_new.sort_vertices() # Ensure vertices are sorted
209
+ segment_thickness_dict_new[id] = pol_new
88
210
 
89
- # Get all oriented lines
90
- for i, v in enumerate(segment_thickness_dict.values()):
91
- angle_rotated = config_angles[i] - rotate_angle
92
- n_p_0 = rot(np.array(v.middle_segment.start), center, rotation_matrix)
93
- n_p_1 = rot(np.array(v.middle_segment.end), center, rotation_matrix)
211
+ return segment_thickness_dict_new
94
212
 
95
- # Find the intersection between the rotated line and the square
96
- polygon_box = Polygon_Shapely([(0, 0), (box_size, 0), (box_size, box_size), (0, box_size)])
97
- line_middle_point = LineString([(n_p_0[0], n_p_0[1]), (n_p_1[0], n_p_1[1])])
213
+ def translate_network(
214
+ segment_thickness_dict: dict[str, Polygon],
215
+ translation_vector: np.ndarray
216
+ ) -> dict[str, object]:
217
+ """
218
+ Translates a network of line segments by a given translation vector.
219
+
220
+ This function moves each segment in the network by applying the translation vector to the coordinates of the vertices
221
+ and the start and end points of the middle segment (if it exists).
222
+
223
+ Parameters:
224
+ -----------
225
+ segment_thickness_dict : dict[str, object]
226
+ A dictionary where the keys are segment IDs (as strings) and the values are segment objects. Each segment
227
+ object must have `vertices` (list of vertex coordinates) and `middle_segment` attributes.
228
+ translation_vector : np.ndarray
229
+ A 2D numpy array representing the translation vector [x, y] that will be applied to all the vertices and
230
+ middle segments of each segment.
98
231
 
99
- # Calculate the intersection between the box and the line
100
- intersection = polygon_box.intersection(line_middle_point)
232
+ Returns:
233
+ --------
234
+ dict[str, object]
235
+ A new dictionary with the translated segments, where the keys are the same segment IDs and the values are
236
+ the translated segment objects.
237
+ """
238
+ # Create a new dictionary to store the translated segments
239
+ segment_thickness_dict_new = {}
101
240
 
102
- # Check if the line intersects the polygon
103
- if intersection.is_empty:
104
- continue
241
+ # Iterate over each segment and apply the translation
242
+ for id, segment in segment_thickness_dict.items():
243
+ # Translate the vertices by adding the translation vector to each vertex
244
+ vertices_new = [np.array(v) + translation_vector for v in segment.vertices]
245
+
246
+ # Check if the segment has a middle segment to translate
247
+ if segment.middle_segment is None:
248
+ middle_segment_new = None
105
249
  else:
106
- length = intersection.length
107
- midpoint = intersection.interpolate(length/2)
250
+ start = segment.middle_segment.start + translation_vector
251
+ end = segment.middle_segment.end + translation_vector
252
+ middle_segment_new = LineSegment(start=start, end=end)
253
+
254
+ # Store the translated segment in the new dictionary
255
+ segment_thickness_dict_new[id] = Polygon(vertices=vertices_new, middle_segment=middle_segment_new)
108
256
 
109
- x = midpoint.xy[0][0]
110
- y = midpoint.xy[1][0]
257
+ return segment_thickness_dict_new
111
258
 
112
- orientated_config.append({ 'location': (x,y), 'angle': angle_rotated, 'thickness': config[i]['thickness'] })
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)
113
267
 
114
- return orientated_config
268
+ return float(np.mean(S_all))
115
269
 
116
- def generate_line_segments_thickness_orientation(
117
- size: int,
118
- thickness_arr: List[float],
119
- orientation: List[int],
120
- angles: List[float],
121
- config: List[List[float]] = None,
122
- epsilon: float = 0,
123
- box_size: float = 1,
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],
124
294
  grid_points: int = 360
125
- ) -> List[Tuple[Dict[str, LineSegment], Dict[str, Dict[str, object]], Dict[int, Polygon], np.ndarray]]:
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(
323
+ data_dict: Dict[str, dict],
324
+ orientation: List[int],
325
+ grid_points: int = 360,
326
+ box_measurements: List[Tuple[float, float]] = [(0, 0), (0, 1), (1, 1), (1, 0)]
327
+ ) -> List[Dict[str, dict]]:
126
328
  """
127
- Generates a specified number of line segments and updates the polygon and segment thickness dictionaries.
128
-
129
- Args:
130
- size (int): The number of line segments to generate.
131
- thickness_arr (List[float]): A list containing the thickness values for each segment to be generated.
132
- angles (str): The angle distribution method for generating segments. Defaults to 'uniform'.
133
- List[float]: list of angles in radians.
134
- orientation (List[int]): the orientation of the model.
135
- config (List[List[float]]): A list of configurations for the nucleation points and angles.
136
- epsilon (float): the minimum distance between two line.
137
- box_size (float): the size of the system.
329
+ Generates a set of networks of line segments with different thicknesses and orientations, and clips them to fit
330
+ within a bounding box. The function also aligns the network to the maximum alignment angle with respect to the y-axis.
331
+
332
+ Parameters:
333
+ -----------
334
+ data_dict : Dict[str, dict]
335
+ A dictionary containing the initial network data. Must include the key 'segment_thickness_dict', which holds
336
+ the segment information.
337
+ orientation : List[int]
338
+ A list of orientations (angles in degrees or radians) to rotate the network. For each orientation, the network
339
+ is regenerated and rotated.
340
+ grid_points : int, optional
341
+ The number of grid points for calculating the maximum alignment angle (default is 360).
342
+ box_measurements : List[Tuple[float, float]], optional
343
+ A list of tuples representing the corner points of the bounding box (default is a unit square).
138
344
 
139
345
  Returns:
140
- - an array of dictionaries for each orientation containing:
141
- Tuple[Dict[str, LineSegment], Dict[str, Dict[str, object]], Dict[int, Polygon]]:
142
- - Updated dictionary of line segments.
143
- - Updated dictionary of polygons.
144
- - Updated dictionary of segment thicknesses.
145
- - Array of the nucleation points and angles [x,y,theta].
346
+ --------
347
+ List[Dict[str, dict]]
348
+ A list of dictionaries. Each dictionary contains the 'orientation' of the network and the updated 'data_dict'
349
+ with the rotated and clipped segment information.
146
350
  """
147
- # Size of the box
148
- box_size_0 = box_size*np.sqrt(2)
149
-
150
- # Initial structure
151
- data_dict = generate_line_segments_dynamic_thickness(size = size,
152
- thickness_arr = thickness_arr,
153
- epsilon= epsilon,
154
- config = config,
155
- angles = angles,
156
- box_size= box_size_0)
157
351
 
352
+ # Compute the center of the box
353
+ box_center = (np.array(box_measurements[0]) + np.array(box_measurements[2])) / 2
354
+
355
+ # Extract the segment thickness dictionary from the input data
158
356
  segment_thickness_dict = data_dict['segment_thickness_dict']
159
- generated_config = data_dict['generated_config']
160
357
 
161
- # Calculate alignment with the y axis
162
- director = (0,1)
163
- max_angle = get_max_alignment_angle(segment_thickness_dict, director, box_size, grid_points)
358
+ # Define the director vector along the y-axis
359
+ director = np.array([0, 1])
360
+
361
+ # 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
+
364
+ # Store the initial unmodified configuration
365
+ output = [{'orientation': 'original', 'data_dict': data_dict}]
164
366
 
165
- # Regenerate network for each orientation
166
- output = []
367
+ # Loop through each given orientation, rotate, clip, and translate the network
167
368
  for o in orientation:
168
- rotate_angle = o-max_angle
169
- orientated_config = orientate_network(segment_thickness_dict, generated_config, rotate_angle, box_size)
369
+ # Compute the rotation angle for the current orientation relative to max alignment
370
+ rotate_angle = o - max_angle
371
+
372
+ # 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)
170
374
 
171
- data_dict_new = generate_line_segments_dynamic_thickness(size=size,
172
- thickness_arr=thickness_arr,
173
- epsilon=epsilon,
174
- config=orientated_config,
175
- angles=angles,
176
- box_size=box_size)
375
+ # 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)
177
377
 
378
+ # Translate the clipped network to start at the origin (0,0)
379
+ translation_vector = -np.array(box_measurements[0])
380
+ segment_thickness_dict_new = translate_network(segment_thickness_dict_new, translation_vector)
381
+
382
+ # Prepare a new data dictionary with the transformed segment information
383
+ data_dict_new = {
384
+ 'segments_dict': None,
385
+ 'polygon_arr': None,
386
+ 'segment_thickness_dict': segment_thickness_dict_new,
387
+ 'jammed': None,
388
+ 'generated_config': None
389
+ }
390
+
391
+ # Append the result for this orientation
178
392
  output.append({'orientation': o, 'data_dict': data_dict_new})
179
393
 
180
394
  return output
@@ -0,0 +1,240 @@
1
+ import numpy as np
2
+ import random
3
+ from typing import List, Dict, Tuple, Union, Optional
4
+ from shapely.geometry import Polygon as Polygon_Shapely, LineString
5
+
6
+ from .Classes import Line, LineSegment, Polygon
7
+
8
+ def minDistance_line_point(A, B, E):
9
+ # vector AB
10
+ AB = np.array(B) - np.array(A)
11
+ EB = np.array(B) - np.array(E)
12
+ AE = np.array(E) - np.array(A)
13
+
14
+ # Calculating the dot product
15
+ AB_BE = np.dot(AB, EB)
16
+ AB_AE = np.dot(AB, AE)
17
+
18
+ # Case 1
19
+ if (AB_BE > 0):
20
+ # Finding the magnitude
21
+ y = E[1] - B[1]
22
+ x = E[0] - B[0]
23
+ reqAns = np.sqrt(x * x + y * y)
24
+
25
+ # Case 2
26
+ elif (AB_AE < 0):
27
+ y = E[1] - A[1]
28
+ x = E[0] - A[0]
29
+ reqAns = np.sqrt(x * x + y * y)
30
+
31
+ # Case 3
32
+ else:
33
+ reqAns = np.linalg.outer(AB, AE) / np.linalg.norm(AB)
34
+
35
+ return reqAns
36
+
37
+ def doLinesIntersect(line1, line2):
38
+ """
39
+ Check if two lines intersect and return the intersection point.
40
+
41
+ Args:
42
+ - line1 (Line): The first line segment.
43
+ - line2 (Line): The second line segment.
44
+
45
+ Returns:
46
+ - intersect (bool): True if the lines intersect, False otherwise.
47
+ - intersection_point (tuple or None): The intersection point (x, y) if lines intersect, None otherwise.
48
+ """
49
+
50
+ x1, y1 = line1[0]
51
+ v1, w1 = (np.cos(line1[1]), np.sin(line1[1]))
52
+
53
+ x2, y2 = line2[0]
54
+ v2, w2 = (np.cos(line2[1]), np.sin(line2[1]))
55
+
56
+ determinant = v1 * w2 - v2 * w1
57
+
58
+ if determinant == 0:
59
+ return False, (None, None)
60
+
61
+ t1 = ((x2 - x1) * w2 - (y2 - y1) * v2) / determinant
62
+ t2 = ((x2 - x1) * w1 - (y2 - y1) * v1) / determinant
63
+
64
+ intersect_x = x1 + v1 * t1
65
+ intersect_y = y2 + w2 * t2
66
+
67
+ if -1e-6 < intersect_x < 1 + 1e-6 and -1e-6 < intersect_y < 1 + 1e-6:
68
+ return True, (intersect_x, intersect_y)
69
+ else:
70
+ return False, (None, None)
71
+
72
+ def seeds(number_of_lines, radius = 0.015, number_of_trials = 10000):
73
+ Line = {}
74
+ line_id = 0
75
+
76
+ nucleation_points = [(0,0), (1,0), (1,1), (0,1)]
77
+ angle = [0,np.pi/2, np.pi, 3*np.pi/2]
78
+
79
+ Line = {'b1':[ (0,0), 0], 'b2':[ (1,0), np.pi/2], 'b3':[ (1,1), np.pi], 'b4':[ (0,1), 3*np.pi/2] }
80
+
81
+ for i in range(number_of_lines):
82
+ new_points = (random.uniform(0,1), random.uniform(0,1))
83
+
84
+ line_new_point = []
85
+ for j in range(len(nucleation_points)):
86
+ start = (np.cos(angle[i])*10+nucleation_points[i][0], np.sin(angle[i])*10+nucleation_points[i][1])
87
+ end = (-np.cos(angle[i])*10+nucleation_points[i][0], -np.sin(angle[i])*10+nucleation_points[i][1])
88
+ line_new_point += [minDistance_line_point(start, end, new_points)]
89
+
90
+ trial = 0
91
+ while sum(np.array(line_new_point) < radius) != 0 or np.sum( np.sqrt(np.sum((np.array(nucleation_points) - np.array(new_points))**2, axis = 1)) < radius) != 0:
92
+ new_points = (random.uniform(0,1), random.uniform(0,1))
93
+
94
+ line_new_point = []
95
+ for j in range(len(nucleation_points)):
96
+ start = (np.cos(angle[i])*10+nucleation_points[i][0], np.sin(angle[i])*10+nucleation_points[i][1])
97
+ end = (-np.cos(angle[i])*10+nucleation_points[i][0], -np.sin(angle[i])*10+nucleation_points[i][1])
98
+ line_new_point += [minDistance_line_point(start, end, new_points)]
99
+
100
+ trial += 1
101
+
102
+ if trial > number_of_trials:
103
+ break
104
+
105
+ if trial <= number_of_trials:
106
+ nucleation_points += [new_points]
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)
109
+ angle += [angle_new]
110
+ Line[line_id] = [ new_points ,angle_new]
111
+ line_id += 1
112
+ else:
113
+ print('jammed')
114
+ break
115
+
116
+ return Line
117
+
118
+ def all_intersection(Line):
119
+ intersections_dict = {}
120
+
121
+ for k,v in Line.items():
122
+ #intersections_dict[k] = {}
123
+ intersections_dict[k] = {'back':{}, 'front':{}}
124
+
125
+ for kk,vv in Line.items():
126
+ intersect, (intersect_x, intersect_y) = doLinesIntersect(v ,vv)
127
+
128
+ if intersect:
129
+ segment_length = np.sqrt( (v[0][0] - intersect_x)**2 + (v[0][1] - intersect_y)**2)
130
+
131
+ if intersect_x < v[0][0]:
132
+ intersections_dict[k]['back'][kk] = [(intersect_x, intersect_y), segment_length]
133
+ else:
134
+ if intersect_x == v[0][0] and intersect_y < v[0][1]:
135
+ intersections_dict[k]['back'][kk] = [(intersect_x, intersect_y), segment_length]
136
+ else:
137
+ intersections_dict[k]['front'][kk] = [(intersect_x, intersect_y), segment_length]
138
+ intersections_dict[k]['back'] = dict(sorted(intersections_dict[k]['back'].items(), key=lambda e:e[1], reverse = True))
139
+ intersections_dict[k]['front'] = dict(sorted(intersections_dict[k]['front'].items(), key=lambda e:e[1]))
140
+
141
+ return intersections_dict
142
+
143
+ def transform_to_standard_lines(segments):
144
+ data = []
145
+ for s in segments:
146
+ start = (s[3], s[4])
147
+ end = (s[5], s[6])
148
+ line = LineSegment(start=start, end=end, id=s[0], neighbors_initial=[s[1], s[2]], neighbors=None)
149
+ data.append(line)
150
+
151
+ return data
152
+
153
+ def static_line_graph_generation(seed_loc, intersections_dict):
154
+ borders = ['b1', 'b2', 'b3', 'b4']
155
+ segments = []
156
+ edges = []
157
+
158
+ for k,v in seed_loc.items():
159
+ if k not in borders:
160
+ #front
161
+ for kk_f, vv_f in intersections_dict[k]['front'].items():
162
+ try:
163
+ d_k_kk = intersections_dict[kk_f]['back'][k][1]
164
+ front_coordinate = intersections_dict[kk_f]['back'][k][0]
165
+ front_id = kk_f
166
+ where = 'back'
167
+ except:
168
+ d_k_kk = intersections_dict[kk_f]['front'][k][1]
169
+ front_coordinate = intersections_dict[kk_f]['front'][k][0]
170
+ front_id = kk_f
171
+ where = 'front'
172
+
173
+ if vv_f[1] > d_k_kk:
174
+ #check kk neighbors
175
+ boolean = []
176
+ for kkk, vvv in intersections_dict[kk_f][where].items():
177
+ if vvv[1] < d_k_kk:
178
+ try:
179
+ d_kk_kkk = intersections_dict[kkk]['back'][kk_f][1]
180
+ except:
181
+ d_kk_kkk = intersections_dict[kkk]['front'][kk_f][1]
182
+
183
+ if d_kk_kkk > vvv[1]:
184
+ boolean += [0]
185
+ else:
186
+ boolean += [1]
187
+
188
+ #print(k,kk, boolean)
189
+
190
+ if sum(boolean) == 0:
191
+ #print(k, kk, front_coordinate)
192
+ break
193
+
194
+ #back
195
+ for kk_b, vv_b in intersections_dict[k]['back'].items():
196
+ try:
197
+ d_k_kk = intersections_dict[kk_b]['back'][k][1]
198
+ back_coordinate = intersections_dict[kk_b]['back'][k][0]
199
+ back_id = kk_b
200
+ where = 'back'
201
+ except:
202
+ d_k_kk = intersections_dict[kk_b]['front'][k][1]
203
+ back_coordinate = intersections_dict[kk_b]['front'][k][0]
204
+ back_id = kk_b
205
+ where = 'front'
206
+
207
+ if vv_b[1] > d_k_kk:
208
+ boolean = []
209
+ for kkk, vvv in intersections_dict[kk_b][where].items():
210
+ if vvv[1] < d_k_kk:
211
+ try:
212
+ d_kk_kkk = intersections_dict[kkk]['back'][kk_b][1]
213
+ except:
214
+ d_kk_kkk = intersections_dict[kkk]['front'][kk_b][1]
215
+
216
+ if d_kk_kkk > vvv[1]:
217
+ boolean += [0]
218
+ else:
219
+ boolean += [1]
220
+
221
+ if sum(boolean) == 0:
222
+ break
223
+
224
+ try:
225
+ if k!= kk_f and k!= kk_b and kk_f != kk_b and (k, kk_f) not in edges and (kk_f, k) not in edges and (k, kk_b) not in edges and (kk_b, k) not in edges:
226
+ segments+= [[k, kk_f, kk_b, front_coordinate[0], front_coordinate[1], back_coordinate[0], back_coordinate[1]]]
227
+ edges += [(k,kk_f)]
228
+ edges += [(k,kk_b)]
229
+ except:
230
+ None
231
+
232
+ return segments
233
+
234
+ def generate_line_segments_thickness_static(size, seed_loc=None):
235
+ if seed_loc is None:
236
+ seed_loc = seeds(size, 0.0)
237
+ segments = static_line_graph_generation(seed_loc, all_intersection(seed_loc))
238
+ segments = transform_to_standard_lines(segments)
239
+
240
+ return segments
@@ -1,21 +0,0 @@
1
- RDG_networks/Classes.py,sha256=_9X3JPHFAYYlaC8IZ_H9__sfz99G5l9UfPl65lL60_4,7977
2
- RDG_networks/__init__.py,sha256=PnXsJcqHJ-TlwMBEV4v46jP5VE654UYeU094yP0rdh8,1149
3
- RDG_networks/draw_segments.py,sha256=U53N5GXmQHWKdM1Q1faP_EGKjc6enOu2mcsunzSFpP0,984
4
- RDG_networks/generate_line_network.py,sha256=lJ4rhObim3WcEQoebomewRQKWNJC5phFyFYRW7qjXIg,1127
5
- RDG_networks/generate_line_segments.py,sha256=QV8_k7q6TD5c7Hcb2Ms_apEdWYw4XdLr7rdJgh49v4Q,9004
6
- RDG_networks/generate_line_segments_dynamic.py,sha256=GoIhGXYbcvjqR5BJCnkvAGp8QBpzsE1ZSbl2k9XAOGI,7531
7
- RDG_networks/generate_line_segments_static.py,sha256=7KvHZi3krv-tAGydJR_gbMMmHKZ5azzrKcQe3fuWzCE,9265
8
- RDG_networks/get_intersection_segments.py,sha256=mXB5qCy1oOps4Vu1mX6flW6v_4Xxc71YK41yOWjJX8o,2797
9
- RDG_networks/sample_in_polygon.py,sha256=qpPpW-Da1vK8ZkVWMJ0zBsE8IgyMB619gCdybSkzKSQ,1605
10
- RDG_networks/save_to_stl.py,sha256=xHwuoG39cbemggoIjT44DlsMlhjlV3uxTWo6gcHEePA,3414
11
- RDG_networks/thickness/Classes.py,sha256=kgWNP5NCc11dTVAfujk30r_MsY2Xf4dAOmoalLRHa5E,8063
12
- RDG_networks/thickness/__init__.py,sha256=iKwquAaxqyCZ6qi1YSkgm2oIEuu_FjhHsqJmwUNScyc,327
13
- RDG_networks/thickness/generate_line_segments_thickness_orientation.py,sha256=7sEZX8ISTBm5Fy-jqKsvRpZkteaRxfTVPNTB3lNEUYk,7797
14
- RDG_networks/thickness/ggenerate_line_segments_thickness.py,sha256=Eho-HAFVJdYSv1xqNSE2pCR--ncLrv1ZWahTkOlCI0A,29091
15
- RDG_networks/thickness/sample_in_polygon.py,sha256=nJ-yqfoCCGfC6_EpGL3L1t1LOYdqWZd-7v5bxy6th34,1849
16
- RDG_Networks-0.3.5.dist-info/LICENSE.txt,sha256=BHUkX2GsdTr30sKmVZ1MLGR1njnx17EX_oUuuSVZZPE,598
17
- RDG_Networks-0.3.5.dist-info/METADATA,sha256=dxEztE7a_LbwlRI-Os1SbdRPRIc7gg1-kTDVPOIZv2M,2422
18
- RDG_Networks-0.3.5.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
19
- RDG_Networks-0.3.5.dist-info/entry_points.txt,sha256=f6AkERofpy7bp4KBUJo3y_ey6lZtvXxBVAMtPZZHqiI,676
20
- RDG_Networks-0.3.5.dist-info/top_level.txt,sha256=4gUUYafD5Al9V8ZSiViVGYHpRMMCsCBcGgCNodk9Syg,13
21
- RDG_Networks-0.3.5.dist-info/RECORD,,