FreeCAD

Форк
0
/
DraftVecUtils.py 
886 строк · 24.5 Кб
1
# ***************************************************************************
2
# *   Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net>        *
3
# *   Copyright (c) 2009, 2010 Ken Cline <cline@frii.com>                   *
4
# *                                                                         *
5
# *   This program is free software; you can redistribute it and/or modify  *
6
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
7
# *   as published by the Free Software Foundation; either version 2 of     *
8
# *   the License, or (at your option) any later version.                   *
9
# *   for detail see the LICENCE text file.                                 *
10
# *                                                                         *
11
# *   This program is distributed in the hope that it will be useful,       *
12
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
# *   GNU Library General Public License for more details.                  *
15
# *                                                                         *
16
# *   You should have received a copy of the GNU Library General Public     *
17
# *   License along with this program; if not, write to the Free Software   *
18
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
19
# *   USA                                                                   *
20
# *                                                                         *
21
# ***************************************************************************
22
"""Provide vector math utilities used in the Draft workbench.
23

24
Vector math utilities used primarily in the Draft workbench
25
but which can also be used in other workbenches and in macros.
26
"""
27
## \defgroup DRAFTVECUTILS DraftVecUtils
28
#  \ingroup UTILITIES
29
#  \brief Vector math utilities used in Draft workbench
30
#
31
# Vector math utilities used primarily in the Draft workbench
32
# but which can also be used in other workbenches and in macros.
33

34
# Check code with
35
# flake8 --ignore=E226,E266,E401,W503
36

37
import math
38

39
import FreeCAD
40
from FreeCAD import Vector
41
from draftutils import params
42
from draftutils import messages
43

44
__title__ = "FreeCAD Draft Workbench - Vector library"
45
__author__ = "Yorik van Havre, Werner Mayer, Martin Burbaum, Ken Cline"
46
__url__ = "https://www.freecad.org"
47

48
## \addtogroup DRAFTVECUTILS
49
#  @{
50

51

52
def precision():
53
    """Get the number of decimal numbers used for precision.
54

55
    Returns
56
    -------
57
    int
58
        Return the number of decimal places set up in the preferences,
59
        or a standard value (6), if the parameter is missing.
60
    """
61
    return params.get_param("precision")
62

63

64
def typecheck(args_and_types, name="?"):
65
    """Check that the arguments are instances of certain types.
66

67
    Parameters
68
    ----------
69
    args_and_types : list
70
        A list of tuples. The first element of a tuple is tested as being
71
        an instance of the second element.
72
        ::
73
            args_and_types = [(a, Type), (b, Type2), ...]
74

75
        Then
76
        ::
77
            isinstance(a, Type)
78
            isinstance(b, Type2)
79

80
        A `Type` can also be a tuple of many types, in which case
81
        the check is done for any of them.
82
        ::
83
            args_and_types = [(a, (Type3, int, float)), ...]
84

85
            isinstance(a, (Type3, int, float))
86

87
    name : str, optional
88
        Defaults to `'?'`. The name of the check.
89

90
    Raises
91
    ------
92
    TypeError
93
        If the first element in the tuple is not an instance of the second
94
        element.
95
    """
96
    for v, t in args_and_types:
97
        if not isinstance(v, t):
98
            messages._wrn("typecheck[{0}]: {1} is not {2}".format(name, v, t))
99
            raise TypeError("fcvec." + str(name))
100

101

102
def toString(u):
103
    """Return a string with the Python command to recreate this vector.
104

105
    Parameters
106
    ----------
107
    u : list, or Base::Vector3
108
        A list of FreeCAD.Vectors, or a single vector.
109

110
    Returns
111
    -------
112
    str
113
        The string with the code that can be used in the Python console
114
        to create the same list of vectors, or single vector.
115
    """
116
    if isinstance(u, list):
117
        s = "["
118
        first = True
119
        for v in u:
120
            s += "FreeCAD.Vector("
121
            s += str(v.x) + ", " + str(v.y) + ", " + str(v.z)
