pygeodesy 24.8.4__py2.py3-none-any.whl → 24.8.24__py2.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.
- {PyGeodesy-24.8.4.dist-info → PyGeodesy-24.8.24.dist-info}/METADATA +16 -15
- {PyGeodesy-24.8.4.dist-info → PyGeodesy-24.8.24.dist-info}/RECORD +50 -50
- {PyGeodesy-24.8.4.dist-info → PyGeodesy-24.8.24.dist-info}/WHEEL +1 -1
- pygeodesy/__init__.py +23 -23
- pygeodesy/auxilats/auxDST.py +2 -2
- pygeodesy/basics.py +3 -3
- pygeodesy/cartesianBase.py +5 -5
- pygeodesy/constants.py +11 -11
- pygeodesy/ellipsoidalBase.py +18 -15
- pygeodesy/ellipsoidalExact.py +2 -2
- pygeodesy/ellipsoidalGeodSolve.py +2 -2
- pygeodesy/ellipsoidalKarney.py +2 -2
- pygeodesy/ellipsoidalNvector.py +2 -2
- pygeodesy/ellipsoidalVincenty.py +7 -6
- pygeodesy/epsg.py +3 -3
- pygeodesy/fmath.py +2 -1
- pygeodesy/fsums.py +4 -4
- pygeodesy/gars.py +9 -8
- pygeodesy/geodesicx/gx.py +3 -3
- pygeodesy/geodesicx/gxarea.py +3 -3
- pygeodesy/geodsolve.py +3 -3
- pygeodesy/geohash.py +18 -11
- pygeodesy/geoids.py +293 -315
- pygeodesy/heights.py +150 -158
- pygeodesy/internals.py +21 -1
- pygeodesy/interns.py +2 -3
- pygeodesy/latlonBase.py +13 -7
- pygeodesy/lazily.py +6 -6
- pygeodesy/ltp.py +5 -6
- pygeodesy/ltpTuples.py +7 -1
- pygeodesy/named.py +5 -4
- pygeodesy/namedTuples.py +14 -1
- pygeodesy/osgr.py +7 -7
- pygeodesy/points.py +2 -2
- pygeodesy/resections.py +7 -7
- pygeodesy/rhumb/solve.py +3 -3
- pygeodesy/simplify.py +10 -10
- pygeodesy/sphericalBase.py +3 -3
- pygeodesy/sphericalTrigonometry.py +2 -2
- pygeodesy/streprs.py +3 -3
- pygeodesy/triaxials.py +207 -201
- pygeodesy/units.py +3 -3
- pygeodesy/unitsBase.py +4 -4
- pygeodesy/utmupsBase.py +3 -3
- pygeodesy/vector2d.py +158 -51
- pygeodesy/vector3d.py +13 -52
- pygeodesy/vector3dBase.py +81 -63
- pygeodesy/webmercator.py +3 -3
- pygeodesy/wgrs.py +20 -22
- {PyGeodesy-24.8.4.dist-info → PyGeodesy-24.8.24.dist-info}/top_level.txt +0 -0
pygeodesy/triaxials.py
CHANGED
|
@@ -30,10 +30,10 @@ see the U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
|
|
|
30
30
|
# make sure int/int division yields float quotient, see .basics
|
|
31
31
|
from __future__ import division as _; del _ # PYCHOK semicolon
|
|
32
32
|
|
|
33
|
-
from pygeodesy.basics import isLatLon, isscalar,
|
|
33
|
+
from pygeodesy.basics import isLatLon, isscalar, _ValueError
|
|
34
34
|
from pygeodesy.constants import EPS, EPS0, EPS02, EPS4, INT0, PI2, PI_3, PI4, \
|
|
35
|
-
_EPS2e4, float0_, isfinite, isnear1,
|
|
36
|
-
_1_0, _N_1_0,
|
|
35
|
+
_EPS2e4, float0_, isfinite, isnear1, _over, \
|
|
36
|
+
_0_0, _0_5, _1_0, _N_1_0, _4_0 # PYCHOK used!
|
|
37
37
|
from pygeodesy.datums import Datum, _spherical_datum, _WGS84, Ellipsoid, _EWGS84, Fmt
|
|
38
38
|
# from pygeodesy.ellipsoids import Ellipsoid, _EWGS84 # from .datums
|
|
39
39
|
# from pygeodesy.elliptic import Elliptic # _MODS
|
|
@@ -59,11 +59,11 @@ from pygeodesy.vector3d import _otherV3d, Vector3d, _ALL_LAZY, _MODS
|
|
|
59
59
|
from math import atan2, fabs, sqrt
|
|
60
60
|
|
|
61
61
|
__all__ = _ALL_LAZY.triaxials
|
|
62
|
-
__version__ = '24.
|
|
62
|
+
__version__ = '24.08.18'
|
|
63
63
|
|
|
64
64
|
_not_ordered_ = _not_('ordered')
|
|
65
65
|
_omega_ = 'omega'
|
|
66
|
-
_TRIPS =
|
|
66
|
+
_TRIPS = 359 # Eberly 1074?
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
class _NamedTupleToX(_NamedTupleTo): # in .testNamedTuples
|
|
@@ -99,7 +99,7 @@ class BetaOmega2Tuple(_NamedTupleToX):
|
|
|
99
99
|
def toDegrees(self, name=NN, **toDMS_kwds):
|
|
100
100
|
'''Convert this L{BetaOmega2Tuple} to L{Degrees} or C{toDMS}.
|
|
101
101
|
|
|
102
|
-
@kwarg name: Optional name (C{str})
|
|
102
|
+
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
103
103
|
|
|
104
104
|
@return: L{BetaOmega2Tuple}C{(beta, omega)} with C{beta} and
|
|
105
105
|
C{omega} both in L{Degrees} or as L{toDMS} strings
|
|
@@ -111,7 +111,7 @@ class BetaOmega2Tuple(_NamedTupleToX):
|
|
|
111
111
|
def toRadians(self, **name):
|
|
112
112
|
'''Convert this L{BetaOmega2Tuple} to L{Radians}.
|
|
113
113
|
|
|
114
|
-
@kwarg name: Optional C{B{name}=NN} (C{str})
|
|
114
|
+
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
115
115
|
|
|
116
116
|
@return: L{BetaOmega2Tuple}C{(beta, omega)} with C{beta} and C{omega}
|
|
117
117
|
both in L{Radians}.
|
|
@@ -132,7 +132,7 @@ class BetaOmega3Tuple(_NamedTupleToX):
|
|
|
132
132
|
def toDegrees(self, name=NN, **toDMS_kwds):
|
|
133
133
|
'''Convert this L{BetaOmega3Tuple} to L{Degrees} or C{toDMS}.
|
|
134
134
|
|
|
135
|
-
@kwarg name: Optional name (C{str})
|
|
135
|
+
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
136
136
|
|
|
137
137
|
@return: L{BetaOmega3Tuple}C{(beta, omega, height)} with
|
|
138
138
|
C{beta} and C{omega} both in L{Degrees} or as
|
|
@@ -169,7 +169,7 @@ class Jacobi2Tuple(_NamedTupleToX):
|
|
|
169
169
|
def toDegrees(self, name=NN, **toDMS_kwds):
|
|
170
170
|
'''Convert this L{Jacobi2Tuple} to L{Degrees} or C{toDMS}.
|
|
171
171
|
|
|
172
|
-
@kwarg name: Optional name (C{str})
|
|
172
|
+
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
173
173
|
|
|
174
174
|
@return: L{Jacobi2Tuple}C{(x, y)} with C{x} and C{y} both
|
|
175
175
|
in L{Degrees} or as L{toDMS} strings provided some
|
|
@@ -180,7 +180,7 @@ class Jacobi2Tuple(_NamedTupleToX):
|
|
|
180
180
|
def toRadians(self, **name):
|
|
181
181
|
'''Convert this L{Jacobi2Tuple} to L{Radians}.
|
|
182
182
|
|
|
183
|
-
@kwarg name: Optional C{B{name}=NN} (C{str})
|
|
183
|
+
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
184
184
|
|
|
185
185
|
@return: L{Jacobi2Tuple}C{(x, y)} with C{x} and C{y} both in L{Radians}.
|
|
186
186
|
'''
|
|
@@ -244,7 +244,7 @@ class Triaxial_(_NamedEnumItem):
|
|
|
244
244
|
|
|
245
245
|
@Property_RO
|
|
246
246
|
def a(self):
|
|
247
|
-
'''Get the
|
|
247
|
+
'''Get the C{largest, x} semi-axis (C{meter}, conventionally).
|
|
248
248
|
'''
|
|
249
249
|
a, _, _ = self._abc3
|
|
250
250
|
return a
|
|
@@ -300,7 +300,7 @@ class Triaxial_(_NamedEnumItem):
|
|
|
300
300
|
|
|
301
301
|
@Property_RO
|
|
302
302
|
def b(self):
|
|
303
|
-
'''Get the
|
|
303
|
+
'''Get the C{middle, y} semi-axis (C{meter}, same units as B{C{a}}).
|
|
304
304
|
'''
|
|
305
305
|
_, b, _ = self._abc3
|
|
306
306
|
return b
|
|
@@ -314,7 +314,7 @@ class Triaxial_(_NamedEnumItem):
|
|
|
314
314
|
|
|
315
315
|
@Property_RO
|
|
316
316
|
def c(self):
|
|
317
|
-
'''Get the
|
|
317
|
+
'''Get the C{smallest, z} semi-axis (C{meter}, same units as B{C{a}}).
|
|
318
318
|
'''
|
|
319
319
|
_, _, c = self._abc3
|
|
320
320
|
return c
|
|
@@ -382,8 +382,9 @@ class Triaxial_(_NamedEnumItem):
|
|
|
382
382
|
@kwarg y: Y component (C{scalar}), required if B{C{x_xyz}} if C{scalar}, ignored
|
|
383
383
|
otherwise.
|
|
384
384
|
@kwarg z: Z component (C{scalar}), like B{C{y}}.
|
|
385
|
-
@kwarg normal: If C{True} the projection is the I{
|
|
386
|
-
otherwise the C{radial} line to the center of
|
|
385
|
+
@kwarg normal: If C{True}, the projection is the I{perpendicular, plumb} to the
|
|
386
|
+
triaxial's surface, otherwise the C{radial} line to the center of
|
|
387
|
+
this triaxial (C{bool}).
|
|
387
388
|
@kwarg eps: Tolerance for root finding and validation (C{scalar}), use a negative
|
|
388
389
|
value to skip validation.
|
|
389
390
|
@kwarg name: Optional C{B{name}="heigh4"} (C{str}).
|
|
@@ -395,8 +396,8 @@ class Triaxial_(_NamedEnumItem):
|
|
|
395
396
|
@raise TriaxialError: Non-cartesian B{C{xyz}}, invalid B{C{eps}}, no convergence in
|
|
396
397
|
root finding or validation failed.
|
|
397
398
|
|
|
398
|
-
@see:
|
|
399
|
-
|
|
399
|
+
@see: Methods L{Triaxial.normal3d} and L{Ellipsoid.height4} and I{Eberly}'s U{Distance from a
|
|
400
|
+
Point to ...<https://www.GeometricTools.com/Documentation/DistancePointEllipseEllipsoid.pdf>}.
|
|
400
401
|
'''
|
|
401
402
|
v, r = _otherV3d_(x_xyz, y, z), self.isSpherical
|
|
402
403
|
|
|
@@ -405,10 +406,10 @@ class Triaxial_(_NamedEnumItem):
|
|
|
405
406
|
x = y = z = _0_0
|
|
406
407
|
h -= min(self._abc3) # nearest
|
|
407
408
|
elif r: # .isSpherical
|
|
408
|
-
x, y, z = v.times(r / h).
|
|
409
|
+
x, y, z = v.times(r / h).xyz3
|
|
409
410
|
h -= r
|
|
410
411
|
else:
|
|
411
|
-
x, y, z = v.
|
|
412
|
+
x, y, z = v.xyz3
|
|
412
413
|
try:
|
|
413
414
|
if normal: # plumb to surface
|
|
414
415
|
x, y, z, h, i = _plumbTo5(x, y, z, self, eps=eps)
|
|
@@ -418,7 +419,7 @@ class Triaxial_(_NamedEnumItem):
|
|
|
418
419
|
except Exception as e:
|
|
419
420
|
raise TriaxialError(x=x, y=y, z=z, cause=e)
|
|
420
421
|
if h > 0 and self.sideOf(v, eps=EPS0) < 0:
|
|
421
|
-
h = -h #
|
|
422
|
+
h = -h # inside
|
|
422
423
|
n = _name__(name, name__=self.height4)
|
|
423
424
|
return Vector4Tuple(x, y, z, h, iteration=i, name=n)
|
|
424
425
|
|
|
@@ -439,46 +440,43 @@ class Triaxial_(_NamedEnumItem):
|
|
|
439
440
|
def _norm2(self, s, c, *a):
|
|
440
441
|
'''(INTERNAL) Normalize C{s} and C{c} iff not already.
|
|
441
442
|
'''
|
|
442
|
-
if fabs(
|
|
443
|
+
if fabs(_hypot2_1(s, c)) > EPS02:
|
|
443
444
|
s, c = norm2(s, c)
|
|
444
445
|
if a:
|
|
445
446
|
s, c = norm2(s * self.b, c * a[0])
|
|
446
447
|
return float0_(s, c)
|
|
447
448
|
|
|
448
449
|
def normal3d(self, x_xyz, y=None, z=None, length=_1_0):
|
|
449
|
-
'''Get a 3-D vector at a cartesian on and perpendicular to this triaxial's surface.
|
|
450
|
+
'''Get a 3-D vector at a cartesian I{on and perpendicular to} this triaxial's surface.
|
|
450
451
|
|
|
451
452
|
@arg x_xyz: X component (C{scalar}) or a cartesian (C{Cartesian},
|
|
452
453
|
L{Ecef9Tuple}, L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
|
|
453
454
|
@kwarg y: Y component (C{scalar}), required if B{C{x_xyz}} if C{scalar}, ignored
|
|
454
455
|
otherwise.
|
|
455
456
|
@kwarg z: Z component (C{scalar}), like B{C{y}}.
|
|
456
|
-
@kwarg length: Optional length and in-/outward direction (C{scalar}).
|
|
457
|
+
@kwarg length: Optional, signed length and in-/outward direction (C{scalar}).
|
|
457
458
|
|
|
458
|
-
@return: A C{Vector3d(x_, y_, z_)} normalized to B{C{length}}, pointing
|
|
459
|
-
|
|
459
|
+
@return: A C{Vector3d(x_, y_, z_)} normalized to B{C{length}}, pointing in- or
|
|
460
|
+
outward for neg- respectively positive B{C{length}}.
|
|
460
461
|
|
|
461
462
|
@raise TriaxialError: Zero length cartesian or vector.
|
|
462
463
|
|
|
463
|
-
@note: Cartesian
|
|
464
|
-
|
|
464
|
+
@note: Cartesian C{(B{x}, B{y}, B{z})} I{must be on} this triaxial's surface, use
|
|
465
|
+
method L{Triaxial.sideOf} to validate.
|
|
466
|
+
|
|
467
|
+
@see: Methods L{Triaxial.height4} and L{Triaxial.sideOf}.
|
|
465
468
|
'''
|
|
466
469
|
# n = 2 * (x / a2, y / b2, z / c2)
|
|
467
470
|
# == 2 * (x, y * a2 / b2, z * a2 / c2) / a2 # iff ordered
|
|
468
471
|
# == 2 * (x, y / _1e2ab, z / _1e2ac) / a2
|
|
469
472
|
# == unit(x, y / _1e2ab, z / _1e2ac).times(length)
|
|
470
|
-
|
|
471
|
-
|
|
473
|
+
x, y, z = _otherV3d_(x_xyz, y, z).xyz3
|
|
474
|
+
n = Vector3d(x, y / self._1e2ab,
|
|
475
|
+
z / self._1e2ac, name__=self.normal3d)
|
|
476
|
+
u = n.length
|
|
477
|
+
if u < EPS0:
|
|
472
478
|
raise TriaxialError(x=x_xyz, y=y, z=z, txt=_null_)
|
|
473
|
-
return n.times(length /
|
|
474
|
-
|
|
475
|
-
@Property_RO
|
|
476
|
-
def _normal3d(self):
|
|
477
|
-
'''(INTERNAL) Get M{Vector3d((d/a)**2, (d/b)**2, (d/c)**2)}, M{d = max(a, b, c)}.
|
|
478
|
-
'''
|
|
479
|
-
d = max(self._abc3)
|
|
480
|
-
t = tuple(((d / x)**2 if x != d else _1_0) for x in self._abc3)
|
|
481
|
-
return Vector3d(*t, name__=self.normal3d)
|
|
479
|
+
return n.times(length / u)
|
|
482
480
|
|
|
483
481
|
def _order3(self, *abc, **reverse): # reverse=False
|
|
484
482
|
'''(INTERNAL) Un-/Order C{a}, C{b} and C{c}.
|
|
@@ -495,7 +493,7 @@ class Triaxial_(_NamedEnumItem):
|
|
|
495
493
|
@return: Vector3d(x, y, z) un-/ordered.
|
|
496
494
|
'''
|
|
497
495
|
ijk = self._order_ijk(**reverse)
|
|
498
|
-
return v.classof(*_getitems(v.
|
|
496
|
+
return v.classof(*_getitems(v.xyz3, *ijk)) if ijk else v
|
|
499
497
|
|
|
500
498
|
@Property_RO
|
|
501
499
|
def _ordered4(self):
|
|
@@ -549,33 +547,34 @@ class Triaxial_(_NamedEnumItem):
|
|
|
549
547
|
a = _rphi(a, b, sb, cb)
|
|
550
548
|
if a != c:
|
|
551
549
|
c = _rphi(a, c, sa, ca)
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
return x, y, z
|
|
550
|
+
t = c * ca
|
|
551
|
+
return (t * cb), (t * sb), (c * sa)
|
|
555
552
|
|
|
556
553
|
def sideOf(self, x_xyz, y=None, z=None, eps=EPS4):
|
|
557
|
-
'''Is a cartesian
|
|
554
|
+
'''Is a cartesian on, above or below the surface of this triaxial?
|
|
558
555
|
|
|
559
556
|
@arg x_xyz: X component (C{scalar}) or a cartesian (C{Cartesian},
|
|
560
557
|
L{Ecef9Tuple}, L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
|
|
561
558
|
@kwarg y: Y component (C{scalar}), required if B{C{x_xyz}} if C{scalar},
|
|
562
559
|
ignored otherwise.
|
|
563
560
|
@kwarg z: Z component (C{scalar}), like B{C{y}}.
|
|
564
|
-
@kwarg eps:
|
|
561
|
+
@kwarg eps: On-surface tolerance (C{scalar}, distance I{squared}).
|
|
565
562
|
|
|
566
563
|
@return: C{INT0} if C{(B{x}, B{y}, B{z})} is near this triaxial's surface
|
|
567
|
-
within tolerance B{C{eps}}, otherwise
|
|
568
|
-
|
|
569
|
-
|
|
564
|
+
within tolerance B{C{eps}}, otherwise the signed, radial distance
|
|
565
|
+
I{squared} (C{float}), negative for in- or positive for outside
|
|
566
|
+
this triaxial.
|
|
570
567
|
|
|
571
568
|
@see: Methods L{Triaxial.height4} and L{Triaxial.normal3d}.
|
|
572
569
|
'''
|
|
573
|
-
|
|
570
|
+
v = _otherV3d_(x_xyz, y, z)
|
|
571
|
+
s = fsumf_(_N_1_0, *map(_over02, v.xyz3, self._abc3))
|
|
572
|
+
return INT0 if fabs(s) < eps else s
|
|
574
573
|
|
|
575
574
|
def toEllipsoid(self, **name):
|
|
576
575
|
'''Convert this triaxial to an L{Ellipsoid}, provided 2 axes match.
|
|
577
576
|
|
|
578
|
-
@kwarg name: Optional
|
|
577
|
+
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
579
578
|
|
|
580
579
|
@return: An L{Ellipsoid} with north along this C{Z} axis if C{a == b},
|
|
581
580
|
this C{Y} axis if C{a == c} or this C{X} axis if C{b == c}.
|
|
@@ -598,8 +597,8 @@ class Triaxial_(_NamedEnumItem):
|
|
|
598
597
|
'''Return this C{Triaxial} as a string.
|
|
599
598
|
|
|
600
599
|
@kwarg prec: Precision, number of decimal digits (0..9).
|
|
601
|
-
@kwarg name: Optional
|
|
602
|
-
|
|
600
|
+
@kwarg name: Optional name (C{str}), to override or C{None}
|
|
601
|
+
to exclude this triaxial's name.
|
|
603
602
|
|
|
604
603
|
@return: This C{Triaxial}'s attributes (C{str}).
|
|
605
604
|
'''
|
|
@@ -614,6 +613,12 @@ class Triaxial_(_NamedEnumItem):
|
|
|
614
613
|
t += T.volume, T.area
|
|
615
614
|
return self._instr(area_p=self.area_p(), prec=prec, props=t, **name)
|
|
616
615
|
|
|
616
|
+
@Property_RO
|
|
617
|
+
def unOrdered(self):
|
|
618
|
+
'''Is this triaxial I{un-ordered} and I{not spherical} (C{bool})?
|
|
619
|
+
'''
|
|
620
|
+
return not (self.isOrdered or bool(self.isSpherical))
|
|
621
|
+
|
|
617
622
|
@Property_RO
|
|
618
623
|
def volume(self):
|
|
619
624
|
'''Get the volume (C{meter**3}), M{4 / 3 * PI * a * b * c}.
|
|
@@ -665,27 +670,16 @@ class Triaxial(Triaxial_):
|
|
|
665
670
|
c2 = self._1e2ac # cos(phi)**2 = (c/a)**2
|
|
666
671
|
s = sqrt(self.e2ac) # sin(phi)**2 = 1 - c2
|
|
667
672
|
r = asin1(s) # phi = atan2(sqrt(c2), s)
|
|
668
|
-
b *= fsum1f_(aE.fE(r) * s, c / a * c / b,
|
|
673
|
+
b *= fsum1f_(aE.fE(r) * s, (c / a) * (c / b),
|
|
669
674
|
aE.fF(r) * c2 / s)
|
|
670
675
|
a = Meter2(area=a * b * PI2)
|
|
671
676
|
else: # a == b > c
|
|
672
677
|
a = Ellipsoid(a, b=c).areax
|
|
673
678
|
return a
|
|
674
679
|
|
|
675
|
-
def _exyz3(self, u):
|
|
676
|
-
'''(INTERNAL) Helper for C{.forwardBetOmg}.
|
|
677
|
-
'''
|
|
678
|
-
if u > 0:
|
|
679
|
-
u2 = u**2
|
|
680
|
-
x = u * sqrt0(_1_0 + self._a2c2 / u2, Error=TriaxialError)
|
|
681
|
-
y = u * sqrt0(_1_0 + self._b2c2 / u2, Error=TriaxialError)
|
|
682
|
-
else:
|
|
683
|
-
x = y = u = _0_0
|
|
684
|
-
return x, y, u
|
|
685
|
-
|
|
686
680
|
def forwardBetaOmega(self, beta, omega, height=0, **name):
|
|
687
|
-
'''Convert I{ellipsoidal} lat-
|
|
688
|
-
|
|
681
|
+
'''Convert I{ellipsoidal} lat- C{beta}, longitude C{omega} and C{height}
|
|
682
|
+
to cartesian.
|
|
689
683
|
|
|
690
684
|
@arg beta: Ellipsoidal latitude (C{radians} or L{Degrees}).
|
|
691
685
|
@arg omega: Ellipsoidal longitude (C{radians} or L{Degrees}).
|
|
@@ -695,22 +689,27 @@ class Triaxial(Triaxial_):
|
|
|
695
689
|
|
|
696
690
|
@return: A L{Vector3Tuple}C{(x, y, z)}.
|
|
697
691
|
|
|
698
|
-
@see: Method L{Triaxial.reverseBetaOmega} and U{
|
|
692
|
+
@see: Method L{Triaxial.reverseBetaOmega} and U{expressions (23-25)<https://
|
|
699
693
|
www.Topo.Auth.GR/wp-content/uploads/sites/111/2021/12/09_Panou.pdf>}.
|
|
700
694
|
'''
|
|
701
695
|
if height:
|
|
702
|
-
|
|
703
|
-
|
|
696
|
+
z = self._Height(height) + self.c
|
|
697
|
+
if z > 0:
|
|
698
|
+
z2 = z**2
|
|
699
|
+
x = z * _sqrt0(_1_0 + self._a2c2 / z2)
|
|
700
|
+
y = z * _sqrt0(_1_0 + self._b2c2 / z2)
|
|
701
|
+
else:
|
|
702
|
+
x = y = z = _0_0
|
|
704
703
|
else:
|
|
705
|
-
x, y, z = self._abc3
|
|
704
|
+
x, y, z = self._abc3
|
|
706
705
|
if z: # and x and y:
|
|
707
706
|
sa, ca = SinCos2(beta)
|
|
708
707
|
sb, cb = SinCos2(omega)
|
|
709
708
|
|
|
710
709
|
r = self._a2b2_a2c2
|
|
711
|
-
x *= cb *
|
|
712
|
-
y *= ca *
|
|
713
|
-
z *= sa *
|
|
710
|
+
x *= cb * _sqrt0(ca**2 + sa**2 * r)
|
|
711
|
+
y *= ca * sb
|
|
712
|
+
z *= sa * _sqrt0(_1_0 - cb**2 * r)
|
|
714
713
|
return Vector3Tuple(x, y, z, **name)
|
|
715
714
|
|
|
716
715
|
def forwardBetaOmega_(self, sbeta, cbeta, somega, comega, **name):
|
|
@@ -763,7 +762,7 @@ class Triaxial(Triaxial_):
|
|
|
763
762
|
|
|
764
763
|
@return: A L{Vector3Tuple}C{(x, y, z)}.
|
|
765
764
|
|
|
766
|
-
@see: Method L{Triaxial.reverseLatLon} and U{
|
|
765
|
+
@see: Method L{Triaxial.reverseLatLon} and U{expressions (9-11)<https://
|
|
767
766
|
www.Topo.Auth.GR/wp-content/uploads/sites/111/2021/12/09_Panou.pdf>}.
|
|
768
767
|
'''
|
|
769
768
|
return self._forwardLatLon3(height, name, *sincos2d_(lat, lon))
|
|
@@ -781,7 +780,7 @@ class Triaxial(Triaxial_):
|
|
|
781
780
|
|
|
782
781
|
@return: A L{Vector3Tuple}C{(x, y, z)}.
|
|
783
782
|
|
|
784
|
-
@see: Method L{Triaxial.reverseLatLon} and U{
|
|
783
|
+
@see: Method L{Triaxial.reverseLatLon} and U{expressions (9-11)<https://
|
|
785
784
|
www.Topo.Auth.GR/wp-content/uploads/sites/111/2021/12/09_Panou.pdf>}.
|
|
786
785
|
'''
|
|
787
786
|
sa, ca = self._norm2(slat, clat)
|
|
@@ -795,7 +794,7 @@ class Triaxial(Triaxial_):
|
|
|
795
794
|
h = self._Height(height)
|
|
796
795
|
# 1 - (1 - (c/a)**2) * sa**2 - (1 - (b/a)**2) * ca**2 * sb**2
|
|
797
796
|
t = fsumf_(_1_0, -self.e2ac * sa**2, -self.e2ab * ca_x_sb**2)
|
|
798
|
-
n = self.a /
|
|
797
|
+
n = self.a / _sqrt0(t) # prime vertical
|
|
799
798
|
x = (h + n) * ca * cb
|
|
800
799
|
y = (h + n * self._1e2ab) * ca_x_sb
|
|
801
800
|
z = (h + n * self._1e2ac) * sa
|
|
@@ -834,10 +833,10 @@ class Triaxial(Triaxial_):
|
|
|
834
833
|
x2 = _Fsumf_(_1_0, -b2_a2 * sa**2, c2_a2 * ca**2).fover(a2c2_a2)
|
|
835
834
|
z2 = _Fsumf_(c2_a2, sb**2, b2_a2 * cb**2).fover(a2c2_a2)
|
|
836
835
|
|
|
837
|
-
x, y, z =
|
|
838
|
-
x *= cb *
|
|
839
|
-
y *= ca *
|
|
840
|
-
z *= sa *
|
|
836
|
+
x, y, z = self._abc3
|
|
837
|
+
x *= cb * _sqrt0(x2)
|
|
838
|
+
y *= ca * sb
|
|
839
|
+
z *= sa * _sqrt0(z2)
|
|
841
840
|
return x, y, z
|
|
842
841
|
|
|
843
842
|
def reverseBetaOmega(self, x_xyz, y=None, z=None, **name):
|
|
@@ -856,7 +855,7 @@ class Triaxial(Triaxial_):
|
|
|
856
855
|
units as this triaxial's axes.
|
|
857
856
|
|
|
858
857
|
@see: Methods L{Triaxial.forwardBetaOmega} and L{Triaxial.forwardBetaOmega_}
|
|
859
|
-
and U{
|
|
858
|
+
and U{expressions (21-22)<https://www.Topo.Auth.GR/wp-content/uploads/
|
|
860
859
|
sites/111/2021/12/09_Panou.pdf>}.
|
|
861
860
|
'''
|
|
862
861
|
v = _otherV3d_(x_xyz, y, z)
|
|
@@ -873,9 +872,9 @@ class Triaxial(Triaxial_):
|
|
|
873
872
|
@kwarg z: Z component (C{scalar}), like B{C{y}}.
|
|
874
873
|
@arg h: Height above or below this triaxial's surface (C{meter}, same units
|
|
875
874
|
as the axes).
|
|
876
|
-
@kwarg normal: If C{True} the height is C{normal} to the surface, otherwise
|
|
875
|
+
@kwarg normal: If C{True}, the height is C{normal} to the surface, otherwise
|
|
877
876
|
C{radially} to the center of this triaxial (C{bool}).
|
|
878
|
-
@kwarg eps: Tolerance for surface test (C{scalar}).
|
|
877
|
+
@kwarg eps: Tolerance for on-surface test (C{scalar}), see method L{Triaxial.sideOf}.
|
|
879
878
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
880
879
|
|
|
881
880
|
@return: A L{Vector3Tuple}C{(x, y, z)}.
|
|
@@ -885,7 +884,7 @@ class Triaxial(Triaxial_):
|
|
|
885
884
|
@see: Methods L{Triaxial.forwardCartesian} and L{Triaxial.height4}.
|
|
886
885
|
'''
|
|
887
886
|
v = _otherV3d_(x_xyz, y, z, **name)
|
|
888
|
-
s =
|
|
887
|
+
s = self.sideOf(v.xyz, eps=eps)
|
|
889
888
|
if s: # PYCHOK no cover
|
|
890
889
|
t = _SPACE_((_inside_ if s < 0 else _outside_), self.toRepr())
|
|
891
890
|
raise TriaxialError(eps=eps, sideOf=s, x=v.x, y=v.y, z=v.z, txt=t)
|
|
@@ -912,7 +911,7 @@ class Triaxial(Triaxial_):
|
|
|
912
911
|
as this triaxial's axes.
|
|
913
912
|
|
|
914
913
|
@see: Methods L{Triaxial.forwardLatLon} and L{Triaxial.forwardLatLon_}
|
|
915
|
-
and U{
|
|
914
|
+
and U{expressions (4-5)<https://www.Topo.Auth.GR/wp-content/uploads/
|
|
916
915
|
sites/111/2021/12/09_Panou.pdf>}.
|
|
917
916
|
'''
|
|
918
917
|
v = _otherV3d_(x_xyz, y, z)
|
|
@@ -925,7 +924,7 @@ class Triaxial(Triaxial_):
|
|
|
925
924
|
def _reverseLatLon3(self, s, atan2_, v, forward_):
|
|
926
925
|
'''(INTERNAL) Helper for C{.reverseBetOmg} and C{.reverseLatLon}.
|
|
927
926
|
'''
|
|
928
|
-
x, y, z = s.
|
|
927
|
+
x, y, z = s.xyz3
|
|
929
928
|
d = hypot( x, y)
|
|
930
929
|
a = atan2_(z, d)
|
|
931
930
|
b = atan2_(y, x)
|
|
@@ -996,7 +995,7 @@ class JacobiConformal(Triaxial):
|
|
|
996
995
|
|
|
997
996
|
@arg beta: Ellipsoidal latitude (C{radians} or L{Degrees}).
|
|
998
997
|
@arg omega: Ellipsoidal longitude (C{radians} or L{Degrees}).
|
|
999
|
-
@kwarg name: Optional, overriding C{B{name}="xyR2"}
|
|
998
|
+
@kwarg name: Optional name (C{str}), overriding C{B{name}="xyR2"}.
|
|
1000
999
|
|
|
1001
1000
|
@return: A L{Jacobi2Tuple}C{(x, y)}.
|
|
1002
1001
|
'''
|
|
@@ -1010,7 +1009,7 @@ class JacobiConformal(Triaxial):
|
|
|
1010
1009
|
@arg cbeta: Ellipsoidal latitude C{cos(beta)} (C{scalar}).
|
|
1011
1010
|
@arg somega: Ellipsoidal longitude C{sin(omega)} (C{scalar}).
|
|
1012
1011
|
@arg comega: Ellipsoidal longitude C{cos(omega)} (C{scalar}).
|
|
1013
|
-
@kwarg name: Optional, overriding C{B{name}="xyR2_"}
|
|
1012
|
+
@kwarg name: Optional name (C{str}), overriding C{B{name}="xyR2_"}.
|
|
1014
1013
|
|
|
1015
1014
|
@return: A L{Jacobi2Tuple}C{(x, y)}.
|
|
1016
1015
|
'''
|
|
@@ -1098,7 +1097,7 @@ class JacobiConformalSpherical(JacobiConformal):
|
|
|
1098
1097
|
|
|
1099
1098
|
@Property_RO
|
|
1100
1099
|
def ab(self):
|
|
1101
|
-
'''Get relative magnitude C{
|
|
1100
|
+
'''Get relative magnitude C{a - b} (C{meter}, same units as B{C{a}}).
|
|
1102
1101
|
'''
|
|
1103
1102
|
return self._ab
|
|
1104
1103
|
|
|
@@ -1117,7 +1116,7 @@ class JacobiConformalSpherical(JacobiConformal):
|
|
|
1117
1116
|
|
|
1118
1117
|
@Property_RO
|
|
1119
1118
|
def bc(self):
|
|
1120
|
-
'''Get relative magnitude C{
|
|
1119
|
+
'''Get relative magnitude C{b - c} (C{meter}, same units as B{C{a}}).
|
|
1121
1120
|
'''
|
|
1122
1121
|
return self._bc
|
|
1123
1122
|
|
|
@@ -1158,7 +1157,7 @@ Triaxials = Triaxials(Triaxial, Triaxial_) # PYCHOK singleton
|
|
|
1158
1157
|
# <https://link.Springer.com/article/10.1007/s00190-022-01650-9>
|
|
1159
1158
|
_abc84_35 = (_EWGS84.a + 35), (_EWGS84.a - 35), _EWGS84.b
|
|
1160
1159
|
Triaxials._assert( # a (Km) b (Km) c (Km) planet
|
|
1161
|
-
Amalthea = _lazy('Amalthea', 125.0, 73.0, 64),
|
|
1160
|
+
Amalthea = _lazy('Amalthea', 125.0, 73.0, 64.0), # Jupiter
|
|
1162
1161
|
Ariel = _lazy('Ariel', 581.1, 577.9, 577.7), # Uranus
|
|
1163
1162
|
Earth = _lazy('Earth', 6378.173435, 6378.1039, 6356.7544),
|
|
1164
1163
|
Enceladus = _lazy('Enceladus', 256.6, 251.4, 248.3), # Saturn
|
|
@@ -1210,9 +1209,9 @@ def _hartzell3(pov, los, Tun): # in .Ellipsoid.hartzell4, .formy.hartzell
|
|
|
1210
1209
|
p3 = T._order3d(p3)
|
|
1211
1210
|
u3 = T._order3d(u3).unit() # unit vector, opposing signs
|
|
1212
1211
|
|
|
1213
|
-
x2, y2, z2 = p3.x2y2z2 # p3.times_(p3).
|
|
1214
|
-
ux, vy, wz = u3.times_(p3).
|
|
1215
|
-
u2, v2, w2 = u3.x2y2z2 # u3.times_(u3).
|
|
1212
|
+
x2, y2, z2 = p3.x2y2z2 # p3.times_(p3).xyz3
|
|
1213
|
+
ux, vy, wz = u3.times_(p3).xyz3
|
|
1214
|
+
u2, v2, w2 = u3.x2y2z2 # u3.times_(u3).xyz3
|
|
1216
1215
|
|
|
1217
1216
|
t = (p2 * c2), c2, b2
|
|
1218
1217
|
m = fdot(t, u2, v2, w2) # a2 factored out
|
|
@@ -1229,9 +1228,9 @@ def _hartzell3(pov, los, Tun): # in .Ellipsoid.hartzell4, .formy.hartzell
|
|
|
1229
1228
|
|
|
1230
1229
|
d = Fdot(t, ux, vy, wz).fadd_(r).fover(m) # -r for antipode, a2 factored out
|
|
1231
1230
|
if d > 0: # POV inside or LOS outside or missing the triaxial
|
|
1232
|
-
s = fsumf_(_N_1_0, x2
|
|
1231
|
+
s = fsumf_(_N_1_0, _over(x2, a2), _over(y2, b2), _over(z2, c2)) # like _sideOf
|
|
1233
1232
|
raise _ValueError(_outside_ if s > 0 else _inside_)
|
|
1234
|
-
elif fsum1f_(x2, y2, z2) <
|
|
1233
|
+
elif fsum1f_(x2, y2, z2, -d*d) < 0: # d past triaxial's center
|
|
1235
1234
|
raise _ValueError(_too_(_distant_))
|
|
1236
1235
|
|
|
1237
1236
|
v = p3.minus(u3.times(d)) # cartesian type(pov) or Vector3d
|
|
@@ -1240,19 +1239,19 @@ def _hartzell3(pov, los, Tun): # in .Ellipsoid.hartzell4, .formy.hartzell
|
|
|
1240
1239
|
|
|
1241
1240
|
|
|
1242
1241
|
def hartzell4(pov, los=False, tri_biax=_WGS84, **name):
|
|
1243
|
-
'''Compute the intersection of a tri-/biaxial ellipsoid and a Line-Of-Sight
|
|
1244
|
-
|
|
1242
|
+
'''Compute the intersection of a tri-/biaxial ellipsoid and a Line-Of-Sight from
|
|
1243
|
+
a Point-Of-View outside.
|
|
1245
1244
|
|
|
1246
1245
|
@arg pov: Point-Of-View outside the tri-/biaxial (C{Cartesian}, L{Ecef9Tuple}
|
|
1247
1246
|
C{LatLon} or L{Vector3d}).
|
|
1248
1247
|
@kwarg los: Line-Of-Sight, I{direction} to the tri-/biaxial (L{Los}, L{Vector3d}),
|
|
1249
|
-
C{True} for the I{normal, plumb}
|
|
1250
|
-
C{None} to point to
|
|
1248
|
+
or C{True} for the I{normal, perpendicular, plumb} to the surface of
|
|
1249
|
+
the tri-/biaxial or C{False} or C{None} to point to its center.
|
|
1251
1250
|
@kwarg tri_biax: A triaxial (L{Triaxial}, L{Triaxial_}, L{JacobiConformal} or
|
|
1252
1251
|
L{JacobiConformalSpherical}) or biaxial ellipsoid (L{Datum},
|
|
1253
|
-
L{Ellipsoid}, L{Ellipsoid2}, L{a_f2Tuple} or
|
|
1254
|
-
conventionally in C{meter}).
|
|
1255
|
-
@kwarg name: Optional, overriding C{B{name}="hartzell4"}
|
|
1252
|
+
L{Ellipsoid}, L{Ellipsoid2}, L{a_f2Tuple}) or spherical earth
|
|
1253
|
+
radius (C{scalar}, conventionally in C{meter}).
|
|
1254
|
+
@kwarg name: Optional name (C{str}), overriding C{B{name}="hartzell4"}.
|
|
1256
1255
|
|
|
1257
1256
|
@return: L{Vector4Tuple}C{(x, y, z, h)} on the tri-/biaxial's surface, with C{h}
|
|
1258
1257
|
the distance from B{C{pov}} to C{(x, y, z)} I{along the} B{C{los}}, all
|
|
@@ -1283,11 +1282,11 @@ def hartzell4(pov, los=False, tri_biax=_WGS84, **name):
|
|
|
1283
1282
|
name=_name__(name, name__=hartzell4))
|
|
1284
1283
|
|
|
1285
1284
|
|
|
1286
|
-
def
|
|
1287
|
-
'''(INTERNAL)
|
|
1288
|
-
fabs(
|
|
1285
|
+
def _hypot2_1(x, y, z=0):
|
|
1286
|
+
'''(INTERNAL) Compute M{x**2 + y**2 + z**2 - 1} with C{max(fabs(x), fabs(y),
|
|
1287
|
+
fabs(z))} rarely greater than 1.0.
|
|
1289
1288
|
'''
|
|
1290
|
-
return fsumf_(
|
|
1289
|
+
return fsumf_(_N_1_0, x*x, y*y, z*z)
|
|
1291
1290
|
|
|
1292
1291
|
|
|
1293
1292
|
def _otherV3d_(x_xyz, y, z, **name):
|
|
@@ -1297,6 +1296,18 @@ def _otherV3d_(x_xyz, y, z, **name):
|
|
|
1297
1296
|
_otherV3d(x_xyz=x_xyz, **name)
|
|
1298
1297
|
|
|
1299
1298
|
|
|
1299
|
+
def _over0(p, q):
|
|
1300
|
+
'''(INTERNAL) Return C{p / q} or C{0}.
|
|
1301
|
+
'''
|
|
1302
|
+
return (p / q) if q > fabs(p) else _0_0
|
|
1303
|
+
|
|
1304
|
+
|
|
1305
|
+
def _over02(p, q):
|
|
1306
|
+
'''(INTERNAL) Return C{(p / q)**2} or C{0}.
|
|
1307
|
+
'''
|
|
1308
|
+
return (p / q)**2 if p and q else _0_0
|
|
1309
|
+
|
|
1310
|
+
|
|
1300
1311
|
def _plumbTo4(x, y, a, b, eps=EPS):
|
|
1301
1312
|
'''(INTERNAL) Nearest point on and distance to a 2-D ellipse, I{unordered}.
|
|
1302
1313
|
|
|
@@ -1311,46 +1322,48 @@ def _plumbTo4(x, y, a, b, eps=EPS):
|
|
|
1311
1322
|
if not (b > 0 and isfinite(a)):
|
|
1312
1323
|
raise _ValueError(a=a, b=b)
|
|
1313
1324
|
|
|
1314
|
-
i
|
|
1325
|
+
i = None
|
|
1315
1326
|
if y:
|
|
1316
1327
|
if x:
|
|
1317
|
-
u =
|
|
1318
|
-
|
|
1319
|
-
g =
|
|
1320
|
-
if
|
|
1321
|
-
a, b, d = x, y, _0_0
|
|
1322
|
-
else:
|
|
1328
|
+
u = fabs(x / a)
|
|
1329
|
+
w = fabs(y / b)
|
|
1330
|
+
g = _hypot2_1(u, w)
|
|
1331
|
+
if fabs(g) > EPS02:
|
|
1323
1332
|
r = (b / a)**2
|
|
1324
|
-
t, i =
|
|
1325
|
-
a = x
|
|
1326
|
-
b = y
|
|
1327
|
-
d =
|
|
1333
|
+
t, i = _rootNd(_1_0 / r, 0, u, 0, w, g) # eps
|
|
1334
|
+
a = _over(x, t * r + _1_0)
|
|
1335
|
+
b = _over(y, t + _1_0)
|
|
1336
|
+
d = hypot(x - a, y - b)
|
|
1337
|
+
else: # on the ellipse
|
|
1338
|
+
a, b, d = x, y, _0_0
|
|
1328
1339
|
else: # x == 0
|
|
1329
1340
|
if y < 0:
|
|
1330
1341
|
b = -b
|
|
1331
|
-
a
|
|
1342
|
+
a = x # signed-0
|
|
1343
|
+
d = fabs(y - b)
|
|
1332
1344
|
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
if d > _a(n): # PYCHOK no cover
|
|
1337
|
-
r = n / d
|
|
1345
|
+
elif x: # y == 0
|
|
1346
|
+
d, r = None, _over0(a * x, (a + b) * (a - b))
|
|
1347
|
+
if r:
|
|
1338
1348
|
a *= r
|
|
1339
1349
|
r = _1_0 - r**2
|
|
1340
1350
|
if r > EPS02:
|
|
1341
1351
|
b *= sqrt(r)
|
|
1342
|
-
d = hypot(x - a, b)
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1352
|
+
d = hypot(x - a, y - b)
|
|
1353
|
+
elif x < 0:
|
|
1354
|
+
a = -a
|
|
1355
|
+
if d is None:
|
|
1356
|
+
b = y # signed-0
|
|
1357
|
+
d = fabs(x - a)
|
|
1358
|
+
|
|
1359
|
+
else: # x == y == 0
|
|
1360
|
+
a = x # signed-0
|
|
1361
|
+
d = b
|
|
1362
|
+
|
|
1350
1363
|
return a, b, d, i
|
|
1351
1364
|
|
|
1352
1365
|
|
|
1353
|
-
def _plumbTo5(x, y, z, Tun, eps=EPS): #
|
|
1366
|
+
def _plumbTo5(x, y, z, Tun, eps=EPS): # in .testTriaxials
|
|
1354
1367
|
'''(INTERNAL) Nearest point on and distance to an I{un-/ordered} triaxial.
|
|
1355
1368
|
|
|
1356
1369
|
@see: I{Eberly}'s U{Distance from a Point to ... an Ellipsoid ...<https://
|
|
@@ -1370,24 +1383,24 @@ def _plumbTo5(x, y, z, Tun, eps=EPS): # MCCABE 19 in .testTriaxials
|
|
|
1370
1383
|
else: # no validation
|
|
1371
1384
|
val, eps = 0, max(EPS0, -eps)
|
|
1372
1385
|
|
|
1373
|
-
i
|
|
1386
|
+
i = None
|
|
1374
1387
|
if z:
|
|
1375
1388
|
if y:
|
|
1376
1389
|
if x:
|
|
1377
|
-
u =
|
|
1378
|
-
v =
|
|
1379
|
-
w =
|
|
1380
|
-
g =
|
|
1381
|
-
if
|
|
1382
|
-
a, b, c, d = x, y, z, _0_0
|
|
1383
|
-
else:
|
|
1390
|
+
u = fabs(x / a)
|
|
1391
|
+
v = fabs(y / b)
|
|
1392
|
+
w = fabs(z / c)
|
|
1393
|
+
g = _hypot2_1(u, v, w)
|
|
1394
|
+
if fabs(g) > EPS02:
|
|
1384
1395
|
r = T._1e2ac # (c / a)**2
|
|
1385
1396
|
s = T._1e2bc # (c / b)**2
|
|
1386
|
-
t, i =
|
|
1387
|
-
a = x
|
|
1388
|
-
b = y
|
|
1389
|
-
c = z
|
|
1390
|
-
d =
|
|
1397
|
+
t, i = _rootNd(_1_0 / r, _1_0 / s, u, v, w, g) # eps
|
|
1398
|
+
a = _over(x, t * r + _1_0)
|
|
1399
|
+
b = _over(y, t * s + _1_0)
|
|
1400
|
+
c = _over(z, t + _1_0)
|
|
1401
|
+
d = hypot_(x - a, y - b, z - c)
|
|
1402
|
+
else: # on the ellipsoid
|
|
1403
|
+
a, b, c, d = x, y, z, _0_0
|
|
1391
1404
|
else: # x == 0
|
|
1392
1405
|
a = x # 0
|
|
1393
1406
|
b, c, d, i = _plumbTo4(y, z, b, c, eps=eps)
|
|
@@ -1397,69 +1410,47 @@ def _plumbTo5(x, y, z, Tun, eps=EPS): # MCCABE 19 in .testTriaxials
|
|
|
1397
1410
|
else: # x == y == 0
|
|
1398
1411
|
if z < 0:
|
|
1399
1412
|
c = -c
|
|
1400
|
-
a, b, d = x, y,
|
|
1413
|
+
a, b, d = x, y, fabs(z - c)
|
|
1401
1414
|
|
|
1402
1415
|
else: # z == 0
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
if
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
n = _hypot21(u, v)
|
|
1413
|
-
if n < 0:
|
|
1414
|
-
a *= u
|
|
1415
|
-
b *= v
|
|
1416
|
-
c *= sqrt(-n)
|
|
1417
|
-
d = hypot_(x - a, y - b, c)
|
|
1418
|
-
t = False
|
|
1419
|
-
if t:
|
|
1416
|
+
u = _over0(a * x, T._a2c2) # (a + c) * (a - c)
|
|
1417
|
+
v = _over0(b * y, T._b2c2) # (b + c) * (b - c)
|
|
1418
|
+
s = _hypot2_1(u, v)
|
|
1419
|
+
if u and v and s < 0:
|
|
1420
|
+
a *= u
|
|
1421
|
+
b *= v
|
|
1422
|
+
c *= sqrt(-s)
|
|
1423
|
+
d = hypot_(x - a, y - b, c)
|
|
1424
|
+
else:
|
|
1420
1425
|
c = z # signed-0
|
|
1421
1426
|
a, b, d, i = _plumbTo4(x, y, a, b, eps=eps)
|
|
1422
1427
|
|
|
1423
|
-
if val > 0:
|
|
1424
|
-
|
|
1425
|
-
if e: # not near the ellipsoid's surface
|
|
1426
|
-
raise _ValueError(a=a, b=b, c=c, d=d,
|
|
1427
|
-
sideOf=e, eps=val)
|
|
1428
|
-
if d: # angle of delta and normal vector
|
|
1429
|
-
m = Vector3d(x, y, z).minus_(a, b, c)
|
|
1430
|
-
if m.euclid > val:
|
|
1431
|
-
m = m.unit()
|
|
1432
|
-
n = T.normal3d(a, b, c)
|
|
1433
|
-
e = n.dot(m) # n.negate().dot(m)
|
|
1434
|
-
if not isnear1(_a(e), eps1=val):
|
|
1435
|
-
raise _ValueError(n=n, m=m,
|
|
1436
|
-
dot=e, eps=val)
|
|
1428
|
+
if val > 0:
|
|
1429
|
+
_validate(a, b, c, d, T, x, y, z, val)
|
|
1437
1430
|
return a, b, c, d, i
|
|
1438
1431
|
|
|
1439
1432
|
|
|
1440
|
-
def
|
|
1441
|
-
'''(INTERNAL) Robust
|
|
1433
|
+
def _rootNd(r, s, u, v, w, g, eps=EPS0):
|
|
1434
|
+
'''(INTERNAL) Robust 2-D or 3-D root finder: 2-D if C{s == v == 0} else 3-D root.
|
|
1442
1435
|
|
|
1443
|
-
@see: I{Eberly}'s U{Robust Root Finders
|
|
1444
|
-
Documentation/DistancePointEllipseEllipsoid.pdf>}.
|
|
1436
|
+
@see: I{Eberly}'s U{Robust Root Finders ... and Listing 4<https://
|
|
1437
|
+
www.GeometricTools.com/Documentation/DistancePointEllipseEllipsoid.pdf>}.
|
|
1445
1438
|
'''
|
|
1446
|
-
_1, __2 = _1_0, _0_5
|
|
1447
|
-
_a, _h21 = fabs, _hypot21
|
|
1448
|
-
|
|
1449
1439
|
u *= r
|
|
1450
|
-
v *= s # 0 for
|
|
1451
|
-
t0 = w
|
|
1452
|
-
t1 = _0_0 if g < 0 else (hypot_(u, w, v)
|
|
1440
|
+
v *= s # 0 for 2-D root
|
|
1441
|
+
t0 = w + _N_1_0
|
|
1442
|
+
t1 = _0_0 if g < 0 else (hypot_(u, w, v) + _N_1_0)
|
|
1453
1443
|
# assert t0 <= t1
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
t
|
|
1459
|
-
if t in (t0, t1):
|
|
1444
|
+
_e = -eps
|
|
1445
|
+
for i in range(1, _TRIPS): # 47..65
|
|
1446
|
+
t = (t1 + t0) * _0_5
|
|
1447
|
+
e = t1 - t0
|
|
1448
|
+
if _e < e < eps or t in (t0, t1):
|
|
1460
1449
|
break
|
|
1461
|
-
g =
|
|
1462
|
-
|
|
1450
|
+
g = fsumf_(_N_1_0, # ~= _hypot2_1
|
|
1451
|
+
_over02(u, t + r),
|
|
1452
|
+
_over02(w, t - _N_1_0),
|
|
1453
|
+
_over02(v, t + s) if v else _0_0)
|
|
1463
1454
|
if g > 0:
|
|
1464
1455
|
t0 = t
|
|
1465
1456
|
elif g < 0:
|
|
@@ -1468,17 +1459,32 @@ def _rootXd(r, s, u, v, w, g, eps):
|
|
|
1468
1459
|
break
|
|
1469
1460
|
else: # PYCHOK no cover
|
|
1470
1461
|
t = Fmt.no_convergence(e, eps)
|
|
1471
|
-
raise _ValueError(t, txt__=
|
|
1462
|
+
raise _ValueError(t, txt__=_rootNd)
|
|
1472
1463
|
return t, i
|
|
1473
1464
|
|
|
1474
1465
|
|
|
1475
|
-
def
|
|
1476
|
-
'''(INTERNAL)
|
|
1466
|
+
def _sqrt0(x):
|
|
1467
|
+
'''(INTERNAL) C{sqrt0} with C{TriaxialError}.
|
|
1468
|
+
'''
|
|
1469
|
+
return sqrt0(x, Error=TriaxialError)
|
|
1470
|
+
|
|
1477
1471
|
|
|
1478
|
-
|
|
1472
|
+
def _validate(a, b, c, d, T, x, y, z, val):
|
|
1473
|
+
'''(INTERNAL) Validate an C{_plumTo5} result.
|
|
1479
1474
|
'''
|
|
1480
|
-
|
|
1481
|
-
|
|
1475
|
+
e = T.sideOf(a, b, c, eps=val)
|
|
1476
|
+
if e: # not near the ellipsoid's surface
|
|
1477
|
+
raise _ValueError(a=a, b=b, c=c, d=d,
|
|
1478
|
+
sideOf=e, eps=val)
|
|
1479
|
+
if d: # angle of delta and normal vector
|
|
1480
|
+
m = Vector3d(x, y, z).minus_(a, b, c)
|
|
1481
|
+
if m.euclid > val:
|
|
1482
|
+
m = m.unit()
|
|
1483
|
+
n = T.normal3d(a, b, c)
|
|
1484
|
+
e = n.dot(m) # n.negate().dot(m)
|
|
1485
|
+
if not isnear1(fabs(e), eps1=val):
|
|
1486
|
+
raise _ValueError(n=n, m=m,
|
|
1487
|
+
dot=e, eps=val)
|
|
1482
1488
|
|
|
1483
1489
|
|
|
1484
1490
|
if __name__ == '__main__':
|