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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,,