122
            s += ")"
123
            # This test isn't needed, because `first` never changes value?
124
            if first:
125
                s += ", "
126
                first = True
127
        # Remove the last comma
128
        s = s.rstrip(", ")
129
        s += "]"
130
        return s
131
    else:
132
        s = "FreeCAD.Vector("
133
        s += str(u.x) + ", " + str(u.y) + ", " + str(u.z)
134
        s += ")"
135
        return s
136

137

138
def tup(u, array=False):
139
    """Return a tuple or a list with the coordinates of a vector.
140

141
    Parameters
142
    ----------
143
    u : Base::Vector3
144
        A FreeCAD.Vector.
145
    array : bool, optional
146
        Defaults to `False`, and the output is a tuple.
147
        If `True` the output is a list.
148

149
    Returns
150
    -------
151
    tuple or list
152
        The coordinates of the vector in a tuple `(x, y, z)`
153
        or in a list `[x, y, z]`, if `array=True`.
154
    """
155
    typecheck([(u, Vector)], "tup")
156
    if array:
157
        return [u.x, u.y, u.z]
158
    else:
159
        return (u.x, u.y, u.z)
160

161

162
def neg(u):
163
    """Return the negative of a given vector.
164

165
    Parameters
166
    ----------
167
    u : Base::Vector3
168
        A FreeCAD.Vector.
169

170
    Returns
171
    -------
172
    Base::Vector3
173
        A vector in which each element has the opposite sign of
174
        the original element.
175
    """
176
    typecheck([(u, Vector)], "neg")
177
    return Vector(-u.x, -u.y, -u.z)
178

179

180
def equals(u, v):
181
    """Check for equality between two vectors.
182

183
    Due to rounding errors, two vectors will rarely be `equal`.
184
    Therefore, this function checks that the corresponding elements
185
    of the two vectors differ by less than the decimal `precision` established
186
    in the parameter database, accessed through `FreeCAD.ParamGet()`.
187
    ::
188
        x1 - x2 < precision
189
        y1 - y2 < precision
190
        z1 - z2 < precision
191

192
    Parameters
193
    ----------
194
    u : Base::Vector3
195
        The first vector.
196
    v : Base::Vector3
197
        The second vector.
198

199
    Returns
200
    -------
201
    bool
202
        `True` if the vectors are within the precision, `False` otherwise.
203
    """
204
    typecheck([(u, Vector), (v, Vector)], "equals")
205
    return isNull(u.sub(v))
206

207

208
def scale(u, scalar):
209
    """Scales (multiplies) a vector by a scalar factor.
210

211
    Parameters
212
    ----------
213
    u : Base::Vector3
214
        The FreeCAD.Vector to scale.
215
    scalar : float
216
        The scaling factor.
217

218
    Returns
219
    -------
220
    Base::Vector3
221
        The new vector with each of its elements multiplied by `scalar`.
222
    """
223
    typecheck([(u, Vector), (scalar, (int, int, float))], "scale")
224
    return Vector(u.x*scalar, u.y*scalar, u.z*scalar)
225

226

227
def scaleTo(u, l):
228
    """Scale a vector so that its magnitude is equal to a given length.
229

230
    The magnitude of a vector is
231
    ::
232
        L = sqrt(x**2 + y**2 + z**2)
233

234
    This function multiplies each coordinate, `x`, `y`, `z`,
235
    by a factor to produce the desired magnitude `L`.
236
    This factor is the ratio of the new magnitude to the old magnitude,
237
    ::
238
        x_scaled = x * (L_new/L_old)
239

240
    Parameters
241
    ----------
242
    u : Base::Vector3
243
        The vector to scale.
244
    l : int or float
245
        The new magnitude of the vector in standard units (mm).
246

247
    Returns
248
    -------
249
    Base::Vector3
250
        The new vector with each of its elements scaled by a factor.
251
        Or the same input vector `u`, if it is `(0, 0, 0)`.
252
    """
253
    typecheck([(u, Vector), (l, (int, int, float))], "scaleTo")
