RDG-Networks 0.3.13__tar.gz → 0.3.15__tar.gz

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.
Files changed (28) hide show
  1. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/PKG-INFO +12 -2
  2. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_Networks.egg-info/PKG-INFO +12 -2
  3. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_Networks.egg-info/SOURCES.txt +1 -0
  4. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_Networks.egg-info/entry_points.txt +1 -0
  5. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/__init__.py +2 -0
  6. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/thickness/Classes.py +4 -2
  7. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/thickness/__init__.py +2 -0
  8. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/thickness/generate_line_segments_thickness.py +3 -1
  9. rdg_networks-0.3.15/RDG_networks/thickness/generate_line_segments_thickness_config.py +590 -0
  10. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/setup.py +2 -1
  11. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/LICENSE.txt +0 -0
  12. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_Networks.egg-info/dependency_links.txt +0 -0
  13. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_Networks.egg-info/requires.txt +0 -0
  14. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_Networks.egg-info/top_level.txt +0 -0
  15. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/Classes.py +0 -0
  16. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/draw_segments.py +0 -0
  17. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/generate_line_network.py +0 -0
  18. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/generate_line_segments.py +0 -0
  19. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/generate_line_segments_dynamic.py +0 -0
  20. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/generate_line_segments_static.py +0 -0
  21. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/get_intersection_segments.py +0 -0
  22. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/sample_in_polygon.py +0 -0
  23. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/save_data.py +0 -0
  24. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/thickness/generate_line_segments_thickness_static.py +0 -0
  25. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/thickness/orientate_network.py +0 -0
  26. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/RDG_networks/thickness/sample_in_polygon.py +0 -0
  27. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/README.md +0 -0
  28. {rdg_networks-0.3.13 → rdg_networks-0.3.15}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: RDG-Networks
3
- Version: 0.3.13
3
+ Version: 0.3.15
4
4
  Summary: RDG Networks project
5
5
  Home-page: https://github.com/NiekMooij/RDG_networks
6
6
  Author: Niek Mooij
@@ -15,6 +15,16 @@ Requires-Dist: numpy
15
15
  Requires-Dist: scipy
16
16
  Requires-Dist: shapely
17
17
  Requires-Dist: typing
18
+ Dynamic: author
19
+ Dynamic: author-email
20
+ Dynamic: classifier
21
+ Dynamic: description
22
+ Dynamic: description-content-type
23
+ Dynamic: home-page
24
+ Dynamic: license
25
+ Dynamic: license-file
26
+ Dynamic: requires-dist
27
+ Dynamic: summary
18
28
 
19
29
  ## Overview
20
30
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: RDG-Networks
3
- Version: 0.3.13
3
+ Version: 0.3.15
4
4
  Summary: RDG Networks project
5
5
  Home-page: https://github.com/NiekMooij/RDG_networks
6
6
  Author: Niek Mooij
@@ -15,6 +15,16 @@ Requires-Dist: numpy
15
15
  Requires-Dist: scipy
16
16
  Requires-Dist: shapely
17
17
  Requires-Dist: typing
18
+ Dynamic: author
19
+ Dynamic: author-email
20
+ Dynamic: classifier
21
+ Dynamic: description
22
+ Dynamic: description-content-type
23
+ Dynamic: home-page
24
+ Dynamic: license
25
+ Dynamic: license-file
26
+ Dynamic: requires-dist
27
+ Dynamic: summary
18
28
 
19
29
  ## Overview
20
30
 
@@ -20,6 +20,7 @@ RDG_networks/save_data.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_config.py
23
24
  RDG_networks/thickness/generate_line_segments_thickness_static.py
24
25
  RDG_networks/thickness/orientate_network.py
25
26
  RDG_networks/thickness/sample_in_polygon.py
@@ -6,6 +6,7 @@ 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_config = RDG_networks.thickness.generate_line_segments_thickness_config:main
9
10
  generate_line_segments_thickness_static = RDG_networks.generate_line_segments_thickness_static:main
