pyedb 0.13.0__py3-none-any.whl → 0.14.0__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.
Potentially problematic release.
This version of pyedb might be problematic. Click here for more details.
- pyedb/__init__.py +1 -1
- pyedb/configuration/cfg_common.py +0 -5
- pyedb/configuration/cfg_components.py +0 -2
- pyedb/configuration/cfg_operations.py +0 -2
- pyedb/configuration/cfg_package_definition.py +0 -2
- pyedb/configuration/cfg_ports_sources.py +14 -11
- pyedb/configuration/cfg_stackup.py +0 -7
- pyedb/configuration/configuration.py +0 -6
- pyedb/dotnet/application/Variables.py +4 -40
- pyedb/dotnet/edb.py +27 -82
- pyedb/dotnet/edb_core/{edb_data/components_data.py → cell/hierarchy/component.py} +13 -133
- pyedb/dotnet/edb_core/cell/hierarchy/model.py +0 -3
- pyedb/dotnet/edb_core/cell/hierarchy/netlist_model.py +30 -0
- pyedb/dotnet/edb_core/cell/hierarchy/pin_pair_model.py +105 -0
- pyedb/dotnet/edb_core/cell/hierarchy/s_parameter_model.py +34 -0
- pyedb/dotnet/edb_core/cell/hierarchy/spice_model.py +34 -0
- pyedb/dotnet/edb_core/cell/layout.py +137 -0
- pyedb/dotnet/edb_core/cell/layout_obj.py +2 -4
- pyedb/dotnet/edb_core/cell/primitive.py +199 -1
- pyedb/dotnet/edb_core/cell/terminal/bundle_terminal.py +52 -0
- pyedb/dotnet/edb_core/cell/terminal/edge_terminal.py +50 -0
- pyedb/dotnet/edb_core/cell/terminal/padstack_instance_terminal.py +88 -0
- pyedb/dotnet/edb_core/cell/terminal/pingroup_terminal.py +59 -0
- pyedb/dotnet/edb_core/cell/terminal/point_terminal.py +73 -0
- pyedb/dotnet/edb_core/{edb_data/terminals.py → cell/terminal/terminal.py} +33 -242
- pyedb/dotnet/edb_core/components.py +10 -56
- pyedb/dotnet/edb_core/definition/component_def.py +1 -8
- pyedb/dotnet/edb_core/definition/component_model.py +0 -2
- pyedb/dotnet/edb_core/definition/definitions.py +0 -2
- pyedb/dotnet/edb_core/definition/package_def.py +7 -5
- pyedb/dotnet/edb_core/edb_data/control_file.py +0 -3
- pyedb/dotnet/edb_core/edb_data/hfss_extent_info.py +0 -5
- pyedb/dotnet/edb_core/edb_data/hfss_pi_simulation_setup_data.py +4 -9
- pyedb/dotnet/edb_core/edb_data/layer_data.py +0 -7
- pyedb/dotnet/edb_core/edb_data/nets_data.py +2 -5
- pyedb/dotnet/edb_core/edb_data/padstacks_data.py +11 -29
- pyedb/dotnet/edb_core/edb_data/ports.py +4 -4
- pyedb/dotnet/edb_core/edb_data/primitives_data.py +3 -26
- pyedb/dotnet/edb_core/edb_data/raptor_x_simulation_setup_data.py +13 -20
- pyedb/dotnet/edb_core/edb_data/simulation_configuration.py +3 -11
- pyedb/dotnet/edb_core/edb_data/sources.py +12 -17
- pyedb/dotnet/edb_core/general.py +1 -6
- pyedb/dotnet/edb_core/geometry/polygon_data.py +0 -3
- pyedb/dotnet/edb_core/hfss.py +1 -33
- pyedb/dotnet/edb_core/layout.py +0 -35
- pyedb/dotnet/edb_core/layout_validation.py +1 -3
- pyedb/dotnet/edb_core/materials.py +1 -22
- pyedb/dotnet/edb_core/net_class.py +0 -8
- pyedb/dotnet/edb_core/nets.py +4 -29
- pyedb/dotnet/edb_core/padstack.py +76 -30
- pyedb/dotnet/edb_core/sim_setup_data/data/adaptive_frequency_data.py +72 -0
- pyedb/dotnet/edb_core/sim_setup_data/data/mesh_operation.py +287 -0
- pyedb/dotnet/edb_core/{edb_data/hfss_simulation_setup_data.py → sim_setup_data/data/settings.py} +174 -878
- pyedb/dotnet/edb_core/sim_setup_data/data/sweep_data.py +509 -0
- pyedb/dotnet/edb_core/sim_setup_data/io/__init__.py +0 -0
- pyedb/dotnet/edb_core/{edb_data/siwave_simulation_setup_data.py → sim_setup_data/io/siwave.py} +0 -341
- pyedb/dotnet/edb_core/siwave.py +5 -33
- pyedb/dotnet/edb_core/stackup.py +4 -51
- pyedb/dotnet/edb_core/utilities/simulation_setup.py +612 -366
- pyedb/generic/data_handlers.py +1 -9
- pyedb/generic/general_methods.py +3 -53
- pyedb/generic/plot.py +1 -2
- pyedb/ipc2581/ecad/cad_data/layer_feature.py +1 -7
- pyedb/ipc2581/ecad/cad_data/package.py +1 -4
- pyedb/ipc2581/ecad/cad_data/path.py +1 -3
- pyedb/ipc2581/ecad/cad_data/polygon.py +1 -6
- pyedb/ipc2581/ecad/cad_data/step.py +1 -10
- pyedb/ipc2581/ipc2581.py +8 -15
- pyedb/modeler/geometry_operators.py +164 -67
- pyedb/siwave.py +1 -16
- {pyedb-0.13.0.dist-info → pyedb-0.14.0.dist-info}/METADATA +2 -2
- {pyedb-0.13.0.dist-info → pyedb-0.14.0.dist-info}/RECORD +75 -61
- /pyedb/dotnet/edb_core/cell/{__init__.py → terminal/__init__.py} +0 -0
- {pyedb-0.13.0.dist-info → pyedb-0.14.0.dist-info}/LICENSE +0 -0
- {pyedb-0.13.0.dist-info → pyedb-0.14.0.dist-info}/WHEEL +0 -0
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
+
from collections import defaultdict
|
|
2
3
|
import math
|
|
3
4
|
import re
|
|
4
5
|
import sys
|
|
5
6
|
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
6
9
|
from pyedb.generic.constants import AXIS, PLANE, SWEEPDRAFT, scale_units
|
|
7
|
-
from pyedb.generic.general_methods import pyedb_function_handler
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class GeometryOperators(object):
|
|
11
13
|
"""Manages geometry operators."""
|
|
12
14
|
|
|
13
15
|
@staticmethod
|
|
14
|
-
@pyedb_function_handler()
|
|
15
16
|
def List2list(input_list): # pragma: no cover
|
|
16
17
|
"""Convert a C# list object to a Python list.
|
|
17
18
|
|
|
@@ -37,7 +38,6 @@ class GeometryOperators(object):
|
|
|
37
38
|
return output_list
|
|
38
39
|
|
|
39
40
|
@staticmethod
|
|
40
|
-
@pyedb_function_handler()
|
|
41
41
|
def parse_dim_arg(string, scale_to_unit=None, variable_manager=None): # pragma: no cover
|
|
42
42
|
"""Convert a number and unit to a float.
|
|
43
43
|
Angles are converted in radians.
|
|
@@ -106,7 +106,6 @@ class GeometryOperators(object):
|
|
|
106
106
|
return value
|
|
107
107
|
|
|
108
108
|
@staticmethod
|
|
109
|
-
@pyedb_function_handler()
|
|
110
109
|
def cs_plane_to_axis_str(val): # pragma: no cover
|
|
111
110
|
"""Retrieve a string for a coordinate system plane.
|
|
112
111
|
|
|
@@ -129,7 +128,6 @@ class GeometryOperators(object):
|
|
|
129
128
|
return "Y"
|
|
130
129
|
|
|
131
130
|
@staticmethod
|
|
132
|
-
@pyedb_function_handler()
|
|
133
131
|
def cs_plane_to_plane_str(val): # pragma: no cover
|
|
134
132
|
"""Retrieve a string for a coordinate system plane.
|
|
135
133
|
|
|
@@ -152,7 +150,6 @@ class GeometryOperators(object):
|
|
|
152
150
|
return "ZX"
|
|
153
151
|
|
|
154
152
|
@staticmethod
|
|
155
|
-
@pyedb_function_handler()
|
|
156
153
|
def cs_axis_str(val): # pragma: no cover
|
|
157
154
|
"""Retrieve a string for a coordinate system axis.
|
|
158
155
|
|
|
@@ -176,7 +173,6 @@ class GeometryOperators(object):
|
|
|
176
173
|
return "Z"
|
|
177
174
|
|
|
178
175
|
@staticmethod
|
|
179
|
-
@pyedb_function_handler()
|
|
180
176
|
def draft_type_str(val): # pragma: no cover
|
|
181
177
|
"""Retrieve the draft type.
|
|
182
178
|
|
|
@@ -199,7 +195,6 @@ class GeometryOperators(object):
|
|
|
199
195
|
return "Natural"
|
|
200
196
|
|
|
201
197
|
@staticmethod
|
|
202
|
-
@pyedb_function_handler()
|
|
203
198
|
def get_mid_point(v1, v2):
|
|
204
199
|
"""Evaluate the midpoint between two points.
|
|
205
200
|
|
|
@@ -220,7 +215,6 @@ class GeometryOperators(object):
|
|
|
220
215
|
return m
|
|
221
216
|
|
|
222
217
|
@staticmethod
|
|
223
|
-
@pyedb_function_handler()
|
|
224
218
|
def get_triangle_area(v1, v2, v3): # pragma: no cover
|
|
225
219
|
"""Evaluate the area of a triangle defined by its three vertices.
|
|
226
220
|
|
|
@@ -249,7 +243,6 @@ class GeometryOperators(object):
|
|
|
249
243
|
return area
|
|
250
244
|
|
|
251
245
|
@staticmethod
|
|
252
|
-
@pyedb_function_handler()
|
|
253
246
|
def v_cross(a, b): # pragma: no cover
|
|
254
247
|
"""Evaluate the cross product of two geometry vectors.
|
|
255
248
|
|
|
@@ -269,7 +262,6 @@ class GeometryOperators(object):
|
|
|
269
262
|
return c
|
|
270
263
|
|
|
271
264
|
@staticmethod
|
|
272
|
-
@pyedb_function_handler()
|
|
273
265
|
def _v_dot(a, b): # pragma: no cover
|
|
274
266
|
"""Evaluate the dot product between two geometry vectors.
|
|
275
267
|
|
|
@@ -295,7 +287,6 @@ class GeometryOperators(object):
|
|
|
295
287
|
return False
|
|
296
288
|
|
|
297
289
|
@staticmethod
|
|
298
|
-
@pyedb_function_handler()
|
|
299
290
|
def v_dot(a, b): # pragma: no cover
|
|
300
291
|
"""Evaluate the dot product between two geometry vectors.
|
|
301
292
|
|
|
@@ -315,7 +306,6 @@ class GeometryOperators(object):
|
|
|
315
306
|
return GeometryOperators._v_dot(a, b)
|
|
316
307
|
|
|
317
308
|
@staticmethod
|
|
318
|
-
@pyedb_function_handler()
|
|
319
309
|
def v_prod(s, v): # pragma: no cover
|
|
320
310
|
"""Evaluate the product between a scalar value and a vector.
|
|
321
311
|
|
|
@@ -338,7 +328,6 @@ class GeometryOperators(object):
|
|
|
338
328
|
return r
|
|
339
329
|
|
|
340
330
|
@staticmethod
|
|
341
|
-
@pyedb_function_handler()
|
|
342
331
|
def v_rotate_about_axis(vector, angle, radians=False, axis="z"): # pragma: no cover
|
|
343
332
|
"""Evaluate rotation of a vector around an axis.
|
|
344
333
|
|
|
@@ -380,7 +369,6 @@ class GeometryOperators(object):
|
|
|
380
369
|
return rotated_x, rotated_y, rotated_z
|
|
381
370
|
|
|
382
371
|
@staticmethod
|
|
383
|
-
@pyedb_function_handler()
|
|
384
372
|
def v_sub(a, b): # pragma: no cover
|
|
385
373
|
"""Evaluate two geometry vectors by subtracting them (a-b).
|
|
386
374
|
|
|
@@ -401,7 +389,6 @@ class GeometryOperators(object):
|
|
|
401
389
|
return c
|
|
402
390
|
|
|
403
391
|
@staticmethod
|
|
404
|
-
@pyedb_function_handler()
|
|
405
392
|
def v_sum(a, b): # pragma: no cover
|
|
406
393
|
"""Evaluate two geometry vectors by adding them (a+b).
|
|
407
394
|
|
|
@@ -422,7 +409,6 @@ class GeometryOperators(object):
|
|
|
422
409
|
return c
|
|
423
410
|
|
|
424
411
|
@staticmethod
|
|
425
|
-
@pyedb_function_handler()
|
|
426
412
|
def v_norm(a): # pragma: no cover
|
|
427
413
|
"""Evaluate the Euclidean norm of a geometry vector.
|
|
428
414
|
|
|
@@ -444,7 +430,6 @@ class GeometryOperators(object):
|
|
|
444
430
|
return m
|
|
445
431
|
|
|
446
432
|
@staticmethod
|
|
447
|
-
@pyedb_function_handler()
|
|
448
433
|
def normalize_vector(v): # pragma: no cover
|
|
449
434
|
"""Normalize a geometry vector.
|
|
450
435
|
|
|
@@ -465,7 +450,6 @@ class GeometryOperators(object):
|
|
|
465
450
|
return vn
|
|
466
451
|
|
|
467
452
|
@staticmethod
|
|
468
|
-
@pyedb_function_handler()
|
|
469
453
|
def v_points(p1, p2): # pragma: no cover
|
|
470
454
|
"""Vector from one point to another point.
|
|
471
455
|
|
|
@@ -484,7 +468,6 @@ class GeometryOperators(object):
|
|
|
484
468
|
return GeometryOperators.v_sub(p2, p1)
|
|
485
469
|
|
|
486
470
|
@staticmethod
|
|
487
|
-
@pyedb_function_handler()
|
|
488
471
|
def points_distance(p1, p2): # pragma: no cover
|
|
489
472
|
"""Evaluate the distance between two points expressed as their Cartesian coordinates.
|
|
490
473
|
|
|
@@ -510,7 +493,6 @@ class GeometryOperators(object):
|
|
|
510
493
|
# fmt: on
|
|
511
494
|
|
|
512
495
|
@staticmethod
|
|
513
|
-
@pyedb_function_handler()
|
|
514
496
|
def find_point_on_plane(pointlists, direction=0): # pragma: no cover
|
|
515
497
|
"""Find a point on a plane.
|
|
516
498
|
|
|
@@ -539,7 +521,6 @@ class GeometryOperators(object):
|
|
|
539
521
|
return point
|
|
540
522
|
|
|
541
523
|
@staticmethod
|
|
542
|
-
@pyedb_function_handler()
|
|
543
524
|
def distance_vector(p, a, b): # pragma: no cover
|
|
544
525
|
"""Evaluate the vector distance between point ``p`` and a line defined by two points, ``a`` and ``b``.
|
|
545
526
|
|
|
@@ -571,7 +552,6 @@ class GeometryOperators(object):
|
|
|
571
552
|
return vd
|
|
572
553
|
|
|
573
554
|
@staticmethod
|
|
574
|
-
@pyedb_function_handler()
|
|
575
555
|
def is_between_points(p, a, b, tol=1e-6): # pragma: no cover
|
|
576
556
|
"""Check if a point lies on the segment defined by two points.
|
|
577
557
|
|
|
@@ -604,7 +584,6 @@ class GeometryOperators(object):
|
|
|
604
584
|
return True
|
|
605
585
|
|
|
606
586
|
@staticmethod
|
|
607
|
-
@pyedb_function_handler()
|
|
608
587
|
def is_parallel(a1, a2, b1, b2, tol=1e-6): # pragma: no cover
|
|
609
588
|
"""Check if a segment defined by two points is parallel to a segment defined by two other points.
|
|
610
589
|
|
|
@@ -633,7 +612,6 @@ class GeometryOperators(object):
|
|
|
633
612
|
return False
|
|
634
613
|
|
|
635
614
|
@staticmethod
|
|
636
|
-
@pyedb_function_handler()
|
|
637
615
|
def parallel_coeff(a1, a2, b1, b2): # pragma: no cover
|
|
638
616
|
"""ADD DESCRIPTION.
|
|
639
617
|
|
|
@@ -661,7 +639,6 @@ class GeometryOperators(object):
|
|
|
661
639
|
return abs(var)
|
|
662
640
|
|
|
663
641
|
@staticmethod
|
|
664
|
-
@pyedb_function_handler()
|
|
665
642
|
def is_collinear(a, b, tol=1e-6): # pragma: no cover
|
|
666
643
|
"""Check if two vectors are collinear (parallel or anti-parallel).
|
|
667
644
|
|
|
@@ -689,7 +666,6 @@ class GeometryOperators(object):
|
|
|
689
666
|
return False
|
|
690
667
|
|
|
691
668
|
@staticmethod
|
|
692
|
-
@pyedb_function_handler()
|
|
693
669
|
def is_projection_inside(a1, a2, b1, b2): # pragma: no cover
|
|
694
670
|
"""Project a segment onto another segment and check if the projected segment is inside it.
|
|
695
671
|
|
|
@@ -722,7 +698,6 @@ class GeometryOperators(object):
|
|
|
722
698
|
return True
|
|
723
699
|
|
|
724
700
|
@staticmethod
|
|
725
|
-
@pyedb_function_handler()
|
|
726
701
|
def arrays_positions_sum(vertlist1, vertlist2): # pragma: no cover
|
|
727
702
|
"""Return the sum of two vertices lists.
|
|
728
703
|
|
|
@@ -744,7 +719,6 @@ class GeometryOperators(object):
|
|
|
744
719
|
return s / (len(vertlist1) + len(vertlist2))
|
|
745
720
|
|
|
746
721
|
@staticmethod
|
|
747
|
-
@pyedb_function_handler()
|
|
748
722
|
def v_angle(a, b): # pragma: no cover
|
|
749
723
|
"""Evaluate the angle between two geometry vectors.
|
|
750
724
|
|
|
@@ -770,7 +744,6 @@ class GeometryOperators(object):
|
|
|
770
744
|
return math.acos(d / (an * bn))
|
|
771
745
|
|
|
772
746
|
@staticmethod
|
|
773
|
-
@pyedb_function_handler()
|
|
774
747
|
def pointing_to_axis(x_pointing, y_pointing): # pragma: no cover
|
|
775
748
|
"""Retrieve the axes from the HFSS X axis and Y pointing axis as per
|
|
776
749
|
the definition of the AEDT interface coordinate system.
|
|
@@ -798,7 +771,6 @@ class GeometryOperators(object):
|
|
|
798
771
|
return xp, yp, zp
|
|
799
772
|
|
|
800
773
|
@staticmethod
|
|
801
|
-
@pyedb_function_handler()
|
|
802
774
|
def axis_to_euler_zxz(x, y, z): # pragma: no cover
|
|
803
775
|
"""Retrieve Euler angles of a frame following the rotation sequence ZXZ.
|
|
804
776
|
|
|
@@ -842,7 +814,6 @@ class GeometryOperators(object):
|
|
|
842
814
|
return phi, theta, psi
|
|
843
815
|
|
|
844
816
|
@staticmethod
|
|
845
|
-
@pyedb_function_handler()
|
|
846
817
|
def axis_to_euler_zyz(x, y, z): # pragma: no cover
|
|
847
818
|
"""Retrieve Euler angles of a frame following the rotation sequence ZYZ.
|
|
848
819
|
|
|
@@ -886,7 +857,6 @@ class GeometryOperators(object):
|
|
|
886
857
|
return phi, theta, psi
|
|
887
858
|
|
|
888
859
|
@staticmethod
|
|
889
|
-
@pyedb_function_handler()
|
|
890
860
|
def quaternion_to_axis(q): # pragma: no cover
|
|
891
861
|
"""Convert a quaternion to a rotated frame defined by X, Y, and Z axes.
|
|
892
862
|
|
|
@@ -925,7 +895,6 @@ class GeometryOperators(object):
|
|
|
925
895
|
return x, y, z
|
|
926
896
|
|
|
927
897
|
@staticmethod
|
|
928
|
-
@pyedb_function_handler()
|
|
929
898
|
def quaternion_to_axis_angle(q): # pragma: no cover
|
|
930
899
|
"""Convert a quaternion to the axis angle rotation formulation.
|
|
931
900
|
|
|
@@ -951,7 +920,6 @@ class GeometryOperators(object):
|
|
|
951
920
|
return u, theta
|
|
952
921
|
|
|
953
922
|
@staticmethod
|
|
954
|
-
@pyedb_function_handler()
|
|
955
923
|
def axis_angle_to_quaternion(u, theta): # pragma: no cover
|
|
956
924
|
"""Convert the axis angle rotation formulation to a quaternion.
|
|
957
925
|
|
|
@@ -978,7 +946,6 @@ class GeometryOperators(object):
|
|
|
978
946
|
return [q1, q2, q3, q4]
|
|
979
947
|
|
|
980
948
|
@staticmethod
|
|
981
|
-
@pyedb_function_handler()
|
|
982
949
|
def quaternion_to_euler_zxz(q): # pragma: no cover
|
|
983
950
|
"""Convert a quaternion to Euler angles following rotation sequence ZXZ.
|
|
984
951
|
|
|
@@ -1008,7 +975,6 @@ class GeometryOperators(object):
|
|
|
1008
975
|
return phi, theta, psi
|
|
1009
976
|
|
|
1010
977
|
@staticmethod
|
|
1011
|
-
@pyedb_function_handler()
|
|
1012
978
|
def euler_zxz_to_quaternion(phi, theta, psi): # pragma: no cover
|
|
1013
979
|
"""Convert the Euler angles following rotation sequence ZXZ to a quaternion.
|
|
1014
980
|
|
|
@@ -1039,7 +1005,6 @@ class GeometryOperators(object):
|
|
|
1039
1005
|
return [q1, q2, q3, q4]
|
|
1040
1006
|
|
|
1041
1007
|
@staticmethod
|
|
1042
|
-
@pyedb_function_handler()
|
|
1043
1008
|
def quaternion_to_euler_zyz(q): # pragma: no cover
|
|
1044
1009
|
"""Convert a quaternion to Euler angles following rotation sequence ZYZ.
|
|
1045
1010
|
|
|
@@ -1069,7 +1034,6 @@ class GeometryOperators(object):
|
|
|
1069
1034
|
return phi, theta, psi
|
|
1070
1035
|
|
|
1071
1036
|
@staticmethod
|
|
1072
|
-
@pyedb_function_handler()
|
|
1073
1037
|
def euler_zyz_to_quaternion(phi, theta, psi): # pragma: no cover
|
|
1074
1038
|
"""Convert the Euler angles following rotation sequence ZYZ to a quaternion.
|
|
1075
1039
|
|
|
@@ -1100,7 +1064,6 @@ class GeometryOperators(object):
|
|
|
1100
1064
|
return [q1, q2, q3, q4]
|
|
1101
1065
|
|
|
1102
1066
|
@staticmethod
|
|
1103
|
-
@pyedb_function_handler()
|
|
1104
1067
|
def deg2rad(angle):
|
|
1105
1068
|
"""Convert the angle from degrees to radians.
|
|
1106
1069
|
|
|
@@ -1119,7 +1082,6 @@ class GeometryOperators(object):
|
|
|
1119
1082
|
return angle / 180.0 * pi
|
|
1120
1083
|
|
|
1121
1084
|
@staticmethod
|
|
1122
|
-
@pyedb_function_handler()
|
|
1123
1085
|
def rad2deg(angle):
|
|
1124
1086
|
"""Convert the angle from radians to degrees.
|
|
1125
1087
|
|
|
@@ -1138,7 +1100,6 @@ class GeometryOperators(object):
|
|
|
1138
1100
|
return angle * 180.0 / pi
|
|
1139
1101
|
|
|
1140
1102
|
@staticmethod
|
|
1141
|
-
@pyedb_function_handler()
|
|
1142
1103
|
def atan2(y, x): # pragma: no cover
|
|
1143
1104
|
"""Implementation of atan2 that does not suffer from the following issues:
|
|
1144
1105
|
math.atan2(0.0, 0.0) = 0.0
|
|
@@ -1168,7 +1129,6 @@ class GeometryOperators(object):
|
|
|
1168
1129
|
return math.atan2(y, x)
|
|
1169
1130
|
|
|
1170
1131
|
@staticmethod
|
|
1171
|
-
@pyedb_function_handler()
|
|
1172
1132
|
def q_prod(p, q): # pragma: no cover
|
|
1173
1133
|
"""Evaluate the product of two quaternions, ``p`` and ``q``, defined as:
|
|
1174
1134
|
p = p0 + p' = p0 + ip1 + jp2 + kp3.
|
|
@@ -1204,7 +1164,6 @@ class GeometryOperators(object):
|
|
|
1204
1164
|
return [r0, rv[0], rv[1], rv[2]]
|
|
1205
1165
|
|
|
1206
1166
|
@staticmethod
|
|
1207
|
-
@pyedb_function_handler()
|
|
1208
1167
|
def q_rotation(v, q): # pragma: no cover
|
|
1209
1168
|
"""Evaluate the rotation of a vector, defined by a quaternion.
|
|
1210
1169
|
Evaluated as:
|
|
@@ -1240,7 +1199,6 @@ class GeometryOperators(object):
|
|
|
1240
1199
|
return w
|
|
1241
1200
|
|
|
1242
1201
|
@staticmethod
|
|
1243
|
-
@pyedb_function_handler()
|
|
1244
1202
|
def q_rotation_inv(v, q):
|
|
1245
1203
|
"""Evaluate the inverse rotation of a vector that is defined by a quaternion.
|
|
1246
1204
|
|
|
@@ -1268,7 +1226,6 @@ class GeometryOperators(object):
|
|
|
1268
1226
|
return GeometryOperators.q_rotation(v, q1)
|
|
1269
1227
|
|
|
1270
1228
|
@staticmethod
|
|
1271
|
-
@pyedb_function_handler()
|
|
1272
1229
|
def get_polygon_centroid(pts): # pragma: no cover
|
|
1273
1230
|
"""Evaluate the centroid of a polygon defined by its points.
|
|
1274
1231
|
|
|
@@ -1304,7 +1261,6 @@ class GeometryOperators(object):
|
|
|
1304
1261
|
return [xc, yc, zc]
|
|
1305
1262
|
|
|
1306
1263
|
@staticmethod
|
|
1307
|
-
@pyedb_function_handler()
|
|
1308
1264
|
def cs_xy_pointing_expression(yaw, pitch, roll): # pragma: no cover
|
|
1309
1265
|
"""Return x_pointing and y_pointing vectors as expressions from
|
|
1310
1266
|
the yaw, ptich, and roll input (as strings).
|
|
@@ -1343,7 +1299,6 @@ class GeometryOperators(object):
|
|
|
1343
1299
|
return [x_pointing, y_pointing]
|
|
1344
1300
|
|
|
1345
1301
|
@staticmethod
|
|
1346
|
-
@pyedb_function_handler()
|
|
1347
1302
|
def get_numeric(s):
|
|
1348
1303
|
"""Convert a string to a numeric value. Discard the suffix."""
|
|
1349
1304
|
if type(s) == str:
|
|
@@ -1357,7 +1312,6 @@ class GeometryOperators(object):
|
|
|
1357
1312
|
return float(s)
|
|
1358
1313
|
|
|
1359
1314
|
@staticmethod
|
|
1360
|
-
@pyedb_function_handler()
|
|
1361
1315
|
def is_small(s):
|
|
1362
1316
|
"""Return ``True`` if the number represented by s is zero (i.e very small).
|
|
1363
1317
|
|
|
@@ -1375,7 +1329,6 @@ class GeometryOperators(object):
|
|
|
1375
1329
|
return True if math.fabs(n) < 2.0 * abs(sys.float_info.epsilon) else False
|
|
1376
1330
|
|
|
1377
1331
|
@staticmethod
|
|
1378
|
-
@pyedb_function_handler()
|
|
1379
1332
|
def numeric_cs(cs_in): # pragma: no cover
|
|
1380
1333
|
"""Return a list of [x,y,z] numeric values given a coordinate system as input.
|
|
1381
1334
|
|
|
@@ -1396,7 +1349,6 @@ class GeometryOperators(object):
|
|
|
1396
1349
|
return [0, 0, 0]
|
|
1397
1350
|
|
|
1398
1351
|
@staticmethod
|
|
1399
|
-
@pyedb_function_handler()
|
|
1400
1352
|
def orient_polygon(x, y, clockwise=True):
|
|
1401
1353
|
"""
|
|
1402
1354
|
Orient a polygon clockwise or counterclockwise. The vertices should be already ordered either way.
|
|
@@ -1472,7 +1424,6 @@ class GeometryOperators(object):
|
|
|
1472
1424
|
return x_ret, y_ret
|
|
1473
1425
|
|
|
1474
1426
|
@staticmethod
|
|
1475
|
-
@pyedb_function_handler()
|
|
1476
1427
|
def v_angle_sign(va, vb, vn, right_handed=True): # pragma: no cover
|
|
1477
1428
|
"""Evaluate the signed angle between two geometry vectors.
|
|
1478
1429
|
The sign is evaluated respect to the normal to the plane containing the two vectors as per the following rule.
|
|
@@ -1517,7 +1468,6 @@ class GeometryOperators(object):
|
|
|
1517
1468
|
return math.atan2(GeometryOperators.v_dot(mcross, vnn), GeometryOperators.v_dot(va, vb))
|
|
1518
1469
|
|
|
1519
1470
|
@staticmethod
|
|
1520
|
-
@pyedb_function_handler()
|
|
1521
1471
|
def v_angle_sign_2D(va, vb, right_handed=True):
|
|
1522
1472
|
"""Evaluate the signed angle between two 2D geometry vectors.
|
|
1523
1473
|
Iit the 2D version of the ``GeometryOperators.v_angle_sign`` considering vn = [0,0,1].
|
|
@@ -1547,7 +1497,6 @@ class GeometryOperators(object):
|
|
|
1547
1497
|
return math.atan2(-c, GeometryOperators.v_dot(va, vb))
|
|
1548
1498
|
|
|
1549
1499
|
@staticmethod
|
|
1550
|
-
@pyedb_function_handler()
|
|
1551
1500
|
def point_in_polygon(point, polygon, tolerance=1e-8):
|
|
1552
1501
|
"""Determine if a point is inside, outside the polygon or at exactly at the border.
|
|
1553
1502
|
|
|
@@ -1595,7 +1544,6 @@ class GeometryOperators(object):
|
|
|
1595
1544
|
# fmt: on
|
|
1596
1545
|
|
|
1597
1546
|
@staticmethod
|
|
1598
|
-
@pyedb_function_handler()
|
|
1599
1547
|
def is_point_in_polygon(point, polygon):
|
|
1600
1548
|
"""Determine if a point is inside or outside a polygon, both located on the same plane.
|
|
1601
1549
|
|
|
@@ -1619,7 +1567,6 @@ class GeometryOperators(object):
|
|
|
1619
1567
|
return True
|
|
1620
1568
|
|
|
1621
1569
|
@staticmethod
|
|
1622
|
-
@pyedb_function_handler()
|
|
1623
1570
|
def are_segments_intersecting(a1, a2, b1, b2, include_collinear=True):
|
|
1624
1571
|
"""
|
|
1625
1572
|
Determine if the two segments a and b are intersecting.
|
|
@@ -1708,7 +1655,6 @@ class GeometryOperators(object):
|
|
|
1708
1655
|
# fmt: on
|
|
1709
1656
|
|
|
1710
1657
|
@staticmethod
|
|
1711
|
-
@pyedb_function_handler()
|
|
1712
1658
|
def is_segment_intersecting_polygon(a, b, polygon):
|
|
1713
1659
|
"""
|
|
1714
1660
|
Determine if a segment defined by two points ``a`` and ``b`` intersects a polygon.
|
|
@@ -1743,7 +1689,6 @@ class GeometryOperators(object):
|
|
|
1743
1689
|
return False
|
|
1744
1690
|
|
|
1745
1691
|
@staticmethod
|
|
1746
|
-
@pyedb_function_handler()
|
|
1747
1692
|
def is_perpendicular(a, b, tol=1e-6):
|
|
1748
1693
|
"""Check if two vectors are perpendicular.
|
|
1749
1694
|
|
|
@@ -1769,7 +1714,6 @@ class GeometryOperators(object):
|
|
|
1769
1714
|
return False
|
|
1770
1715
|
|
|
1771
1716
|
@staticmethod
|
|
1772
|
-
@pyedb_function_handler()
|
|
1773
1717
|
def is_point_projection_in_segment(p, a, b):
|
|
1774
1718
|
"""Check if a point projection lies on the segment defined by two points.
|
|
1775
1719
|
|
|
@@ -1796,7 +1740,6 @@ class GeometryOperators(object):
|
|
|
1796
1740
|
# fmt: on
|
|
1797
1741
|
|
|
1798
1742
|
@staticmethod
|
|
1799
|
-
@pyedb_function_handler()
|
|
1800
1743
|
def point_segment_distance(p, a, b): # pragma: no cover
|
|
1801
1744
|
"""Calculate the distance between a point ``p`` and a segment defined by two points ``a`` and ``b``.
|
|
1802
1745
|
|
|
@@ -1822,7 +1765,6 @@ class GeometryOperators(object):
|
|
|
1822
1765
|
# fmt: on
|
|
1823
1766
|
|
|
1824
1767
|
@staticmethod
|
|
1825
|
-
@pyedb_function_handler()
|
|
1826
1768
|
def find_largest_rectangle_inside_polygon(polygon, partition_max_order=16):
|
|
1827
1769
|
"""Find the largest area rectangles of arbitrary orientation in a polygon.
|
|
1828
1770
|
|
|
@@ -1931,7 +1873,6 @@ class GeometryOperators(object):
|
|
|
1931
1873
|
# fmt: on
|
|
1932
1874
|
|
|
1933
1875
|
@staticmethod
|
|
1934
|
-
@pyedb_function_handler()
|
|
1935
1876
|
def degrees_over_rounded(angle, digits):
|
|
1936
1877
|
"""Ceil of angle.
|
|
1937
1878
|
|
|
@@ -1950,7 +1891,6 @@ class GeometryOperators(object):
|
|
|
1950
1891
|
return math.ceil(math.degrees(angle) * 10**digits) / (10**digits)
|
|
1951
1892
|
|
|
1952
1893
|
@staticmethod
|
|
1953
|
-
@pyedb_function_handler()
|
|
1954
1894
|
def radians_over_rounded(angle, digits):
|
|
1955
1895
|
"""Radian angle ceiling.
|
|
1956
1896
|
|
|
@@ -1969,7 +1909,6 @@ class GeometryOperators(object):
|
|
|
1969
1909
|
return math.ceil(math.radians(angle) * 10**digits) / (10**digits)
|
|
1970
1910
|
|
|
1971
1911
|
@staticmethod
|
|
1972
|
-
@pyedb_function_handler()
|
|
1973
1912
|
def degrees_default_rounded(angle, digits):
|
|
1974
1913
|
"""Convert angle to degree with given digits rounding.
|
|
1975
1914
|
|
|
@@ -1988,7 +1927,6 @@ class GeometryOperators(object):
|
|
|
1988
1927
|
return math.floor(math.degrees(angle) * 10**digits) / (10**digits)
|
|
1989
1928
|
|
|
1990
1929
|
@staticmethod
|
|
1991
|
-
@pyedb_function_handler()
|
|
1992
1930
|
def radians_default_rounded(angle, digits):
|
|
1993
1931
|
"""Convert to radians with given round.
|
|
1994
1932
|
|
|
@@ -2007,7 +1945,6 @@ class GeometryOperators(object):
|
|
|
2007
1945
|
return math.floor(math.radians(angle) * 10**digits) / (10**digits)
|
|
2008
1946
|
|
|
2009
1947
|
@staticmethod
|
|
2010
|
-
@pyedb_function_handler()
|
|
2011
1948
|
def find_closest_points(points_list, reference_point, tol=1e-6): # pragma: no cover
|
|
2012
1949
|
"""Given a list of points, finds the closest points to a reference point.
|
|
2013
1950
|
It returns a list of points because more than one can be found.
|
|
@@ -2055,7 +1992,6 @@ class GeometryOperators(object):
|
|
|
2055
1992
|
# fmt: on
|
|
2056
1993
|
|
|
2057
1994
|
@staticmethod
|
|
2058
|
-
@pyedb_function_handler()
|
|
2059
1995
|
def mirror_point(start, reference, vector): # pragma: no cover
|
|
2060
1996
|
"""Mirror point about a plane defining by a point on the plane and a normal point.
|
|
2061
1997
|
|
|
@@ -2080,3 +2016,164 @@ class GeometryOperators(object):
|
|
|
2080
2016
|
dot_product = sum([distance[i] * vector[i] for i in range(3)])
|
|
2081
2017
|
reflection = [-dot_product * vector[i] * 2 + start[i] for i in range(3)]
|
|
2082
2018
|
return reflection
|
|
2019
|
+
|
|
2020
|
+
@staticmethod
|
|
2021
|
+
def find_points_along_lines(
|
|
2022
|
+
points, minimum_number_of_points=3, distance_threshold=None, return_additional_info=False
|
|
2023
|
+
):
|
|
2024
|
+
"""Detect all points that are placed along lines.
|
|
2025
|
+
|
|
2026
|
+
The method takes as input a list of 2D points and detects all lines that contain at least 3 points.
|
|
2027
|
+
Optionally, the minimum number of points contained in a line can be specified by setting the
|
|
2028
|
+
argument ``minimum_number_of_points``.
|
|
2029
|
+
As default, all points along the lines are returned, regardless of their relative distance.
|
|
2030
|
+
Optionally, a `distance_threshold` can be set. If two points in a line are separated by a distance larger than
|
|
2031
|
+
``distance_threshold``, the line is divided in two parts. If one of those parts does not satisfy the
|
|
2032
|
+
``minimum_number_of_points`` requirement, it is discarded.
|
|
2033
|
+
If `distance_threshold` is set (not ``None``), the computational time increases.
|
|
2034
|
+
|
|
2035
|
+
points : List, numpy.ndarray
|
|
2036
|
+
The points to process. Can be a list of lists where each sublist
|
|
2037
|
+
represents a 2D point ``[x, y]`` coordinates, or a numpy array of shape (n, 2).
|
|
2038
|
+
minimum_number_of_points : int, optional
|
|
2039
|
+
The minimum number of points that a line must contain. Default is ``3``.
|
|
2040
|
+
distance_threshold : float, None, optional
|
|
2041
|
+
If two points in a line are separated by a distance larger than `distance_threshold`,
|
|
2042
|
+
the line is divided in two parts. Default is ``None``, in which case the control is not performed.
|
|
2043
|
+
return_additional_info : bool, optional
|
|
2044
|
+
Whether to return additional information about the number of elements processed.
|
|
2045
|
+
The default is ``True``.
|
|
2046
|
+
|
|
2047
|
+
Returns
|
|
2048
|
+
-------
|
|
2049
|
+
tuple
|
|
2050
|
+
The tuple contains:
|
|
2051
|
+
- lines: a list of lists where each sublist represents a 2D point ``[x, y]`` coordinates in each line.
|
|
2052
|
+
- lines indexes: a list of lists where each sublist represents the index of the point in each line.
|
|
2053
|
+
The index is referring to the point position in the input point list.
|
|
2054
|
+
- number of processed points: optional, returned if ``return_additional_info`` is ``True``
|
|
2055
|
+
- number of processed lines: optional, returned if ``return_additional_info`` is ``True``
|
|
2056
|
+
- number of detected lines after ``minimum_number_of_points`` is applied: optional,
|
|
2057
|
+
returned if ``return_additional_info`` is ``True``
|
|
2058
|
+
- number of detected lines after ``distance_threshold`` is applied: optional,
|
|
2059
|
+
returned if ``return_additional_info`` is ``True``
|
|
2060
|
+
"""
|
|
2061
|
+
|
|
2062
|
+
# Parameters
|
|
2063
|
+
min_num_points = max(3, int(minimum_number_of_points))
|
|
2064
|
+
cluster_lines_flag = False if distance_threshold is None else True
|
|
2065
|
+
tol_rad = 1e-10
|
|
2066
|
+
|
|
2067
|
+
# Converts the input
|
|
2068
|
+
points = np.array(points)
|
|
2069
|
+
# Number of points
|
|
2070
|
+
num_points = len(points)
|
|
2071
|
+
|
|
2072
|
+
angles = []
|
|
2073
|
+
# Evaluate the angle with x-axis for every possible line defined by 2 points
|
|
2074
|
+
# Nested for loops to iterate over each point
|
|
2075
|
+
for i in range(num_points - 1):
|
|
2076
|
+
angles.append([])
|
|
2077
|
+
for j in range(i + 1, num_points):
|
|
2078
|
+
p1 = points[i]
|
|
2079
|
+
p2 = points[j]
|
|
2080
|
+
x1 = p1[0]
|
|
2081
|
+
y1 = p1[1]
|
|
2082
|
+
x2 = p2[0]
|
|
2083
|
+
y2 = p2[1]
|
|
2084
|
+
|
|
2085
|
+
dx = x2 - x1
|
|
2086
|
+
dy = y2 - y1
|
|
2087
|
+
|
|
2088
|
+
# Calculate the angle in radians with the x-axis
|
|
2089
|
+
angle_rad = np.arctan2(dy, dx)
|
|
2090
|
+
if angle_rad < 0:
|
|
2091
|
+
angles[i].append(angle_rad + np.pi)
|
|
2092
|
+
else:
|
|
2093
|
+
angles[i].append(angle_rad)
|
|
2094
|
+
|
|
2095
|
+
# rounding the angles float number
|
|
2096
|
+
def bin_float(value, bin_size):
|
|
2097
|
+
return round(value / bin_size) * bin_size
|
|
2098
|
+
|
|
2099
|
+
for col in angles:
|
|
2100
|
+
for i, a in enumerate(col):
|
|
2101
|
+
col[i] = bin_float(a, tol_rad)
|
|
2102
|
+
|
|
2103
|
+
# Group the lines based on angles
|
|
2104
|
+
detected_lines_idx = []
|
|
2105
|
+
for i in range(num_points - 1):
|
|
2106
|
+
column = angles[i]
|
|
2107
|
+
new_lines = defaultdict(set) # sets are more efficient than lists for inclusion check
|
|
2108
|
+
lines_to_check = [line for line in detected_lines_idx if i in line]
|
|
2109
|
+
for k in range(len(column)):
|
|
2110
|
+
j = k + i + 1
|
|
2111
|
+
angle = column[k]
|
|
2112
|
+
|
|
2113
|
+
# Check if both indexes (points) are in any of the sets (lines)
|
|
2114
|
+
# Note that lines containing `i` are pre-selected outside this loop.
|
|
2115
|
+
# This makes the check O(n^2) instead of O(n^3)
|
|
2116
|
+
found = any(j in l for l in lines_to_check)
|
|
2117
|
+
if found:
|
|
2118
|
+
# this two points already belong to a line, no need to store them again
|
|
2119
|
+
continue
|
|
2120
|
+
new_lines[angle].update((i, j))
|
|
2121
|
+
# considering only lines with at least 3 points
|
|
2122
|
+
lines_to_add = [l for l in new_lines.values() if len(l) >= 3]
|
|
2123
|
+
detected_lines_idx.extend(lines_to_add)
|
|
2124
|
+
|
|
2125
|
+
# Discard the lines with less than min_num_points
|
|
2126
|
+
selected_lines_idx = [line for line in detected_lines_idx if len(line) >= min_num_points]
|
|
2127
|
+
|
|
2128
|
+
# Convert the lines' indexes in ndarrays
|
|
2129
|
+
selected_lines_idx = [np.array(list(line)) for line in selected_lines_idx]
|
|
2130
|
+
|
|
2131
|
+
# First sort the points in the detected lines
|
|
2132
|
+
lines_with_sorted_points_idx = []
|
|
2133
|
+
for line_idx in selected_lines_idx:
|
|
2134
|
+
pts_in_line = points[list(line_idx)]
|
|
2135
|
+
min_x = pts_in_line[:, 0].min()
|
|
2136
|
+
max_x = pts_in_line[:, 0].max()
|
|
2137
|
+
if max_x - min_x < 1e-7:
|
|
2138
|
+
# cluster on y because the line is vertical
|
|
2139
|
+
sort_idx = pts_in_line[:, 1].argsort()
|
|
2140
|
+
else:
|
|
2141
|
+
# cluster on x on the other cases
|
|
2142
|
+
sort_idx = pts_in_line[:, 0].argsort()
|
|
2143
|
+
sorted_points_idx = line_idx[sort_idx]
|
|
2144
|
+
lines_with_sorted_points_idx.append(sorted_points_idx)
|
|
2145
|
+
lines_idx = lines_with_sorted_points_idx
|
|
2146
|
+
|
|
2147
|
+
def cluster_line_points(points_idx, threshold):
|
|
2148
|
+
# Requires sorted points, points must be in numpy array format
|
|
2149
|
+
# Initialize clusters
|
|
2150
|
+
clusters = []
|
|
2151
|
+
current_cluster = [points_idx[0]]
|
|
2152
|
+
# Iterate through the sorted points
|
|
2153
|
+
for i in range(1, len(points_idx)):
|
|
2154
|
+
# Check if the current point is within the distance threshold from the last point in the current cluster
|
|
2155
|
+
if np.linalg.norm(points[points_idx[i]] - points[points_idx[i - 1]]) <= threshold:
|
|
2156
|
+
current_cluster.append(points_idx[i])
|
|
2157
|
+
else:
|
|
2158
|
+
# The current point is too far from the last point, finalize the current cluster and start a new one
|
|
2159
|
+
clusters.append(current_cluster)
|
|
2160
|
+
current_cluster = [points_idx[i]]
|
|
2161
|
+
# Append the last cluster
|
|
2162
|
+
clusters.append(current_cluster)
|
|
2163
|
+
return clusters
|
|
2164
|
+
|
|
2165
|
+
# separate lines based on required minimum distance btw points
|
|
2166
|
+
# It requires the points of the line to be sorted
|
|
2167
|
+
if cluster_lines_flag:
|
|
2168
|
+
clustered_lines = []
|
|
2169
|
+
for p_idx in lines_with_sorted_points_idx:
|
|
2170
|
+
clusters_in_line = cluster_line_points(p_idx, distance_threshold)
|
|
2171
|
+
clustered_lines.extend([c for c in clusters_in_line if len(c) >= min_num_points])
|
|
2172
|
+
lines_idx = clustered_lines
|
|
2173
|
+
|
|
2174
|
+
# Convert indexes to points coordinates
|
|
2175
|
+
lines = [points[list(line)].tolist() for line in lines_idx]
|
|
2176
|
+
|
|
2177
|
+
if return_additional_info:
|
|
2178
|
+
return lines, lines_idx, num_points, len(detected_lines_idx), len(selected_lines_idx), len(lines_idx)
|
|
2179
|
+
return lines, lines_idx
|