254
    if u.Length == 0:
255
        return Vector(u)
256
    else:
257
        a = l/u.Length
258
        return Vector(u.x*a, u.y*a, u.z*a)
259

260

261
def dist(u, v):
262
    """Return the distance between two points (or vectors).
263

264
    Parameters
265
    ----------
266
    u : Base::Vector3
267
        First point, defined by a vector.
268
    v : Base::Vector3
269
        Second point, defined by a vector.
270

271
    Returns
272
    -------
273
    float
274
        The scalar distance from one point to the other.
275
    """
276
    typecheck([(u, Vector), (v, Vector)], "dist")
277
    return u.sub(v).Length
278

279

280
def angle(u, v=Vector(1, 0, 0), normal=Vector(0, 0, 1)):
281
    """Return the angle in radians between the two vectors.
282

283
    It uses the definition of the dot product
284
    ::
285
        A * B = |A||B| cos(angle)
286

287
    If only one vector is given, the angle is between that one and the
288
    horizontal (+X).
289

290
    If a third vector is given, it is the normal used to determine
291
    the sign of the angle.
292
    This normal is used to calculate a `factor` as the dot product
293
    with the cross product of the first two vectors.
294
    ::
295
        C = A x B
296
        factor = normal * C
297

298
    If the `factor` is positive the angle is positive, otherwise
299
    it is the opposite sign.
300

301
    Parameters
302
    ----------
303
    u : Base::Vector3
304
        The first vector.
305
    v : Base::Vector3, optional
306
        The second vector to test against the first one.
307
        It defaults to `(1, 0, 0)`, or +X.
308
    normal : Base::Vector3, optional
309
        The vector indicating the normal.
310
        It defaults to `(0, 0, 1)`, or +Z.
311

312
    Returns
313
    -------
314
    float
315
        The angle in radians between the vectors.
316
        It is zero if the magnitude of one of the vectors is zero,
317
        or if they are colinear.
318
    """
319
    typecheck([(u, Vector), (v, Vector)], "angle")
320
    ll = u.Length * v.Length
321
    if ll == 0:
322
        return 0
323

324
    # The dot product indicates the projection of one vector over the other
325
    dp = u.dot(v)/ll
326

327
    # Due to rounding errors, the dot product could be outside
328
    # the range [-1, 1], so let's force it to be within this range.
329
    if dp < -1:
330
        dp = -1
331
    elif dp > 1:
332
        dp = 1
333

334
    ang = math.acos(dp)
335

336
    # The cross product compared with the provided normal
337
    normal1 = u.cross(v)
338
    coeff = normal.dot(normal1)
339
    if coeff >= 0:
340
        return ang
341
    else:
342
        return -ang
343

344

345
def project(u, v):
346
    """Project the first vector onto the second one.
347

348
    The projection is just the second vector scaled by a factor.
349
    This factor is the dot product divided by the square
350
    of the second vector's magnitude.
351
    ::
352
        f = A * B / |B|**2 = |A||B| cos(angle) / |B|**2
353
        f = |A| cos(angle)/|B|
354

355
    Parameters
356
    ----------
357
    u : Base::Vector3
358
        The first vector.
359
    v : Base::Vector3
360
        The second vector.
361

362
    Returns
363
    -------
364
    Base::Vector3
365
        The new vector, which is the same vector `v` scaled by a factor.
366
        Return `Vector(0, 0, 0)`, if the magnitude of the second vector
367
        is zero.
368
    """
369
    typecheck([(u, Vector), (v, Vector)], "project")
370

371
    # Dot product with itself equals the magnitude squared.
372
    dp = v.dot(v)
373
    if dp == 0:
374
        return Vector(0, 0, 0)  # to avoid division by zero
375
    # Why specifically this value? This should be an else?
376
    if dp != 15:
377
        return scale(v, u.dot(v)/dp)
378

379
    # Return a null vector if the magnitude squared is 15, why?
380
    return Vector(0, 0, 0)
381

382