10
11
  get_alignment_mean = RDG_networks.thickness.get_alignment_mean:main
11
12
  get_intersection_segments = RDG_networks.get_intersection_segments:main
@@ -8,6 +8,7 @@ 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_config import generate_line_segments_thickness_config
11
12
  from .thickness.orientate_network import orientate_network
12
13
  from .thickness.generate_line_segments_thickness_static import generate_line_segments_thickness_static
13
14
  from .thickness.orientate_network import translate_network
@@ -18,6 +19,7 @@ from .save_data import save_to_stl, save_to_json, load_from_json
18
19
 
19
20
  __all__ = ['generate_line_segments',
20
21
  'generate_line_segments_thickness',
22
+ 'generate_line_segments_thickness_config',
21
23
  'orientate_network',
22
24
  'translate_network',
23
25
  'clip_network',
@@ -111,7 +111,7 @@ class Polygon:
111
111
  vertices (List[Tuple[float, float]]): A list of (x, y) coordinates representing the vertices of the polygon.
112
112
  """
113
113
 
114
- def __init__(self, vertices: List[tuple], middle_segment=None, neighbors=None):
114
+ def __init__(self, vertices: List[tuple], middle_segment=None, neighbors=None, neighbors_initial=None):
115
115
  """
116
116
  Initializes a Polygon instance with the provided vertices.
117
117
 
@@ -121,6 +121,7 @@ class Polygon:
121
121
  self.vertices = vertices
122
122
  self.middle_segment = middle_segment
123
123
  self.neighbors = neighbors
124
+ self.neighbors_initial = neighbors_initial
124
125
 
125
126
  def area(self) -> float:
126
127
  """
@@ -205,7 +206,8 @@ class Polygon:
205
206
  return {
206
207
  'vertices': self.vertices,
207
208
  'middle_segment': self.middle_segment.to_dict(),
208
- 'neighbors': self.neighbors
209
+ 'neighbors': self.neighbors,
210
+ 'neighbors_initial': self.neighbors_initial
209
211
  }
210
212
 
211
213
  class Cycle:
@@ -1,6 +1,7 @@
1
1
  # __init__.py
2
2
 
3
3
  from .generate_line_segments_thickness import generate_line_segments_thickness
4
+ from .generate_line_segments_thickness_config import generate_line_segments_thickness_config
4
5
  from .orientate_network import orientate_network
5
6
  from .generate_line_segments_thickness_static import generate_line_segments_thickness_static
6
7
  from .orientate_network import translate_network
@@ -10,6 +11,7 @@ from .orientate_network import get_alignment_mean
10
11
 
11
12
  __all__ = [
12
13
  'generate_line_segments_thickness',
14
+ 'generate_line_segments_thickness_config',
13
15
  'orientate_network',
14
16
  'generate_line_segments_thickness_static',
15
17
  'translate_network',
@@ -441,7 +441,9 @@ def update_data(
441
441
  neighbors = [ i[:-2] if i not in ['b1', 'b2', 'b3', 'b4'] else i for i in neighbors ]
442
442
  neighbors = list(set(neighbors))
443
443
 
444
- segment_thickness_dict[len(segment_thickness_dict) + 1] = Polygon(vertices=vertices0, neighbors=neighbors)
444
+ neighbors_initial = list(set(neighbors))
445
+
446
+ segment_thickness_dict[len(segment_thickness_dict) + 1] = Polygon(vertices=vertices0, neighbors=neighbors, neighbors_initial=neighbors_initial)
445
447
 
446
448
  for n in neighbors:
447
449
  if n in ['b1', 'b2', 'b3', 'b4']:
@@ -0,0 +1,590 @@
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
+ ) -> Union[Tuple[str, Dict[str, object], Tuple[float, float], np.ndarray, np.ndarray], bool]:
149
+
150
+ direction = (np.cos(angle), np.sin(angle))
151
+ direction = np.array(direction) / np.linalg.norm(direction)
152
+
153
+ # Try to find a valid location and direction up to max_attempts
154
+ for polygon_id, polygon in polygon_arr.items():
155
+ if is_inside_polygon(polygon['vertices'], nucleation_point):
156
+ break
157
+
158
+ # Compute the perpendicular vector to the direction
159
+ perpendicular = np.array([direction[1], -direction[0]])
160
+ perpendicular = perpendicular / np.linalg.norm(perpendicular)
161
+
162
+ # Ensure the perpendicular vector is oriented consistently (y-component is non-negative)
163
+ if perpendicular[1] < 0:
164
+ perpendicular = -perpendicular
165
+
166
+ # Compute the positions for the segment with thickness, shifted by half-thickness along the perpendicular direction
167
+ p1 = np.array(nucleation_point) + (thickness/2 + min_distance) * perpendicular
168
+ p2 = np.array(nucleation_point) - (thickness/2 + min_distance) * perpendicular
169
+
170
+ # Check if both endpoints of the segment are inside the polygon
171
+ if is_inside_polygon(polygon['vertices'], p1) and is_inside_polygon(polygon['vertices'], p2):
172
+ return polygon_id, polygon, nucleation_point, direction, perpendicular, angle
173
+
174
+ # If no valid location and direction is found, return False
175
+ return False
176
+
177
+ 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):
178
+ # Extract vertices and cycle (faces) of the original polygon
179
+ vertices = polygon_arr[polygon_id]['vertices']
180
+ cycle = polygon_arr[polygon_id]['faces']
181
+
182
+ # Get first cycle and vertices
183
+ index_start_1, index_end_1 = (cycle.index(neighbor1_1), cycle.index(neighbor1_2))
184
+ if index_start_1 < index_end_1:
185
+ cycle1 = [segment_new_id_1] + cycle[index_start_1:index_end_1+1]
186
+ vertices1 = [vertex_begin_1] + vertices[index_start_1:index_end_1] + [vertex_end_1]
187
+ else:
188
+ cycle1 = [segment_new_id_1] + cycle[index_start_1:] + cycle[:index_end_1+1]
189
+ vertices1 = [vertex_begin_1] + vertices[index_start_1:] + vertices[:index_end_1] + [vertex_end_1]
190
+
191
+ # Get second cycle and vertices
192
+ index_start_2, index_end_2 = (cycle.index(neighbor2_2), cycle.index(neighbor2_1))
193
+ if index_start_2 < index_end_2:
194
+ cycle2 = [segment_new_id_2] + cycle[index_start_2:index_end_2+1]
195
+ vertices2 = [vertex_end_2] + vertices[index_start_2:index_end_2] + [vertex_begin_2]
196
+ else:
197
+ cycle2 = [segment_new_id_2] + cycle[index_start_2:] + cycle[:index_end_2+1]
198
+ vertices2 = [vertex_end_2] + vertices[index_start_2:] + vertices[:index_end_2] + [vertex_begin_2]
199
+
200
+ # Get middle cycle and vertices
201
+ cycle0 = [neighbor1_1, segment_new_id_1, neighbor1_2]
202
+ vertices0 = [vertex_begin_1, vertex_end_1]
203
+
204
+ index_start_0, index_end_0 = (cycle.index(neighbor1_2), cycle.index(neighbor2_2))
205
+ if index_start_0 < index_end_0:
206
+ cycle0 = cycle0 + cycle[index_start_0:index_end_0+1]
207
+ vertices0 = vertices0 + vertices[index_start_0:index_end_0]
208
+
209
+ elif index_start_0 > index_end_0:
210
+ cycle0 = cycle0 + cycle[index_start_0:] + cycle[:index_end_0+1]
211
+ vertices0 = vertices0 + vertices[index_start_0:] + vertices[:index_end_0]
212
+
213
+ cycle0 = cycle0 + [segment_new_id_2]
214
+ vertices0 = vertices0 + [vertex_end_2] + [vertex_begin_2]
215
+
216
+ index_start_0, index_end_0 = (cycle.index(neighbor2_1), cycle.index(neighbor1_1))
217
+ if index_start_0 < index_end_0:
218
+ cycle0 = cycle0 + cycle[index_start_0:index_end_0+1]
219
+ vertices0 = vertices0 + vertices[index_start_0:index_end_0]
220
+
221
+ elif index_start_0 > index_end_0:
222
+ cycle0 = cycle0 + cycle[index_start_0:] + cycle[:index_end_0+1]
223
+ vertices0 = vertices0 + vertices[index_start_0:] + vertices[:index_end_0]
224
+
225
+ return cycle0, vertices0, cycle1, vertices1, cycle2, vertices2
226
+
227
+ def get_new_segment(
228
+ line_segments_to_check: List[LineSegment],
229
+ location: Tuple[float, float],
230
+ direction: Tuple[float, float],
231
+ id: Optional[int] = None,
232
+ box_size: float = 1
233
+ ) -> LineSegment:
234
+ """
235
+ Creates a new line segment by extending a given location in a specified direction and
236
+ determines its neighbors by checking intersections with other line segments.
237
+
238
+ Args:
239
+ line_segments_to_check (List[LineSegment]): List of existing line segments to check for intersections.
240
+ location (Tuple[float, float]): The starting point (x, y) for the new line segment.
241
+ direction (Tuple[float, float]): The direction vector in which to extend the line segment.
242
+ id (Optional[int]): Optional ID for the new line segment. If not provided, defaults to None.
243
+ box_size(optional[Int]]): The size of the box. Defaults to 1.
244
+
245
+ Returns:
246
+ LineSegment: A new line segment object with its neighbors based on intersections.
247
+ """
248
+
249
+ # Create a temporary line segment extending from the location in both directions
250
+ 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))
251
+
252
+ intersection_points = []
253
+
254
+ # Check for intersections with existing line segments
255
+ for segment in line_segments_to_check:
256
+ intersect, (intersect_x, intersect_y) = doSegmentsIntersect(s_temp, segment, box_size)
257
+
258
+ if intersect:
259
+ segment_length = math.sqrt(
260
+ (location[0] - intersect_x) ** 2
261
+ + (location[1] - intersect_y) ** 2
262
+ )
263
+ intersection_points.append(
264
+ {"id": segment.id, "point": (intersect_x, intersect_y), "segment_length": segment_length}
265
+ )
266
+
267
+ # Divide intersections into ones behind and in front of the new line
268
+ intersections_b = [intersection for intersection in intersection_points if intersection["point"][0] < location[0]]
269
+ intersections_f = [intersection for intersection in intersection_points if intersection["point"][0] > location[0]]
270
+
271
+ if not intersections_b or not intersections_f:
272
+ intersections_b = [intersection for intersection in intersection_points if intersection["point"][1] < location[1]]
273
+ intersections_f = [intersection for intersection in intersection_points if intersection["point"][1] > location[1]]
274
+
275
+ # Determine the closest intersections for segment start and end
276
+ s_start = min(intersections_b, key=lambda x: x["segment_length"])
277
+ s_end = min(intersections_f, key=lambda x: x["segment_length"])
278
+ start, end = s_start['point'], s_end['point']
279
+ start_id, end_id = s_start['id'], s_end['id']
280
+
281
+ # Ensure the start comes before the end
282
+ if start[0] > end[0]:
283
+ start, end = end, start
284
+ start_id, end_id = end_id, start_id
285
+
286
+ # Create a new line segment and assign neighbors
287
+ neighbors_initial = {start_id: start, end_id: end}
288
+ segment_new = LineSegment(start=start, end=end, id=id, neighbors_initial=neighbors_initial, neighbors=neighbors_initial)
289
+
290
+ return segment_new
291
+
292
+ def update_data(
293
+ segments_dict: Dict[int, LineSegment],
294
+ polygon_arr: Dict[str, Dict[str, object]],
295
+ polygon_id: str,
296
+ segment_thickness_dict: Dict[int, Polygon],
297
+ vertices0: List[Tuple[float, float]],
298
+ vertices1: List[Tuple[float, float]],
299
+ vertices2: List[Tuple[float, float]],
300
+ cycle0: List[int],
301
+ cycle1: List[int],
302
+ cycle2: List[int],
303
+ neighbor1_1: int,
304
+ neighbor1_2: int,
305
+ neighbor2_1: int,
306
+ neighbor2_2: int,
307
+ vertex_begin_1: Tuple[float, float],
308
+ vertex_end_1: Tuple[float, float],
309
+ vertex_begin_2: Tuple[float, float],
310
+ vertex_end_2: Tuple[float, float],
311
+ id_1: int,
312
+ id_2: int
313
+ ) -> Tuple[Dict[int, LineSegment], Dict[str, Dict[str, object]], Dict[int, Polygon]]:
314
+ """
315
+ Updates the segments, polygons, and segment thickness dictionaries by adding new data derived
316
+ from provided vertices and neighbor information.
317
+
318
+ Args:
319
+ segments_dict (Dict[int, LineSegment]): A dictionary of segments with segment ID as the key.
320
+ polygon_arr (Dict[str, Dict[str, object]]): A dictionary of polygons with polygon ID as the key.
321
+ polygon_id (str): The ID of the polygon being updated.
322
+ segment_thickness_dict (Dict[int, Polygon]): A dictionary mapping thickness information to polygon objects.
323
+ vertices0 (List[Tuple[float, float]]): Vertices of the base polygon.
324
+ vertices1 (List[Tuple[float, float]]): Vertices of the first new polygon.
325
+ vertices2 (List[Tuple[float, float]]): Vertices of the second new polygon.
326
+ cycle0 (List[int]): List of face indices for the base polygon.
327
+ cycle1 (List[int]): List of face indices for the first new polygon.
328
+ cycle2 (List[int]): List of face indices for the second new polygon.
329
+ neighbor1_1 (int): ID of the first neighbor of the first segment.
330
+ neighbor1_2 (int): ID of the second neighbor of the first segment.
331
+ neighbor2_1 (int): ID of the first neighbor of the second segment.
332
+ neighbor2_2 (int): ID of the second neighbor of the second segment.
333
+ vertex_begin_1 (Tuple[float, float]): Starting vertex of the first segment.
334
+ vertex_end_1 (Tuple[float, float]): Ending vertex of the first segment.
335
+ vertex_begin_2 (Tuple[float, float]): Starting vertex of the second segment.
336
+ vertex_end_2 (Tuple[float, float]): Ending vertex of the second segment.
337
+ id_1 (int): ID of the first new segment.
338
+ id_2 (int): ID of the second new segment.
339
+
340
+ Returns:
341
+ Tuple[Dict[int, LineSegment], Dict[str, Dict[str, object]], Dict[int, Polygon]]:
342
+ - Updated dictionary of line segments.
343
+ - Updated dictionary of polygons.
344
+ - Updated dictionary of segment thickness.
345
+ """
346
+
347
+ # Update polygon_arr (a dictionary of polygons)
348
+ polygon_new_1 = {
349
+ f'p{len(polygon_arr) + 1}': {
350
+ 'vertices': vertices1,
351
+ 'area': Polygon(vertices=vertices1).area(),
352
+ 'faces': cycle1
353
+ }
354
+ }
355
+ polygon_new_2 = {
356
+ polygon_id: {
357
+ 'vertices': vertices2,
358
+ 'area': Polygon(vertices=vertices2).area(),
359
+ 'faces': cycle2
360
+ }
361
+ }
362
+ polygon_arr.update(polygon_new_1)
363
+ polygon_arr.update(polygon_new_2)
364
+
365
+ # Update the segments_dict for the first segment
366
+ neighbors_initial_1 = {
367
+ neighbor1_1: vertex_begin_1,
368
+ neighbor1_2: vertex_end_1
369
+ }
370
+ segment_new_1 = LineSegment(
371
+ start=vertex_begin_1,
372
+ end=vertex_end_1,
373
+ id=id_1,
374
+ neighbors_initial=neighbors_initial_1,
375
+ neighbors=neighbors_initial_1
376
+ )
377
+ segments_dict[segment_new_1.id] = segment_new_1
378
+ segments_dict[neighbor1_1].neighbors[id_1] = vertex_begin_1
379
+ segments_dict[neighbor1_2].neighbors[id_1] = vertex_end_1
380
+
381
+ # Update the segments_dict for the second segment
382
+ neighbors_initial_2 = {
383
+ neighbor2_1: vertex_begin_2,
384
+ neighbor2_2: vertex_end_2
385
+ }
386
+ segment_new_2 = LineSegment(
387
+ start=vertex_begin_2,
388
+ end=vertex_end_2,
389
+ id=id_2,
390
+ neighbors_initial=neighbors_initial_2,
391
+ neighbors=neighbors_initial_2
392
+ )
393
+ segments_dict[segment_new_2.id] = segment_new_2
394
+ segments_dict[neighbor2_1].neighbors[id_2] = vertex_begin_2
395
+ segments_dict[neighbor2_2].neighbors[id_2] = vertex_end_2
396
+
397
+ # Update the segment_thickness_dict with the base polygon
398
+ neighbors = cycle0.copy()
399
+ neighbors.remove(f'{len(segment_thickness_dict)+1}_1')
400
+ neighbors.remove(f'{len(segment_thickness_dict)+1}_2')
401
+ neighbors = [ i[:-2] if i not in ['b1', 'b2', 'b3', 'b4'] else i for i in neighbors ]
402
+ neighbors = list(set(neighbors))
403
+
404
+ neighbors_initial = list(set(neighbors))
405
+
406
+ segment_thickness_dict[len(segment_thickness_dict) + 1] = Polygon(vertices=vertices0, neighbors=neighbors, neighbors_initial=neighbors_initial)
407
+
408
+ for n in neighbors:
409
+ if n in ['b1', 'b2', 'b3', 'b4']:
410
+ continue
411
+
412
+ else:
413
+ segment_thickness_dict[int(n)].neighbors.append(len(segment_thickness_dict) + 1)
414
+
415
+ return segments_dict, polygon_arr, segment_thickness_dict
416
+
417
+ def add_line_segment(
418
+ segments_dict: Dict[int, LineSegment],
419
+ polygon_arr: Dict[str, Dict[str, object]],
420
+ segment_thickness_dict: Dict[int, Polygon],
421
+ angle: float,
422
+ thickness: float = 0,
423
+ nucleation_point: Tuple[float, float] = None,
424
+ min_distance: float = 0,
425
+ box_size: float = 1,
426
+ ) -> Union[Tuple[Dict[int, LineSegment], Dict[str, Dict[str, object]], Dict[int, Polygon], List[float], float], bool]:
427
+ """
428
+ Adds a new line segment to the segments and polygon data structures, with a given thickness and angle distribution.
429
+
430
+ Args:
431
+ segments_dict (Dict[int, LineSegment]): A dictionary containing the current line segments.
432
+ polygon_arr (Dict[str, Dict[str, object]]): A dictionary containing the current polygons and their properties.
433
+ segment_thickness_dict (Dict[int, Polygon]): A dictionary storing the thickness information mapped to polygons.
434
+ thickness (float): The thickness of the new segment to be added. Defaults to 0.
435
+ angles (str): The angle distribution method. Defaults to 'uniform'.
436
+ nucleation_point (Tuple[float, float]): A predefined nucleation point for the new segment. Defaults to None.
437
+ min_distance (float): The minimum distance between two lines. Defaults to 0.
438
+ box_size (float): The size of the box. Defaults to 1.
439
+ max_attempts (int): The maximum number of attempts to find a valid location and direction. Defaults to 1000.
440
+
441
+ Returns:
442
+ Union[Tuple[Dict[int, LineSegment], Dict[str, Dict[str, object]], Dict[int, Polygon]], List[float], float, bool]:
443
+ - A tuple containing the updated segments dictionary, polygon dictionary, and thickness dictionary,
444
+ or False if no valid location for the new segment is found.
445
+ -nucleation point in a list [x,y] and the angle of the segment in radians.
446
+ """
447
+
448
+ # Get a valid location and direction, or return False if none is found
449
+ loc = get_location_and_direction(polygon_arr, angle=angle, thickness=thickness, nucleation_point=nucleation_point, min_distance=min_distance)
450
+ if loc:
451
+ polygon_id, polygon, location_new, direction_new, perpendicular, angle_new = loc
452
+ else:
453
+ print('Configuration is not feasible. Skipped point.')
454
+ return False
455
+
456
+ # Get the borders of the new segment with the given thickness
457
+ line_segments_to_check = [segments_dict[segment] for segment in polygon['faces']]
458
+ middle_segment = get_new_segment(line_segments_to_check, location=location_new, direction=direction_new, box_size=box_size)
459
+ s1 = get_new_segment(line_segments_to_check, location=np.array(location_new) + thickness * perpendicular / 2, direction=direction_new,box_size=box_size)
460
+ s2 = get_new_segment(line_segments_to_check, location=np.array(location_new) - thickness * perpendicular / 2, direction=direction_new, box_size=box_size)
461
+
462
+ # Extract neighbor information and segment vertices
463
+ neighbor1_1, neighbor1_2 = list(s1.neighbors.keys())
464
+ vertex_begin_1, vertex_end_1 = list(s1.neighbors.values())
465
+ neighbor2_1, neighbor2_2 = list(s2.neighbors.keys())
466
+ vertex_begin_2, vertex_end_2 = list(s2.neighbors.values())
467
+ id_1 = str(int((len(segments_dict.keys()) - 2) / 2)) + '_1'
468
+ id_2 = str(int((len(segments_dict.keys()) - 2) / 2)) + '_2'
469
+
470
+ # Get the resulting polygons after splitting
471
+ cycle0, vertices0, cycle1, vertices1, cycle2, vertices2 = get_polygons(
472
+ polygon_id,
473
+ polygon_arr,
474
+ neighbor1_1,
475
+ neighbor1_2,
476
+ vertex_begin_1,
477
+ vertex_end_1,
478
+ neighbor2_1,
479
+ neighbor2_2,
480
+ vertex_begin_2=vertex_begin_2,
481
+ vertex_end_2=vertex_end_2,
482
+ segment_new_id_1=id_1,
483
+ segment_new_id_2=id_2
484
+ )
485
+
486
+ # Update all relevant data structures
487
+ segments_dict, polygon_arr, segment_thickness_dict = update_data(
488
+ segments_dict,
489
+ polygon_arr,
490
+ polygon_id,
491
+ segment_thickness_dict,
492
+ vertices0,
493
+ vertices1,
494
+ vertices2,
495
+ cycle0,
496
+ cycle1,
497
+ cycle2,
498
+ neighbor1_1,
499
+ neighbor1_2,
500
+ neighbor2_1,
501
+ neighbor2_2,
502
+ vertex_begin_1,
503
+ vertex_end_1,
504
+ vertex_begin_2,
505
+ vertex_end_2,
506
+ id_1,
507
+ id_2
508
+ )
509
+
510
+ # Associate the middle segment with the newly created thickness entry
511
+ segment_thickness_dict[list(segment_thickness_dict.keys())[-1]].middle_segment = middle_segment
512
+
513
+ return segments_dict, polygon_arr, segment_thickness_dict, location_new, angle_new
514
+
515
+ def generate_line_segments_thickness_config(
516
+ size: int,
517
+ thickness_arr: List[float],
518
+ angles: str = 'uniform',
519
+ nucleation_points = None,
520
+ epsilon: float = 0,
521
+ box_size: float = 1
522
+ ) -> Tuple[Dict[str, LineSegment], Dict[str, Dict[str, object]], Dict[int, Polygon], np.ndarray]:
523
+ """
524
+ Generates a specified number of line segments and updates the polygon and segment thickness dictionaries.
525
+
526
+ Args:
527
+ size (int): The number of line segments to generate.
528
+ thickness_arr (List[float]): A list containing the thickness values for each segment to be generated.
529
+ angles (str): Angle used in the generation of the segments.
530
+ nucleation_points
531
+ epsilon (float): the minimum distance between two line.
532
+ box_size (float): the size of the box.
533
+
534
+ Returns:
535
+ Tuple[Dict[str, LineSegment], Dict[str, Dict[str, object]], Dict[int, Polygon]]:
536
+ - Updated dictionary of line segments.
537
+ - Updated dictionary of polygons.
538
+ - Updated dictionary of segment thicknesses.
539
+ - Array of the nucleation points and angles [x,y,theta].
540
+ """
541
+
542
+ # Initialize border segments for a square and its polygon representation
543
+ borders = [
544
+ 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)}),
545
+ 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)}),
546
+ 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)}),
547
+ 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)})
548
+ ]
549
+
550
+ polygon_arr = {
551
+ 'p1': {
552
+ 'vertices': [(0, 0), (0, box_size), (box_size, box_size), (box_size, 0)],
553
+ 'area': box_size**2,
554
+ 'faces': ['b1', 'b2', 'b3', 'b4']
555
+ }
556
+ }
557
+
558
+ segments = borders
559
+ segments_dict = {segment.id: segment for segment in segments}
560
+ segment_thickness_dict = {}
561
+ generated_config = []
562
+
563
+ for i in range(size):
564
+ nucleation_point = nucleation_points[i]
565
+ angle = angles[i]
566
+ thickness = thickness_arr[i]
567
+
568
+ output = add_line_segment(segments_dict,
569
+ polygon_arr,
570
+ segment_thickness_dict,
571
+ thickness=thickness,
572
+ min_distance = epsilon,
573
+ nucleation_point = nucleation_point,
574
+ angle=angle,
575
+ box_size=box_size)
576
+ if output:
577
+ segments_dict, polygon_arr, segment_thickness_dict, location, angle = output
578
+ generated_config.append({ 'location': location, 'angle': angle, 'thickness': thickness_arr[i] })
579
+ nucleation_points.append(location)
580
+
581
+ percentage = np.round(i / size * 100, 3)
582
+ print(f'generate_segments: {percentage}% done', end='\r')
583
+
584
+ data_dict = {'segments_dict': segments_dict,
585
+ 'polygon_arr': polygon_arr,
586
+ 'segment_thickness_dict': segment_thickness_dict,
587
+ 'jammed': 'NA',
588
+ 'generated_config': generated_config}
589
+
590
+ return data_dict
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='RDG-Networks',
5
- version='0.3.13',
5
+ version='0.3.15',
6
6
  author='Niek Mooij',
7
7
  author_email='mooij.niek@gmail.com',
8
8
  description='RDG Networks project',
@@ -25,6 +25,7 @@ setup(
25
25
  'console_scripts': [
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
+ 'generate_line_segments_thickness_config=RDG_networks.thickness.generate_line_segments_thickness_config:main',
28
29
 
29
30
  'orientate_network=RDG_networks.thickness.orientate_network:main',
30
31
 
File without changes
File without changes
File without changes