@@ -34,6 +34,7 @@ def add_constraint(self, constraint: GeoCon):
34
34
if not isinstance (constraint , DistanceConstraint ):
35
35
raise ValueError ("The first constraint in a constraint cluster must be a distance constraint" )
36
36
constraint .child_nodes [0 ].root = True
37
+ constraint .child_nodes [1 ].rotation_handle = True
37
38
38
39
if constraint .param () is not None :
39
40
constraint .param ().gcs = self
@@ -95,10 +96,10 @@ def add_constraint(self, constraint: GeoCon):
95
96
# if edge_data_p12:
96
97
# networkx.set_edge_attributes(self, {(constraint.start_point, constraint.vertex): constraint}, name="angle")
97
98
# return
98
- if edge_data_p21 and not angle_in_p21 and not (constraint .vertex .root and "distance" in edge_data_p21 .keys ()):
99
+ if edge_data_p21 and not angle_in_p21 and not (constraint .vertex .root and "distance" in edge_data_p21 .keys () and constraint . start_point . rotation_handle ):
99
100
networkx .set_edge_attributes (self , {(constraint .vertex , constraint .start_point ): constraint }, name = "angle" )
100
101
return
101
- if edge_data_p23 and not angle_in_p23 and not (constraint .vertex .root and "distance" in edge_data_p23 .keys ()):
102
+ if edge_data_p23 and not angle_in_p23 and not (constraint .vertex .root and "distance" in edge_data_p23 .keys () and constraint . end_point . rotation_handle ):
102
103
networkx .set_edge_attributes (self , {(constraint .vertex , constraint .end_point ): constraint }, name = "angle" )
103
104
return
104
105
# if edge_data_p32:
@@ -107,38 +108,48 @@ def add_constraint(self, constraint: GeoCon):
107
108
108
109
if len (in_edges_p1 ) > 0 :
109
110
# if angle_in_p12 or angle_in_p21:
110
- if angle_in_p21 :
111
+ if angle_in_p21 and not constraint . end_point . rotation_handle :
111
112
self .add_edge (constraint .vertex , constraint .end_point , angle = constraint )
112
113
return
113
114
# if angle_in_p23 or angle_in_p32:
114
115
if angle_in_p23 :
115
116
raise ValueError ("Cannot create a valid angle constraint from this case" )
116
- self .add_edge (constraint .vertex , constraint .end_point , angle = constraint )
117
- return
117
+ if constraint .vertex not in [nbr for nbr in self .neighbors (constraint .end_point )]:
118
+ self .add_edge (constraint .vertex , constraint .end_point , angle = constraint )
119
+ return
118
120
if len (in_edges_p2 ) > 0 :
119
121
# if angle_in_p12 or angle_in_p21:
120
- if angle_in_p21 :
122
+ if angle_in_p21 and not constraint . end_point . rotation_handle :
121
123
self .add_edge (constraint .vertex , constraint .end_point , angle = constraint )
122
124
return
123
125
# if angle_in_p23 or angle_in_p32:
124
- if angle_in_p23 :
126
+ if angle_in_p23 and not constraint . start_point . rotation_handle :
125
127
self .add_edge (constraint .vertex , constraint .start_point , angle = constraint )
126
128
return
127
- self .add_edge (constraint .vertex , constraint .end_point , angle = constraint )
128
- return
129
+ if constraint .vertex not in [nbr for nbr in self .neighbors (constraint .end_point )]:
130
+ self .add_edge (constraint .vertex , constraint .end_point , angle = constraint )
131
+ return
129
132
if len (in_edges_p3 ) > 0 :
130
133
# if angle_in_p12 or angle_in_p21:
131
134
if angle_in_p21 :
132
135
raise ValueError ("Cannot create a valid angle constraint from this case" )
133
136
# if angle_in_p23 or angle_in_p32:
134
- if angle_in_p23 :
137
+ if angle_in_p23 and not constraint . start_point . rotation_handle :
135
138
self .add_edge (constraint .vertex , constraint .start_point , angle = constraint )
136
139
return
140
+ if constraint .vertex not in [nbr for nbr in self .neighbors (constraint .start_point )]:
141
+ self .add_edge (constraint .vertex , constraint .start_point , angle = constraint )
142
+ return
143
+
144
+ if not constraint .end_point .rotation_handle and constraint .vertex not in [nbr for nbr in self .neighbors (constraint .end_point )]:
145
+ self .add_edge (constraint .vertex , constraint .end_point , angle = constraint )
146
+ return
147
+ if not constraint .start_point .rotation_handle and constraint .vertex not in [nbr for nbr in self .neighbors (constraint .start_point )]:
137
148
self .add_edge (constraint .vertex , constraint .start_point , angle = constraint )
138
149
return
139
150
140
- self . add_edge ( constraint . vertex , constraint . end_point , angle = constraint )
141
- return
151
+ raise ValueError ( "Relative angle constraint could not be created" )
152
+
142
153
elif isinstance (constraint , AntiParallel3Constraint ) or isinstance (constraint , Perp3Constraint ):
143
154
in_edges_p1 = tuple ([edge for edge in self .in_edges (nbunch = constraint .p1 , data = True )])
144
155
in_edges_p2 = tuple ([edge for edge in self .in_edges (nbunch = constraint .p2 , data = True )])
@@ -162,10 +173,10 @@ def add_constraint(self, constraint: GeoCon):
162
173
# if edge_data_p12:
163
174
# networkx.set_edge_attributes(self, {(constraint.p1, constraint.p2): constraint}, name="angle")
164
175
# return
165
- if edge_data_p21 and not angle_in_p21 and not (constraint .p2 .root and "distance" in edge_data_p21 .keys ()):
176
+ if edge_data_p21 and not angle_in_p21 and not (constraint .p2 .root and "distance" in edge_data_p21 .keys () and constraint . p1 . rotation_handle ):
166
177
networkx .set_edge_attributes (self , {(constraint .p2 , constraint .p1 ): constraint }, name = "angle" )
167
178
return
168
- if edge_data_p23 and not angle_in_p23 and not (constraint .p2 .root and "distance" in edge_data_p23 .keys ()):
179
+ if edge_data_p23 and not angle_in_p23 and not (constraint .p2 .root and "distance" in edge_data_p23 .keys () and constraint . p3 . rotation_handle ):
169
180
networkx .set_edge_attributes (self , {(constraint .p2 , constraint .p3 ): constraint }, name = "angle" )
170
181
return
171
182
# if edge_data_p32:
@@ -174,44 +185,51 @@ def add_constraint(self, constraint: GeoCon):
174
185
175
186
if len (in_edges_p1 ) > 0 :
176
187
# if angle_in_p12 or angle_in_p21:
177
- if angle_in_p21 :
188
+ if angle_in_p21 and not constraint . p3 . rotation_handle :
178
189
self .add_edge (constraint .p2 , constraint .p3 , angle = constraint )
179
190
return
180
191
# if angle_in_p23 or angle_in_p32:
181
192
if angle_in_p23 :
182
193
raise ValueError ("Cannot create a valid angle constraint from this case" )
183
- self .add_edge (constraint .p2 , constraint .p3 , angle = constraint )
184
- return
194
+ if not constraint .p3 .rotation_handle :
195
+ self .add_edge (constraint .p2 , constraint .p3 , angle = constraint )
196
+ return
185
197
if len (in_edges_p2 ) > 0 :
186
198
# if angle_in_p12 or angle_in_p21:
187
- if angle_in_p21 :
199
+ if angle_in_p21 and not constraint . p3 . rotation_handle :
188
200
self .add_edge (constraint .p2 , constraint .p3 , angle = constraint )
189
201
return
190
202
# if angle_in_p23 or angle_in_p32:
191
- if angle_in_p23 :
203
+ if angle_in_p23 and not constraint . p1 . rotation_handle :
192
204
self .add_edge (constraint .p2 , constraint .p1 , angle = constraint )
193
205
return
194
- self .add_edge (constraint .p2 , constraint .p3 , angle = constraint )
195
- return
206
+ if not constraint .p3 .rotation_handle :
207
+ self .add_edge (constraint .p2 , constraint .p3 , angle = constraint )
208
+ return
196
209
if len (in_edges_p3 ) > 0 :
197
210
# if angle_in_p12 or angle_in_p21:
198
211
if angle_in_p21 :
199
212
raise ValueError ("Cannot create a valid angle constraint from this case" )
200
213
# if angle_in_p23 or angle_in_p32:
201
- if angle_in_p23 :
214
+ if angle_in_p23 and not constraint . p1 . rotation_handle :
202
215
self .add_edge (constraint .p2 , constraint .p1 , angle = constraint )
203
216
return
204
- self .add_edge (constraint .p2 , constraint .p1 , angle = constraint )
217
+ if not constraint .p1 .rotation_handle :
218
+ self .add_edge (constraint .p2 , constraint .p1 , angle = constraint )
219
+ return
220
+
221
+ if constraint .p1 .rotation_handle :
222
+ self .add_edge (constraint .p2 , constraint .p3 , angle = constraint )
205
223
return
224
+ if constraint .p3 .rotation_handle :
225
+ self .add_edge (constraint .p2 , constraint .p1 , angle = constraint )
206
226
207
- self .add_edge (constraint .p2 , constraint .p3 , angle = constraint )
208
- return
209
227
elif isinstance (constraint , SymmetryConstraint ):
210
- self .solve_symmetry_constraint (constraint )
211
- self .update_canvas_items (constraint )
228
+ points_solved = self .solve_symmetry_constraint (constraint )
229
+ self .update_canvas_items (points_solved )
212
230
elif isinstance (constraint , ROCurvatureConstraint ):
213
- self .solve_roc_constraint (constraint )
214
- self .update_canvas_items (constraint )
231
+ points_solved = self .solve_roc_constraint (constraint )
232
+ self .update_canvas_items (points_solved )
215
233
216
234
def remove_constraint (self , constraint : GeoCon ):
217
235
raise NotImplementedError ("Constraint removal not yet implemented" )
@@ -221,6 +239,8 @@ def make_point_copies(self):
221
239
222
240
def solve (self , source : GeoCon ):
223
241
points_solved = []
242
+ symmetry_points_solved = []
243
+ roc_points_solved = []
224
244
if isinstance (source , DistanceConstraint ):
225
245
edge_data_p12 = self .get_edge_data (source .p1 , source .p2 )
226
246
if edge_data_p12 and edge_data_p12 ["distance" ] is source :
@@ -280,18 +300,31 @@ def solve(self, source: GeoCon):
280
300
rotation_point = source .vertex
281
301
282
302
if edge_data_p21 and "angle" in edge_data_p21 and edge_data_p21 ["angle" ] is source :
283
- pass
303
+ start = source . start_point if not source . vertex . root else source . vertex
284
304
elif edge_data_p23 and "angle" in edge_data_p23 and edge_data_p23 ["angle" ] is source :
285
- pass
305
+ start = source . end_point if not source . vertex . root else source . vertex
286
306
d_angle *= - 1
287
307
else :
288
308
raise ValueError ("Somehow no angle constraint found between the three points" )
289
309
290
310
rotation_mat = np .array ([[np .cos (d_angle ), - np .sin (d_angle )], [np .sin (d_angle ), np .cos (d_angle )]])
291
311
rotation_point_mat = np .array ([[rotation_point .x ().value ()], [rotation_point .y ().value ()]])
292
312
293
- for point in networkx .bfs_tree (self , source = source .vertex ):
294
- if point is source .vertex :
313
+ # Get all the points that might need to rotate
314
+ all_downstream_points = []
315
+ rotation_handle = None
316
+ for point in networkx .bfs_tree (self , source = start ):
317
+ all_downstream_points .append (point )
318
+ if point .rotation_handle :
319
+ rotation_handle = point
320
+
321
+ # Get the branch to cut, if there is one
322
+ root_rotation_branch = []
323
+ if source .vertex .root and rotation_handle is not None :
324
+ root_rotation_branch = [point for point in networkx .bfs_tree (self , source = rotation_handle )]
325
+
326
+ for point in all_downstream_points :
327
+ if point is source .vertex or point in root_rotation_branch :
295
328
continue
296
329
dx_dy = np .array ([[point .x ().value () - rotation_point .x ().value ()],
297
330
[point .y ().value () - rotation_point .y ().value ()]])
@@ -302,16 +335,21 @@ def solve(self, source: GeoCon):
302
335
points_solved .append (point )
303
336
304
337
elif isinstance (source , SymmetryConstraint ):
305
- self .solve_symmetry_constraint (source )
338
+ symmetry_points_solved = self .solve_symmetry_constraint (source )
306
339
elif isinstance (source , ROCurvatureConstraint ):
307
- self .solve_roc_constraint (source )
340
+ roc_points_solved = self .solve_roc_constraint (source )
308
341
309
- self .solve_other_constraints (points_solved )
342
+ other_points_solved = self .solve_other_constraints (points_solved )
343
+ other_points_solved = list (set (other_points_solved ).union (set (symmetry_points_solved )).union (set (roc_points_solved )))
344
+
345
+ points_solved = list (set (points_solved ).union (set (other_points_solved )))
310
346
311
347
# networkx.draw_circular(self, labels={point: point.name() for point in self.nodes})
312
348
# from matplotlib import pyplot as plt
313
349
# plt.show()
314
350
351
+ return points_solved
352
+
315
353
def move_root (self , root : Point , dx : float , dy : float ):
316
354
if not root .root :
317
355
raise ValueError ("Cannot move a point that is not a root of a constraint cluster" )
@@ -345,6 +383,8 @@ def solve_symmetry_constraint(constraint: SymmetryConstraint):
345
383
y3 + mirror_distance * np .sin (mirror_angle ), force = True
346
384
)
347
385
386
+ return constraint .child_nodes
387
+
348
388
@staticmethod
349
389
def solve_roc_constraint (constraint : ROCurvatureConstraint ):
350
390
@@ -361,47 +401,54 @@ def solve_for_single_curve(p_g1: Point, p_g2: Point, n: int):
361
401
solve_for_single_curve (constraint .g1_point_curve_1 , constraint .g2_point_curve_1 , constraint .curve_1 .degree )
362
402
solve_for_single_curve (constraint .g1_point_curve_2 , constraint .g2_point_curve_2 , constraint .curve_2 .degree )
363
403
404
+ return constraint .child_nodes
405
+
364
406
def solve_symmetry_constraints (self , points : typing .List [Point ]):
407
+ points_solved = []
365
408
symmetry_constraints_solved = []
366
409
for point in points :
367
410
symmetry_constraints = [geo_con for geo_con in point .geo_cons if isinstance (geo_con , SymmetryConstraint )]
368
411
for symmetry_constraint in symmetry_constraints :
369
412
if symmetry_constraint in symmetry_constraints_solved :
370
413
continue
371
- self .solve_symmetry_constraint (symmetry_constraint )
414
+ symmetry_points_solved = self .solve_symmetry_constraint (symmetry_constraint )
372
415
symmetry_constraints_solved .append (symmetry_constraint )
416
+ for symmetry_point_solved in symmetry_points_solved :
417
+ if symmetry_point_solved in points_solved :
418
+ continue
419
+
420
+ points_solved .append (symmetry_point_solved )
421
+
422
+ return points_solved
373
423
374
424
def solve_roc_constraints (self , points : typing .List [Point ]):
375
- roc_constraints_solved = []
425
+ points_solved = []
426
+ roc_constraints_solved = []
376
427
for point in points :
377
428
roc_constraints = [geo_con for geo_con in point .geo_cons if isinstance (geo_con , ROCurvatureConstraint )]
378
429
for roc_constraint in roc_constraints :
379
430
if roc_constraint in roc_constraints_solved :
380
431
continue
381
- self .solve_roc_constraint (roc_constraint )
432
+ roc_points_solved = self .solve_roc_constraint (roc_constraint )
382
433
roc_constraints_solved .append (roc_constraint )
434
+ for roc_point_solved in roc_points_solved :
435
+ if roc_point_solved in points_solved :
436
+ continue
383
437
384
- def solve_other_constraints (self , points : typing .List [Point ]):
385
- self .solve_symmetry_constraints (points )
386
- self .solve_roc_constraints (points )
438
+ points_solved .append (roc_point_solved )
387
439
388
- def update_canvas_items ( self , source : GeoCon or Point ):
440
+ return points_solved
389
441
390
- points_to_update = []
391
- if isinstance (source , Point ):
392
- starting_points = [source ]
393
- elif isinstance (source , GeoCon ):
394
- starting_points = source .child_nodes
395
- else :
396
- raise ValueError ("source must be of type GeoCon or of type Point" )
397
- for starting_point in starting_points :
398
- for point in networkx .dfs_preorder_nodes (self , source = starting_point ):
399
- if point in points_to_update :
400
- continue
401
- points_to_update .append (point )
442
+ def solve_other_constraints (self , points : typing .List [Point ]):
443
+ symmetry_points_solved = self .solve_symmetry_constraints (points )
444
+ roc_points_solved = self .solve_roc_constraints (points )
445
+ return list (set (symmetry_points_solved ).union (roc_points_solved ))
446
+
447
+ @staticmethod
448
+ def update_canvas_items (points_solved : typing .List [Point ]):
402
449
403
450
curves_to_update = []
404
- for point in points_to_update :
451
+ for point in points_solved :
405
452
if point .canvas_item is not None :
406
453
point .canvas_item .updateCanvasItem (point .x ().value (), point .y ().value ())
407
454
@@ -421,7 +468,7 @@ def update_canvas_items(self, source: GeoCon or Point):
421
468
airfoil .canvas_item .generatePicture ()
422
469
423
470
constraints_to_update = []
424
- for point in networkx . dfs_preorder_nodes ( self , source = source . child_nodes [ 0 ]) :
471
+ for point in points_solved :
425
472
for geo_con in point .geo_cons :
426
473
if geo_con not in constraints_to_update :
427
474
constraints_to_update .append (geo_con )
@@ -433,64 +480,3 @@ def update_canvas_items(self, source: GeoCon or Point):
433
480
434
481
class ConstraintValidationError (Exception ):
435
482
pass
436
-
437
-
438
-
439
- def main ():
440
- pass
441
- # geo_col = GeometryCollection()
442
- # gcs2 = GCS2()
443
- # points = [
444
- # geo_col.add_point(0.0, 0.0),
445
- # geo_col.add_point(1.1, 0.0),
446
- # geo_col.add_point(0.1, -0.1),
447
- # geo_col.add_point(0.5, 0.2),
448
- # geo_col.add_point(0.2, 0.7),
449
- # geo_col.add_point(0.4, 0.4)
450
- # ]
451
- # for point in points:
452
- # gcs2.add_point(point)
453
- # constraints = [
454
- # DistanceConstraint(points[0], points[1], 0.25),
455
- # DistanceConstraint(points[1], points[2], 0.35),
456
- # DistanceConstraint(points[2], points[3], 0.4),
457
- # DistanceConstraint(points[1], points[4], 0.15),
458
- # DistanceConstraint(points[2], points[5], 0.2),
459
- # AntiParallel3Constraint(points[0], points[1], points[2]),
460
- # AntiParallel3Constraint(points[1], points[2], points[3]),
461
- # Perp3Constraint(points[4], points[1], points[0]),
462
- # Perp3Constraint(points[5], points[2], points[1])
463
- # ]
464
- # for constraint in constraints:
465
- # gcs2.add_constraint(constraint)
466
- # gcs2.solve(constraint)
467
- # print(f"{points[0].measure_distance(points[1]) = }")
468
- # print(f"{points[1].measure_distance(points[2]) = }")
469
- # print(f"{points[2].measure_distance(points[3]) = }")
470
- # print(f"{points[2].measure_distance(points[4]) = }")
471
- # print(f"{points[3].measure_distance(points[5]) = }")
472
- # # plt.plot([p.x().value() for p in points], [p.y().value() for p in points], ls="none", marker="o")
473
- # # plt.show()
474
- #
475
- # constraints[0].param().set_value(0.35)
476
- # gcs2.solve(constraints[0])
477
- # plt.plot([p.x().value() for p in points], [p.y().value() for p in points], ls="none", marker="o")
478
- # plt.show()
479
- #
480
- # networkx.draw_networkx(gcs2)
481
- # plt.show()
482
- #
483
- # for point in points:
484
- # tree = networkx.bfs_tree(gcs2, source=point)
485
- # networkx.draw_networkx(tree)
486
- # plt.show()
487
- #
488
- # plt.plot([p.x().value() for p in points], [p.y().value() for p in points], ls="none", marker="o")
489
- # plt.gca().set_aspect("equal")
490
- # plt.show()
491
- # networkx.draw_networkx(gcs2)
492
- # plt.show()
493
-
494
-
495
- if __name__ == "__main__" :
496
- main ()
0 commit comments