383
def rotate2D(u, angle):
384
    """Rotate the given vector around the Z axis by the specified angle.
385

386
    The rotation occurs in two dimensions only by means of
387
    a rotation matrix.
388
    ::
389
         u_rot                R                 u
390
        (x_rot) = (cos(-angle) -sin(-angle)) * (x)
391
        (y_rot)   (sin(-angle)  cos(-angle))   (y)
392

393
    Normally the angle is positive, but in this case it is negative.
394

395
    `"Such non-standard orientations are rarely used in mathematics
396
    but are common in 2D computer graphics, which often have the origin
397
    in the top left corner and the y-axis pointing down."`
398
    W3C Recommendations (2003), Scalable Vector Graphics: the initial
399
    coordinate system.
400

401
    Parameters
402
    ----------
403
    u : Base::Vector3
404
        The vector.
405
    angle : float
406
        The angle of rotation given in radians.
407

408
    Returns
409
    -------
410
    Base::Vector3
411
        The new rotated vector.
412
    """
413
    x_rot = math.cos(-angle) * u.x - math.sin(-angle) * u.y
414
    y_rot = math.sin(-angle) * u.x + math.cos(-angle) * u.y
415

416
    return Vector(x_rot, y_rot, u.z)
417

418

419
def rotate(u, angle, axis=Vector(0, 0, 1)):
420
    """Rotate the vector by the specified angle, around the given axis.
421

422
    If the axis is omitted, the rotation is made around the Z axis
423
    (on the XY plane).
424

425
    It uses a 3x3 rotation matrix.
426
    ::
427
        u_rot = R u
428

429
                (c + x*x*t    xyt - zs     xzt + ys )
430
        u_rot = (xyt + zs     c + y*y*t    yzt - xs ) * u
431
                (xzt - ys     yzt + xs     c + z*z*t)
432

433
    Where `x`, `y`, `z` indicate unit components of the axis;
434
    `c` denotes a cosine of the angle; `t` indicates a complement
435
    of that cosine; `xs`, `ys`, `zs` indicate products of the unit
436
    components and the sine of the angle; and `xyt`, `xzt`, `yzt`
437
    indicate products of two unit components and the complement
438
    of the cosine.
439

440
    Parameters
441
    ----------
442
    u : Base::Vector3
443
        The vector.
444
    angle : float
445
        The angle of rotation given in radians.
446
    axis : Base::Vector3, optional
447
        The vector specifying the axis of rotation.
448
        It defaults to `(0, 0, 1)`, the +Z axis.
449

450
    Returns
451
    -------
452
    Base::Vector3
453
        The new rotated vector.
454
        If the `angle` is zero, return the original vector `u`.
455
    """
456
    typecheck([(u, Vector), (angle, (int, float)), (axis, Vector)], "rotate")
457

458
    if angle == 0:
459
        return u
460

461
    # Unit components, so that x**2 + y**2 + z**2 = 1
462
    L = axis.Length
463
    x = axis.x/L
464
    y = axis.y/L
465
    z = axis.z/L
466

467
    c = math.cos(angle)
468
    s = math.sin(angle)
469
    t = 1 - c
470

471
    # Various products
472
    xyt = x * y * t
473
    xzt = x * z * t
474
    yzt = y * z * t
475
    xs = x * s
476
    ys = y * s
477
    zs = z * s
478

479
    m = FreeCAD.Matrix(c + x*x*t,   xyt - zs,   xzt + ys,   0,
480
                       xyt + zs,    c + y*y*t,  yzt - xs,   0,
481
                       xzt - ys,    yzt + xs,   c + z*z*t,  0)
482

483
    return m.multiply(u)
484

485

486
def getRotation(vector, reference=Vector(1, 0, 0)):
487
    """Return a quaternion rotation between a vector and a reference.
488

489
    If the reference is omitted, the +X axis is used.
490

491
    Parameters
492
    ----------
493
    vector : Base::Vector3
494
        The original vector.
495
    reference : Base::Vector3, optional
496
        The reference vector. It defaults to `(1, 0, 0)`, the +X axis.
497

498
    Returns
499
    -------
500
    (x, y, z, Q)
501
        A tuple with the unit elements (normalized) of the cross product
502
        between the `vector` and the `reference`, and a `Q` value,
503
        which is the sum of the products of the magnitudes,
504
        and of the dot product of those vectors.
505
        ::
506
            Q = |A||B| + |A||B| cos(angle)
507

508
        It returns `(0, 0, 0, 1.0)`
509
        if the cross product between the `vector` and the `reference`
510
        is null.
511

512
    See Also
513
    --------
514
    rotate2D, rotate
515
    """
516
    c = vector.cross(reference)
517
    if isNull(c):
518
        return (0, 0, 0, 1.0)
519
    c.normalize()
520

521
    q1 = math.sqrt((vector.Length**2) * (reference.Length**2))
522
    q2 = vector.dot(reference)
523
    Q = q1 + q2
524

525
    return (c.x, c.y, c.z, Q)
526

527

528
def isNull(vector):
529
    """Return False if each of the components of the vector is zero.
530

531
    Due to rounding errors, an element is probably never going to be
532
    exactly zero. Therefore, it rounds the element by the number
533
    of decimals specified in the `precision` parameter
534
    in the parameter database, accessed through `FreeCAD.ParamGet()`.
535
    It then compares the rounded numbers against zero.
536

537
    Parameters
538
    ----------
539
    vector : Base::Vector3
540
        The tested vector.
541

542
    Returns
543
    -------
544
    bool
545
        `True` if each of the elements is zero within the precision.
546
        `False` otherwise.
547
    """
548
    p = precision()
549
    x = round(vector.x, p)
550
    y = round(vector.y, p)
551
    z = round(vector.z, p)
552
    return (x == 0 and y == 0 and z == 0)
553

554

555
def find(vector, vlist):
556
    """Find a vector in a list of vectors, and return the index.
557

558
    Finding a vector tests for `equality` which depends on the `precision`
559
    parameter in the parameter database.
560

561
    Parameters
562
    ----------
563
    vector : Base::Vector3
564
        The tested vector.
565
    vlist : list
566
        A list of Base::Vector3 vectors.
567

568
    Returns
569
    -------
570
    int
571
        The index of the list where the vector is found,
572
        or `None` if the vector is not found.
573

574
    See Also
575
    --------
576
    equals : test for equality between two vectors
577
    """
578
    typecheck([(vector, Vector), (vlist, list)], "find")
579
    for i, v in enumerate(vlist):
580
        if equals(vector, v):
581
            return i
582
    return None
583

584

585
def closest(vector, vlist, return_length=False):
586
    """Find the closest point to one point in a list of points (vectors).
587

588
    The scalar distance between the original point and one point in the list
589
    is calculated. If the distance is smaller than a previously calculated
590
    value, its index is saved, otherwise the next point in the list is tested.
591

592
    Parameters
593
    ----------
594
    vector: Base::Vector3
595
        The tested point or vector.
596

597
    vlist: list
598
        A list of points or vectors.
599

600
    return_length: bool, optional
601
        It defaults to `False`.
602
        If it is `True`, the value of the smallest distance will be returned.
603

604
    Returns
605
    -------
606
    int
607
        The index of the list where the closest point is found.
608

609
    int, float
610
        If `return_length` is `True`, it returns both the index
611
        and the length to the closest point.
612
    """
613
    typecheck([(vector, Vector), (vlist, list)], "closest")
614

615
    # Initially test against a very large distance, then test the next point
616
    # in the list which will probably be much smaller.
617
    dist = 9999999999999999
618
    index = None
619
    for i, v in enumerate(vlist):
620
        d = (vector - v).Length
621
        if d < dist:
622
            dist = d
623
            index = i
624

625
    if return_length:
626
        return index, dist
627
    else:
628
        return index
629

630

631
def isColinear(vlist):
632
    """Check if the vectors in the list are colinear.
633

634
    Colinear vectors are those whose angle between them is zero.
635

636
    This function tests for colinearity between the difference
637
    of the first two vectors, and the difference of the nth vector with
638
    the first vector.
639
    ::
640
        vlist = [a, b, c, d, ..., n]
641

642
        k = b - a
643
        k2 = c - a
644
        k3 = d - a
645
        kn = n - a
646

647
    Then test
648
    ::
649
        angle(k2, k) == 0
650
        angle(k3, k) == 0
651
        angle(kn, k) == 0
652

653
    Parameters
654
    ----------
655
    vlist : list
656
        List of Base::Vector3 vectors.
657
        At least three elements must be present.
658

659
    Returns
660
    -------
661
    bool
662
        `True` if the vector differences are colinear,
663
        or if the list only has two vectors.
664
        `False` otherwise.
665

666
    Notes
667
    -----
668
    Due to rounding errors, the angle may not be exactly zero;
669
    therefore, it rounds the angle by the number
670
    of decimals specified in the `precision` parameter
671
    in the parameter database, and then compares the value to zero.
672
    """
673
    typecheck([(vlist, list)], "isColinear")
674

675
    # Return True if the list only has two vectors, why?
676
    # This doesn't test for colinearity between the first two vectors.
677
    if len(vlist) < 3:
678
        return True
679

680
    p = precision()
681

682
    # Difference between the second vector and the first one
683
    first = vlist[1].sub(vlist[0])
684

685
    # Start testing from the third vector and onward
686
    for i in range(2, len(vlist)):
687

688
        # Difference between the 3rd vector and onward, and the first one.
689
        diff = vlist[i].sub(vlist[0])
690

691
        # The angle between the difference and the first difference.
692
        _angle = angle(diff, first)
693

694
        if round(_angle, p) != 0:
695
            return False
696
    return True
697

698

699
def rounded(v,d=None):
700
    """Return a vector rounded to the `precision` in the parameter database
701
    or to the given decimals value
702

703
    Each of the components of the vector is rounded to the decimal
704
    precision set in the parameter database.
705

706
    Parameters
707
    ----------
708
    v : Base::Vector3
709
        The input vector.
710
    d : (Optional) the number of decimals to round to
711

712
    Returns
713
    -------
714
    Base::Vector3
715
        The new vector where each element `x`, `y`, `z` has been rounded
716
        to the number of decimals specified in the `precision` parameter
717
        in the parameter database.
718
    """
719
    p = precision()
720
    if d:
721
        p = d
722
    return Vector(round(v.x, p), round(v.y, p), round(v.z, p))
723

724

725
def getPlaneRotation(u, v, _ = None):
726
    """Return a rotation matrix defining the (u,v,w) coordinate system.
727

728
    The rotation matrix uses the elements from each vector.
729
    `v` is adjusted to be perpendicular to `u`
730
    ::
731
            (u.x  v.x  w.x  0  )
732
        R = (u.y  v.y  w.y  0  )
733
            (u.z  v.z  w.z  0  )
734
            (0    0    0    1.0)
735

736
    Parameters
737
    ----------
738
    u : Base::Vector3
739
        The first vector.
740
    v : Base::Vector3
741
        Hint for the second vector.
742
    _ : Ignored. For backwards compatibility
743

744
    Returns
745
    -------
746
    Base::Matrix4D
747
        The new rotation matrix defining a new coordinate system,
748
        or `None` if `u` or `v` is `None` or
749
        if `u` and `v` are parallel.
750
    """
751
    if (not u) or (not v):
752
        return None
753
    typecheck([(u, Vector), (v, Vector)], "getPlaneRotation")
754
    u = Vector(u)
755
    u.normalize()
756
    w = u.cross(v)
757
    if not w.Length:
758
        return None
759
    w.normalize()
760
    v = w.cross(u)
761

762
    m = FreeCAD.Matrix(u.x, v.x, w.x, 0,
763
                       u.y, v.y, w.y, 0,
764
                       u.z, v.z, w.z, 0,
765
                       0.0, 0.0, 0.0, 1.0)
766
    return m
767

768

769
def removeDoubles(vlist):
770
    """Remove duplicated vectors from a list of vectors.
771

772
    It removes only the duplicates that are next to each other in the list.
773

774
    It tests the `i` element, and compares it to the `i+1` element.
775
    If the former one is different from the latter,
776
    the former is added to the new list, otherwise it is skipped.
777
    The last element is always included.
778
    ::
779
        [a, b, b, c, c] -> [a, b, c]
780
        [a, a, b, a, a, b] -> [a, b, a, b]
781

782
    Finding duplicated vectors tests for `equality` which depends
783
    on the `precision` parameter in the parameter database.
784

785
    Parameters
786
    ----------
787
    vlist : list of Base::Vector3
788
        List with vectors.
789

790
    Returns
791
    -------
792
    list of Base::Vector3
793
        New list with sequential duplicates removed,
794
        or the original `vlist` if there is only one element in the list.
795

796
    See Also
797
    --------
798
    equals : test for equality between two vectors
799
    """
800
    typecheck([(vlist, list)], "removeDoubles")
801
    nlist = []
802
    if len(vlist) < 2:
803
        return vlist
804

805
    # Iterate until the penultimate element, and test for equality
806
    # with the element in front
807
    for i in range(len(vlist) - 1):
808
        if not equals(vlist[i], vlist[i+1]):
809
            nlist.append(vlist[i])
810
    # Add the last element
811
    nlist.append(vlist[-1])
812
    return nlist
813

814
def get_spherical_coords(x, y, z):
815
    """Get the Spherical coordinates of the vector represented
816
    by Cartesian coordinates (x, y, z).
817

818
    Parameters
819
    ----------
820
    vector : Base::Vector3
821
        The input vector.
822

823
    Returns
824
    -------
825
    tuple of float
826
        Tuple (radius, theta, phi) with the Spherical coordinates.
827
        Radius is the radial coordinate, theta the polar angle and
828
        phi the azimuthal angle in radians.
829

830
    Notes
831
    -----
832
    The vector (0, 0, 0) has undefined values for theta and phi, while
833
    points on the z axis has undefined value for phi. The following
834
    conventions are used (useful in DraftToolBar methods):
835
    (0, 0, 0) -> (0, pi/2, 0)
836
    (0, 0, z) -> (radius, theta, 0)
837
    """
838

839
    v = Vector(x,y,z)
840
    x_axis = Vector(1,0,0)
841
    z_axis = Vector(0,0,1)
842
    y_axis = Vector(0,1,0)
843
    rad = v.Length
844

845
    if not bool(round(rad, precision())):
846
        return (0, math.pi/2, 0)
847

848
    theta = v.getAngle(z_axis)
849
    v.projectToPlane(Vector(0,0,0), z_axis)
850
    phi = v.getAngle(x_axis)
851
    if math.isnan(phi):
852
        return (rad, theta, 0)
853
    # projected vector is on 3rd or 4th quadrant
854
    if v.dot(Vector(y_axis)) < 0:
855
        phi = -1*phi
856

857
    return (rad, theta, phi)
858

859

860
def get_cartesian_coords(radius, theta, phi):
861
    """Get the three-dimensional Cartesian coordinates of the vector
862
    represented by Spherical coordinates (radius, theta, phi).
863

864
    Parameters
865
    ----------
866
    radius : float, int
867
        Radial coordinate of the vector.
868
    theta : float, int
869
        Polar coordinate of the vector in radians.
870
    phi : float, int
871
        Azimuthal coordinate of the vector in radians.
872

873
    Returns
874
    -------
875
    tuple of float :
876
        Tuple (x, y, z) with the Cartesian coordinates.
877
    """
878

879
    x = radius*math.sin(theta)*math.cos(phi)
880
    y = radius*math.sin(theta)*math.sin(phi)
881
    z = radius*math.cos(theta)
882

883
    return (x, y, z)
884

885

886
##  @}
887

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.