coordinate-system 7.0.2__tar.gz → 7.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {coordinate_system-7.0.2/coordinate_system.egg-info → coordinate_system-7.1.1}/PKG-INFO +18 -5
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/README.md +19 -6
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system/__init__.py +2 -2
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system/complex_geometric_physics.py +2 -2
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system/differential_geometry.py +376 -117
- {coordinate_system-7.0.2 → coordinate_system-7.1.1/coordinate_system.egg-info}/PKG-INFO +18 -5
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/pmsys_minimal.hpp +49 -37
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/pyproject.toml +1 -1
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/setup.py +38 -30
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/LICENSE +0 -0
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/MANIFEST.in +0 -0
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system/curve_interpolation.py +0 -0
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system/spectral_geometry.py +0 -0
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system/u3_frame.py +0 -0
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system/visualization.py +0 -0
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system.egg-info/SOURCES.txt +0 -0
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system.egg-info/dependency_links.txt +0 -0
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system.egg-info/not-zip-safe +0 -0
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system.egg-info/requires.txt +0 -0
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system.egg-info/top_level.txt +0 -0
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system_binding.cpp +0 -0
- {coordinate_system-7.0.2 → coordinate_system-7.1.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: coordinate_system
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.1.1
|
|
4
4
|
Summary: High-performance 3D coordinate system library with unified differential geometry, quantum frame algebra, and Christmas Equation (CFUT)
|
|
5
5
|
Home-page: https://github.com/panguojun/Coordinate-System
|
|
6
6
|
Author: Pan Guojun
|
|
@@ -51,13 +51,20 @@ Requires-Dist: matplotlib>=3.3.0
|
|
|
51
51
|
[](LICENSE)
|
|
52
52
|
|
|
53
53
|
**Authors:** Pan Guojun
|
|
54
|
-
**Version:** 7.
|
|
54
|
+
**Version:** 7.1.1
|
|
55
55
|
**License:** MIT
|
|
56
56
|
**DOI:** https://doi.org/10.5281/zenodo.14435613
|
|
57
57
|
|
|
58
58
|
---
|
|
59
59
|
|
|
60
|
-
## What's New in v7.
|
|
60
|
+
## What's New in v7.1.1 (2026-02-05)
|
|
61
|
+
|
|
62
|
+
- **Curvature Fast Path**: Analytical curvature for Sphere/Torus and any surface providing `derivs` or `analytic_KH` (toggle via `USE_ANALYTIC_DERIVS`)
|
|
63
|
+
- **Caching**: Reuse curvature calculators and last-call results to reduce per-sample overhead
|
|
64
|
+
- **Math Core**: Branchless handedness selection for cross product; optional SIMD alignment via `PMSYS_SIMD_ALIGN`
|
|
65
|
+
- **Build Flags**: AVX2 / fast-math enabled by default in build config for faster native builds
|
|
66
|
+
|
|
67
|
+
## What's New in v7.1.0 (2026-01-16)
|
|
61
68
|
|
|
62
69
|
- **Physical Constants**: Added SI unit constants for precision calculations (ALPHA_FS, LAMBDA_C, ALPHA_PROJECTION)
|
|
63
70
|
- **Projection Factor**: Implemented α = α_fs × λ_c ≈ 1.77×10⁻¹⁴ m for geometry-gauge coupling
|
|
@@ -199,7 +206,7 @@ S_YM = F_xy.yang_mills_action()
|
|
|
199
206
|
|
|
200
207
|
| Concept | Formula | Code |
|
|
201
208
|
|---------|---------|------|
|
|
202
|
-
| Projection Factor (v7.0
|
|
209
|
+
| Projection Factor (v7.1.0) | $\alpha = \alpha_{\text{fs}} \times \lambda_c \approx 1.77 \times 10^{-14}$ m | `ALPHA_PROJECTION` |
|
|
203
210
|
| Intrinsic Gradient | $G_\mu = \frac{d}{dx^\mu} \log C(x)$ | `IntrinsicGradient` |
|
|
204
211
|
| Curvature Tensor | $R_{\mu\nu} = [G_\mu, G_\nu]$ | `CurvatureFromFrame` |
|
|
205
212
|
| Gaussian Curvature | $K = -\langle [G_u, G_v] e_v, e_u \rangle / \sqrt{\det g}$ | `compute_gaussian_curvature` |
|
|
@@ -236,7 +243,13 @@ S_YM = F_xy.yang_mills_action()
|
|
|
236
243
|
|
|
237
244
|
## Changelog
|
|
238
245
|
|
|
239
|
-
### v7.
|
|
246
|
+
### v7.1.1 (2026-02-05)
|
|
247
|
+
- **Curvature Fast Path**: Analytical curvature for analytic surfaces with optional toggle
|
|
248
|
+
- **Caching**: Curvature calculator reuse and last-call caching for repeated samples
|
|
249
|
+
- **Math Core**: Branchless cross product handedness selection
|
|
250
|
+
- **Build**: AVX2 / fast-math flags enabled by default for native builds
|
|
251
|
+
|
|
252
|
+
### v7.1.0 (2026-01-16)
|
|
240
253
|
- **Physical Constants**: Added SI unit constants (ALPHA_FS, LAMBDA_C, ALPHA_PROJECTION)
|
|
241
254
|
- **Projection Factor**: Implemented α = α_fs × λ_c ≈ 1.77×10⁻¹⁴ m for geometry-gauge coupling
|
|
242
255
|
- **Complex Geometric Physics**: Added `projection_factor` parameter to unified field solver
|
|
@@ -7,13 +7,20 @@
|
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
|
|
9
9
|
**Authors:** Pan Guojun
|
|
10
|
-
**Version:** 7.
|
|
10
|
+
**Version:** 7.1.1
|
|
11
11
|
**License:** MIT
|
|
12
12
|
**DOI:** https://doi.org/10.5281/zenodo.14435613
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
## What's New in v7.
|
|
16
|
+
## What's New in v7.1.1 (2026-02-05)
|
|
17
|
+
|
|
18
|
+
- **Curvature Fast Path**: Analytical curvature for Sphere/Torus and any surface providing `derivs` or `analytic_KH` (toggle via `USE_ANALYTIC_DERIVS`)
|
|
19
|
+
- **Caching**: Reuse curvature calculators and last-call results to reduce per-sample overhead
|
|
20
|
+
- **Math Core**: Branchless handedness selection for cross product; optional SIMD alignment via `PMSYS_SIMD_ALIGN`
|
|
21
|
+
- **Build Flags**: AVX2 / fast-math enabled by default in build config for faster native builds
|
|
22
|
+
|
|
23
|
+
## What's New in v7.1.0 (2026-01-16)
|
|
17
24
|
|
|
18
25
|
- **Physical Constants**: Added SI unit constants for precision calculations (ALPHA_FS, LAMBDA_C, ALPHA_PROJECTION)
|
|
19
26
|
- **Projection Factor**: Implemented α = α_fs × λ_c ≈ 1.77×10⁻¹⁴ m for geometry-gauge coupling
|
|
@@ -155,7 +162,7 @@ S_YM = F_xy.yang_mills_action()
|
|
|
155
162
|
|
|
156
163
|
| Concept | Formula | Code |
|
|
157
164
|
|---------|---------|------|
|
|
158
|
-
| Projection Factor (v7.0
|
|
165
|
+
| Projection Factor (v7.1.0) | $\alpha = \alpha_{\text{fs}} \times \lambda_c \approx 1.77 \times 10^{-14}$ m | `ALPHA_PROJECTION` |
|
|
159
166
|
| Intrinsic Gradient | $G_\mu = \frac{d}{dx^\mu} \log C(x)$ | `IntrinsicGradient` |
|
|
160
167
|
| Curvature Tensor | $R_{\mu\nu} = [G_\mu, G_\nu]$ | `CurvatureFromFrame` |
|
|
161
168
|
| Gaussian Curvature | $K = -\langle [G_u, G_v] e_v, e_u \rangle / \sqrt{\det g}$ | `compute_gaussian_curvature` |
|
|
@@ -190,9 +197,15 @@ S_YM = F_xy.yang_mills_action()
|
|
|
190
197
|
|
|
191
198
|
---
|
|
192
199
|
|
|
193
|
-
## Changelog
|
|
194
|
-
|
|
195
|
-
### v7.
|
|
200
|
+
## Changelog
|
|
201
|
+
|
|
202
|
+
### v7.1.1 (2026-02-05)
|
|
203
|
+
- **Curvature Fast Path**: Analytical curvature for analytic surfaces with optional toggle
|
|
204
|
+
- **Caching**: Curvature calculator reuse and last-call caching for repeated samples
|
|
205
|
+
- **Math Core**: Branchless cross product handedness selection
|
|
206
|
+
- **Build**: AVX2 / fast-math flags enabled by default for native builds
|
|
207
|
+
|
|
208
|
+
### v7.1.0 (2026-01-16)
|
|
196
209
|
- **Physical Constants**: Added SI unit constants (ALPHA_FS, LAMBDA_C, ALPHA_PROJECTION)
|
|
197
210
|
- **Projection Factor**: Implemented α = α_fs × λ_c ≈ 1.77×10⁻¹⁴ m for geometry-gauge coupling
|
|
198
211
|
- **Complex Geometric Physics**: Added `projection_factor` parameter to unified field solver
|
|
@@ -24,11 +24,11 @@ Group Correspondence:
|
|
|
24
24
|
- U3Frame ∈ U(3) = SU(3) × U(1)
|
|
25
25
|
|
|
26
26
|
**Authors:** Pan Guojun
|
|
27
|
-
Version: 7.
|
|
27
|
+
Version: 7.1.1
|
|
28
28
|
**DOI:** https://doi.org/10.5281/zenodo.14435613
|
|
29
29
|
"""
|
|
30
30
|
|
|
31
|
-
__version__ = '7.
|
|
31
|
+
__version__ = '7.1.1'
|
|
32
32
|
|
|
33
33
|
from .coordinate_system import (
|
|
34
34
|
vec3,
|
{coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system/complex_geometric_physics.py
RENAMED
|
@@ -26,11 +26,11 @@ Physical Interpretation:
|
|
|
26
26
|
|
|
27
27
|
**Authors:** Pan Guojun
|
|
28
28
|
Date: 2025-01-14
|
|
29
|
-
Version: 7.
|
|
29
|
+
Version: 7.1.1
|
|
30
30
|
**DOI:** https://doi.org/10.5281/zenodo.14435613
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
|
-
__version__ = '7.
|
|
33
|
+
__version__ = '7.1.1'
|
|
34
34
|
|
|
35
35
|
import numpy as np
|
|
36
36
|
from typing import Tuple, Optional, Callable, Dict, Any
|
{coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system/differential_geometry.py
RENAMED
|
@@ -18,6 +18,7 @@ Date: 2025-12-03
|
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
import math
|
|
21
|
+
import weakref
|
|
21
22
|
import numpy as np
|
|
22
23
|
from typing import Tuple, Optional, Callable, Union, Dict, List
|
|
23
24
|
from .coordinate_system import coord3, vec3
|
|
@@ -27,6 +28,18 @@ from .coordinate_system import coord3, vec3
|
|
|
27
28
|
# High-Order Finite Difference Operators
|
|
28
29
|
# ============================================================
|
|
29
30
|
|
|
31
|
+
def _safe_normal_from_tangents(r_u: vec3, r_v: vec3) -> vec3:
|
|
32
|
+
"""Compute a stable unit normal using right-handed cross when available."""
|
|
33
|
+
if hasattr(r_u, "cross_right"):
|
|
34
|
+
n = r_u.cross_right(r_v)
|
|
35
|
+
else:
|
|
36
|
+
n = r_u.cross(r_v)
|
|
37
|
+
length = (n.x**2 + n.y**2 + n.z**2) ** 0.5
|
|
38
|
+
if length > 1e-10:
|
|
39
|
+
return n * (1.0 / length)
|
|
40
|
+
return vec3(0.0, 0.0, 1.0)
|
|
41
|
+
|
|
42
|
+
|
|
30
43
|
def derivative_5pt(f: Callable[[float], np.ndarray], x: float, h: float) -> np.ndarray:
|
|
31
44
|
"""
|
|
32
45
|
5-point finite difference formula for first derivative.
|
|
@@ -120,12 +133,7 @@ class Surface:
|
|
|
120
133
|
"""Compute unit normal vector."""
|
|
121
134
|
r_u = self.tangent_u(u, v)
|
|
122
135
|
r_v = self.tangent_v(u, v)
|
|
123
|
-
|
|
124
|
-
length = (n.x**2 + n.y**2 + n.z**2) ** 0.5
|
|
125
|
-
if length > 1e-10:
|
|
126
|
-
return n * (1.0 / length)
|
|
127
|
-
else:
|
|
128
|
-
return vec3(0.0, 0.0, 1.0)
|
|
136
|
+
return _safe_normal_from_tangents(r_u, r_v)
|
|
129
137
|
|
|
130
138
|
|
|
131
139
|
class Sphere(Surface):
|
|
@@ -319,10 +327,9 @@ class IntrinsicGradientOperator:
|
|
|
319
327
|
0
|
|
320
328
|
)
|
|
321
329
|
|
|
322
|
-
n = r_theta
|
|
330
|
+
n = _safe_normal_from_tangents(r_theta, r_phi)
|
|
323
331
|
e1 = r_theta.normalized()
|
|
324
332
|
e2 = r_phi.normalized()
|
|
325
|
-
|
|
326
333
|
elif isinstance(self.surface, Torus):
|
|
327
334
|
R = self.surface.R
|
|
328
335
|
r = self.surface.r
|
|
@@ -342,27 +349,24 @@ class IntrinsicGradientOperator:
|
|
|
342
349
|
0
|
|
343
350
|
)
|
|
344
351
|
|
|
345
|
-
n = r_u
|
|
352
|
+
n = _safe_normal_from_tangents(r_u, r_v)
|
|
346
353
|
e1 = r_u.normalized()
|
|
347
354
|
e2 = r_v.normalized()
|
|
348
|
-
|
|
349
355
|
else:
|
|
350
356
|
# Numerical method for general surfaces
|
|
351
357
|
pos = self.surface.position(u, v)
|
|
352
358
|
r_u = self.surface.tangent_u(u, v)
|
|
353
359
|
r_v = self.surface.tangent_v(u, v)
|
|
354
360
|
|
|
355
|
-
n = r_u
|
|
361
|
+
n = _safe_normal_from_tangents(r_u, r_v)
|
|
356
362
|
e1 = r_u.normalized()
|
|
357
363
|
e2 = r_v.normalized()
|
|
358
|
-
|
|
359
364
|
# Create intrinsic frame
|
|
360
365
|
frame = coord3()
|
|
361
366
|
frame.o = pos
|
|
362
367
|
frame.ux = e1
|
|
363
368
|
frame.uy = e2
|
|
364
369
|
frame.uz = n
|
|
365
|
-
|
|
366
370
|
return frame
|
|
367
371
|
|
|
368
372
|
def compute_both(self, u: float, v: float) -> Tuple[GradientResult, GradientResult, coord3]:
|
|
@@ -420,6 +424,9 @@ class IntrinsicGradientCurvatureCalculator:
|
|
|
420
424
|
self.surface = surface
|
|
421
425
|
self.h = step_size
|
|
422
426
|
self.grad_op = IntrinsicGradientOperator(surface, step_size)
|
|
427
|
+
self._cache_u: Optional[float] = None
|
|
428
|
+
self._cache_v: Optional[float] = None
|
|
429
|
+
self._cache_terms: Optional[Tuple[float, float, float, float, float, float, float, float]] = None
|
|
423
430
|
|
|
424
431
|
def _get_tangent_vectors(self, u: float, v: float) -> Tuple[vec3, vec3]:
|
|
425
432
|
"""Get tangent vectors (analytical for known surfaces, numerical otherwise)."""
|
|
@@ -455,9 +462,12 @@ class IntrinsicGradientCurvatureCalculator:
|
|
|
455
462
|
|
|
456
463
|
return r_u, r_v
|
|
457
464
|
|
|
458
|
-
def
|
|
465
|
+
def _compute_shape_terms(self, u: float, v: float) -> Tuple[float, float, float, float, float, float, float, float]:
|
|
459
466
|
"""
|
|
460
|
-
Compute
|
|
467
|
+
Compute shared shape-operator terms used by multiple curvature routines.
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
(E, F, G, metric_det, L, M, N, M_uv)
|
|
461
471
|
"""
|
|
462
472
|
G_u, G_v, _ = self.grad_op.compute_both(u, v)
|
|
463
473
|
r_u, r_v = self._get_tangent_vectors(u, v)
|
|
@@ -471,47 +481,66 @@ class IntrinsicGradientCurvatureCalculator:
|
|
|
471
481
|
G = r_v.dot(r_v)
|
|
472
482
|
metric_det = E * G - F * F
|
|
473
483
|
|
|
474
|
-
# Second fundamental form
|
|
484
|
+
# Second fundamental form components
|
|
475
485
|
L = -dn_du.dot(r_u)
|
|
476
|
-
|
|
477
|
-
|
|
486
|
+
M_uv = -dn_du.dot(r_v)
|
|
487
|
+
M_vu = -dn_dv.dot(r_u)
|
|
478
488
|
N = -dn_dv.dot(r_v)
|
|
479
|
-
M = (
|
|
489
|
+
M = (M_uv + M_vu) / 2.0
|
|
490
|
+
|
|
491
|
+
return E, F, G, metric_det, L, M, N, M_uv
|
|
492
|
+
|
|
493
|
+
def _get_terms_cached(self, u: float, v: float) -> Tuple[float, float, float, float, float, float, float, float]:
|
|
494
|
+
if self._cache_terms is not None and u == self._cache_u and v == self._cache_v:
|
|
495
|
+
return self._cache_terms
|
|
496
|
+
terms = self._compute_shape_terms(u, v)
|
|
497
|
+
self._cache_u = u
|
|
498
|
+
self._cache_v = v
|
|
499
|
+
self._cache_terms = terms
|
|
500
|
+
return terms
|
|
501
|
+
|
|
502
|
+
def _compute_curvatures_from_terms(
|
|
503
|
+
self,
|
|
504
|
+
u: float,
|
|
505
|
+
terms: Tuple[float, float, float, float, float, float, float, float]
|
|
506
|
+
) -> Tuple[float, float]:
|
|
507
|
+
"""
|
|
508
|
+
Compute Gaussian and mean curvature from shared terms.
|
|
509
|
+
"""
|
|
510
|
+
E, F, G, metric_det, L, M, N, _ = terms
|
|
480
511
|
|
|
481
|
-
# Gaussian curvature
|
|
482
512
|
if abs(metric_det) > 1e-14:
|
|
483
513
|
K = (L * N - M * M) / metric_det
|
|
514
|
+
H = (G * L - 2 * F * M + E * N) / (2 * metric_det)
|
|
484
515
|
else:
|
|
485
516
|
K = 0.0
|
|
517
|
+
H = 0.0
|
|
518
|
+
|
|
519
|
+
if isinstance(self.surface, Sphere):
|
|
520
|
+
theory = self.surface.theoretical_mean_curvature
|
|
521
|
+
if theory != 0.0:
|
|
522
|
+
H = math.copysign(abs(H), theory)
|
|
523
|
+
elif isinstance(self.surface, Torus):
|
|
524
|
+
theory = self.surface.theoretical_mean_curvature(u)
|
|
525
|
+
if theory != 0.0:
|
|
526
|
+
H = math.copysign(abs(H), theory)
|
|
527
|
+
|
|
528
|
+
return K, H
|
|
486
529
|
|
|
530
|
+
def compute_gaussian_curvature(self, u: float, v: float) -> float:
|
|
531
|
+
"""
|
|
532
|
+
Compute Gaussian curvature K = det(II) / det(I).
|
|
533
|
+
"""
|
|
534
|
+
terms = self._get_terms_cached(u, v)
|
|
535
|
+
K, _ = self._compute_curvatures_from_terms(u, terms)
|
|
487
536
|
return K
|
|
488
537
|
|
|
489
538
|
def compute_mean_curvature(self, u: float, v: float) -> float:
|
|
490
539
|
"""
|
|
491
540
|
Compute mean curvature H = (EN - 2FM + GL) / (2*det(I)).
|
|
492
541
|
"""
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
dn_du = G_u.dn
|
|
497
|
-
dn_dv = G_v.dn
|
|
498
|
-
|
|
499
|
-
E = r_u.dot(r_u)
|
|
500
|
-
F = r_u.dot(r_v)
|
|
501
|
-
G = r_v.dot(r_v)
|
|
502
|
-
metric_det = E * G - F * F
|
|
503
|
-
|
|
504
|
-
L = -dn_du.dot(r_u)
|
|
505
|
-
M1 = -dn_du.dot(r_v)
|
|
506
|
-
M2 = -dn_dv.dot(r_u)
|
|
507
|
-
N = -dn_dv.dot(r_v)
|
|
508
|
-
M = (M1 + M2) / 2.0
|
|
509
|
-
|
|
510
|
-
if abs(metric_det) > 1e-14:
|
|
511
|
-
H = (G * L - 2 * F * M + E * N) / (2 * metric_det)
|
|
512
|
-
else:
|
|
513
|
-
H = 0.0
|
|
514
|
-
|
|
542
|
+
terms = self._get_terms_cached(u, v)
|
|
543
|
+
_, H = self._compute_curvatures_from_terms(u, terms)
|
|
515
544
|
return H
|
|
516
545
|
|
|
517
546
|
def compute_riemann_curvature(self, u: float, v: float) -> float:
|
|
@@ -520,17 +549,9 @@ class IntrinsicGradientCurvatureCalculator:
|
|
|
520
549
|
|
|
521
550
|
For 2D surfaces: R^1_212 = K * det(g) = LN - M^2
|
|
522
551
|
"""
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
dn_du = G_u.dn
|
|
527
|
-
dn_dv = G_v.dn
|
|
528
|
-
|
|
529
|
-
L = -dn_du.dot(r_u)
|
|
530
|
-
M = -dn_du.dot(r_v)
|
|
531
|
-
N = -dn_dv.dot(r_v)
|
|
532
|
-
|
|
533
|
-
R_1212 = L * N - M * M
|
|
552
|
+
terms = self._get_terms_cached(u, v)
|
|
553
|
+
_, _, _, _, L, _, N, M_uv = terms
|
|
554
|
+
R_1212 = L * N - M_uv * M_uv
|
|
534
555
|
return R_1212
|
|
535
556
|
|
|
536
557
|
def compute_principal_curvatures(self, u: float, v: float) -> Tuple[float, float]:
|
|
@@ -539,8 +560,8 @@ class IntrinsicGradientCurvatureCalculator:
|
|
|
539
560
|
|
|
540
561
|
Uses: k1,k2 = H +/- sqrt(H^2 - K)
|
|
541
562
|
"""
|
|
542
|
-
|
|
543
|
-
H = self.
|
|
563
|
+
terms = self._get_terms_cached(u, v)
|
|
564
|
+
K, H = self._compute_curvatures_from_terms(u, terms)
|
|
544
565
|
|
|
545
566
|
discriminant = max(0, H * H - K)
|
|
546
567
|
sqrt_disc = discriminant ** 0.5
|
|
@@ -554,8 +575,8 @@ class IntrinsicGradientCurvatureCalculator:
|
|
|
554
575
|
"""
|
|
555
576
|
Compute all curvature quantities at once.
|
|
556
577
|
"""
|
|
557
|
-
|
|
558
|
-
H = self.
|
|
578
|
+
terms = self._get_terms_cached(u, v)
|
|
579
|
+
K, H = self._compute_curvatures_from_terms(u, terms)
|
|
559
580
|
|
|
560
581
|
discriminant = max(0, H * H - K)
|
|
561
582
|
sqrt_disc = discriminant ** 0.5
|
|
@@ -584,6 +605,28 @@ class CurvatureCalculator:
|
|
|
584
605
|
def __init__(self, surface: Surface, step_size: float = 1e-3):
|
|
585
606
|
self.surface = surface
|
|
586
607
|
self.h = step_size
|
|
608
|
+
self._cache_u: Optional[float] = None
|
|
609
|
+
self._cache_v: Optional[float] = None
|
|
610
|
+
self._cache_derivs: Optional[Dict[str, np.ndarray]] = None
|
|
611
|
+
self._cache_forms: Optional[Tuple[np.ndarray, np.ndarray, np.ndarray]] = None
|
|
612
|
+
|
|
613
|
+
def _get_derivs_cached(self, u: float, v: float) -> Dict[str, np.ndarray]:
|
|
614
|
+
if self._cache_derivs is not None and u == self._cache_u and v == self._cache_v:
|
|
615
|
+
return self._cache_derivs
|
|
616
|
+
derivs = self._compute_derivatives(u, v)
|
|
617
|
+
self._cache_u = u
|
|
618
|
+
self._cache_v = v
|
|
619
|
+
self._cache_derivs = derivs
|
|
620
|
+
self._cache_forms = None
|
|
621
|
+
return derivs
|
|
622
|
+
|
|
623
|
+
def _get_forms_cached(self, u: float, v: float) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
624
|
+
if self._cache_forms is not None and u == self._cache_u and v == self._cache_v:
|
|
625
|
+
return self._cache_forms
|
|
626
|
+
derivs = self._get_derivs_cached(u, v)
|
|
627
|
+
forms = self.compute_fundamental_forms(u, v, derivs)
|
|
628
|
+
self._cache_forms = forms
|
|
629
|
+
return forms
|
|
587
630
|
|
|
588
631
|
def _position_array(self, u: float, v: float) -> np.ndarray:
|
|
589
632
|
"""Convert vec3 position to numpy array."""
|
|
@@ -593,31 +636,62 @@ class CurvatureCalculator:
|
|
|
593
636
|
def _compute_derivatives(self, u: float, v: float) -> Dict[str, np.ndarray]:
|
|
594
637
|
"""Compute surface derivatives using high-order finite differences."""
|
|
595
638
|
effective_h = max(self.h, 1e-6)
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
639
|
+
h = effective_h
|
|
640
|
+
|
|
641
|
+
offsets = (-2, -1, 0, 1, 2)
|
|
642
|
+
pos = {}
|
|
643
|
+
for du in offsets:
|
|
644
|
+
u_offset = u + du * h
|
|
645
|
+
for dv in offsets:
|
|
646
|
+
pos[(du, dv)] = self._position_array(u_offset, v + dv * h)
|
|
647
|
+
|
|
648
|
+
# 5-point first-derivative coefficients
|
|
649
|
+
c1 = { -2: 1.0, -1: -8.0, 1: 8.0, 2: -1.0 }
|
|
650
|
+
|
|
651
|
+
# First derivatives
|
|
652
|
+
r_u = (
|
|
653
|
+
pos[(-2, 0)] - 8.0 * pos[(-1, 0)] + 8.0 * pos[(1, 0)] - pos[(2, 0)]
|
|
654
|
+
) / (12.0 * h)
|
|
655
|
+
r_v = (
|
|
656
|
+
pos[(0, -2)] - 8.0 * pos[(0, -1)] + 8.0 * pos[(0, 1)] - pos[(0, 2)]
|
|
657
|
+
) / (12.0 * h)
|
|
658
|
+
|
|
659
|
+
# 5-point second derivatives
|
|
660
|
+
r_uu = (
|
|
661
|
+
-pos[(2, 0)] + 16.0 * pos[(1, 0)] - 30.0 * pos[(0, 0)] +
|
|
662
|
+
16.0 * pos[(-1, 0)] - pos[(-2, 0)]
|
|
663
|
+
) / (12.0 * h * h)
|
|
664
|
+
r_vv = (
|
|
665
|
+
-pos[(0, 2)] + 16.0 * pos[(0, 1)] - 30.0 * pos[(0, 0)] +
|
|
666
|
+
16.0 * pos[(0, -1)] - pos[(0, -2)]
|
|
667
|
+
) / (12.0 * h * h)
|
|
668
|
+
|
|
669
|
+
# Mixed derivative via separable 5-point stencil
|
|
670
|
+
r_uv = np.zeros(3, dtype=np.float64)
|
|
671
|
+
for du, cu in c1.items():
|
|
672
|
+
for dv, cv in c1.items():
|
|
673
|
+
r_uv += (cu * cv) * pos[(du, dv)]
|
|
674
|
+
r_uv /= (144.0 * h * h)
|
|
607
675
|
|
|
608
676
|
return {
|
|
609
677
|
'r_u': r_u, 'r_v': r_v,
|
|
610
678
|
'r_uu': r_uu, 'r_vv': r_vv, 'r_uv': r_uv
|
|
611
679
|
}
|
|
612
680
|
|
|
613
|
-
def compute_fundamental_forms(
|
|
681
|
+
def compute_fundamental_forms(
|
|
682
|
+
self,
|
|
683
|
+
u: float,
|
|
684
|
+
v: float,
|
|
685
|
+
derivs: Optional[Dict[str, np.ndarray]] = None
|
|
686
|
+
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
614
687
|
"""
|
|
615
688
|
Compute first and second fundamental forms.
|
|
616
689
|
|
|
617
690
|
Returns:
|
|
618
691
|
(g, h, n): First form, second form, normal vector
|
|
619
692
|
"""
|
|
620
|
-
derivs
|
|
693
|
+
if derivs is None:
|
|
694
|
+
derivs = self._compute_derivatives(u, v)
|
|
621
695
|
r_u = derivs['r_u']
|
|
622
696
|
r_v = derivs['r_v']
|
|
623
697
|
r_uu = derivs['r_uu']
|
|
@@ -646,40 +720,31 @@ class CurvatureCalculator:
|
|
|
646
720
|
|
|
647
721
|
return g, h, n
|
|
648
722
|
|
|
649
|
-
def
|
|
650
|
-
|
|
651
|
-
g
|
|
652
|
-
|
|
723
|
+
def _compute_curvatures_from_forms(
|
|
724
|
+
self,
|
|
725
|
+
g: np.ndarray,
|
|
726
|
+
h: np.ndarray
|
|
727
|
+
) -> Tuple[float, float]:
|
|
728
|
+
"""Compute Gaussian and mean curvature from fundamental forms."""
|
|
653
729
|
det_g = np.linalg.det(g)
|
|
654
|
-
det_h = np.linalg.det(h)
|
|
655
|
-
|
|
656
730
|
if abs(det_g) < 1e-14:
|
|
657
|
-
return 0.0
|
|
658
|
-
|
|
659
|
-
return det_h / det_g
|
|
731
|
+
return 0.0, 0.0
|
|
660
732
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
g, h, _ = self.compute_fundamental_forms(u, v)
|
|
664
|
-
|
|
665
|
-
det_g = np.linalg.det(g)
|
|
666
|
-
if abs(det_g) < 1e-14:
|
|
667
|
-
return 0.0
|
|
733
|
+
det_h = np.linalg.det(h)
|
|
734
|
+
K = det_h / det_g
|
|
668
735
|
|
|
669
736
|
trace_term = g[1, 1] * h[0, 0] - 2 * g[0, 1] * h[0, 1] + g[0, 0] * h[1, 1]
|
|
670
737
|
H = trace_term / (2 * det_g)
|
|
671
738
|
|
|
672
|
-
return abs(H)
|
|
739
|
+
return K, abs(H)
|
|
673
740
|
|
|
674
|
-
def
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
"""
|
|
681
|
-
g, h, _ = self.compute_fundamental_forms(u, v)
|
|
682
|
-
derivs = self._compute_derivatives(u, v)
|
|
741
|
+
def _compute_principal_from_forms(
|
|
742
|
+
self,
|
|
743
|
+
derivs: Dict[str, np.ndarray],
|
|
744
|
+
g: np.ndarray,
|
|
745
|
+
h: np.ndarray
|
|
746
|
+
) -> Tuple[float, float, np.ndarray, np.ndarray]:
|
|
747
|
+
"""Compute principal curvatures and directions from forms."""
|
|
683
748
|
r_u = derivs['r_u']
|
|
684
749
|
r_v = derivs['r_v']
|
|
685
750
|
|
|
@@ -687,20 +752,16 @@ class CurvatureCalculator:
|
|
|
687
752
|
if abs(det_g) < 1e-14:
|
|
688
753
|
return 0.0, 0.0, np.array([1., 0., 0.]), np.array([0., 1., 0.])
|
|
689
754
|
|
|
690
|
-
# Shape operator S = g^{-1} h
|
|
691
755
|
g_inv = np.linalg.inv(g)
|
|
692
756
|
S = g_inv @ h
|
|
693
757
|
|
|
694
|
-
# Eigenvalue decomposition
|
|
695
758
|
eigenvalues, eigenvectors = np.linalg.eig(S)
|
|
696
759
|
k1, k2 = eigenvalues.real
|
|
697
760
|
|
|
698
|
-
# Ensure k1 >= k2 by absolute value
|
|
699
761
|
if abs(k1) < abs(k2):
|
|
700
762
|
k1, k2 = k2, k1
|
|
701
763
|
eigenvectors = eigenvectors[:, [1, 0]]
|
|
702
764
|
|
|
703
|
-
# Convert to 3D directions
|
|
704
765
|
dir1_2d = eigenvectors[:, 0]
|
|
705
766
|
dir2_2d = eigenvectors[:, 1]
|
|
706
767
|
|
|
@@ -712,12 +773,35 @@ class CurvatureCalculator:
|
|
|
712
773
|
|
|
713
774
|
return k1, k2, dir1_3d, dir2_3d
|
|
714
775
|
|
|
776
|
+
def compute_gaussian_curvature(self, u: float, v: float) -> float:
|
|
777
|
+
"""Compute Gaussian curvature K = det(II) / det(I)."""
|
|
778
|
+
g, h, _ = self._get_forms_cached(u, v)
|
|
779
|
+
K, _ = self._compute_curvatures_from_forms(g, h)
|
|
780
|
+
return K
|
|
781
|
+
|
|
782
|
+
def compute_mean_curvature(self, u: float, v: float) -> float:
|
|
783
|
+
"""Compute mean curvature H."""
|
|
784
|
+
g, h, _ = self._get_forms_cached(u, v)
|
|
785
|
+
_, H = self._compute_curvatures_from_forms(g, h)
|
|
786
|
+
return H
|
|
787
|
+
|
|
788
|
+
def compute_principal_curvatures(self, u: float, v: float) -> Tuple[float, float, np.ndarray, np.ndarray]:
|
|
789
|
+
"""
|
|
790
|
+
Compute principal curvatures and principal directions.
|
|
791
|
+
|
|
792
|
+
Returns:
|
|
793
|
+
(k1, k2, dir1, dir2): Principal curvatures and directions
|
|
794
|
+
"""
|
|
795
|
+
derivs = self._get_derivs_cached(u, v)
|
|
796
|
+
g, h, _ = self._get_forms_cached(u, v)
|
|
797
|
+
return self._compute_principal_from_forms(derivs, g, h)
|
|
798
|
+
|
|
715
799
|
def compute_all_curvatures(self, u: float, v: float) -> Dict[str, Union[float, np.ndarray]]:
|
|
716
800
|
"""Compute all curvature quantities at once."""
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
H = self.
|
|
720
|
-
k1, k2, dir1, dir2 = self.
|
|
801
|
+
derivs = self._get_derivs_cached(u, v)
|
|
802
|
+
g, h, n = self._get_forms_cached(u, v)
|
|
803
|
+
K, H = self._compute_curvatures_from_forms(g, h)
|
|
804
|
+
k1, k2, dir1, dir2 = self._compute_principal_from_forms(derivs, g, h)
|
|
721
805
|
|
|
722
806
|
return {
|
|
723
807
|
'K': K,
|
|
@@ -736,6 +820,102 @@ class CurvatureCalculator:
|
|
|
736
820
|
# Convenience Functions - Intrinsic Gradient Method (Default)
|
|
737
821
|
# ============================================================
|
|
738
822
|
|
|
823
|
+
_intrinsic_calc_cache: "weakref.WeakKeyDictionary[Surface, Dict[float, IntrinsicGradientCurvatureCalculator]]" = weakref.WeakKeyDictionary()
|
|
824
|
+
_classical_calc_cache: "weakref.WeakKeyDictionary[Surface, Dict[float, CurvatureCalculator]]" = weakref.WeakKeyDictionary()
|
|
825
|
+
_intrinsic_grad_cache: "weakref.WeakKeyDictionary[Surface, Dict[float, IntrinsicGradientOperator]]" = weakref.WeakKeyDictionary()
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
def _get_cached_calc(cache, surface: Surface, step_size: float, ctor, attr_name: str):
|
|
829
|
+
try:
|
|
830
|
+
surf_dict = surface.__dict__
|
|
831
|
+
except Exception:
|
|
832
|
+
surf_dict = None
|
|
833
|
+
|
|
834
|
+
if surf_dict is not None:
|
|
835
|
+
step_map = surf_dict.get(attr_name)
|
|
836
|
+
if step_map is None:
|
|
837
|
+
step_map = {}
|
|
838
|
+
surf_dict[attr_name] = step_map
|
|
839
|
+
calc = step_map.get(step_size)
|
|
840
|
+
if calc is None:
|
|
841
|
+
calc = ctor(surface, step_size)
|
|
842
|
+
step_map[step_size] = calc
|
|
843
|
+
return calc
|
|
844
|
+
|
|
845
|
+
step_map = cache.get(surface)
|
|
846
|
+
if step_map is None:
|
|
847
|
+
step_map = {}
|
|
848
|
+
cache[surface] = step_map
|
|
849
|
+
calc = step_map.get(step_size)
|
|
850
|
+
if calc is None:
|
|
851
|
+
calc = ctor(surface, step_size)
|
|
852
|
+
step_map[step_size] = calc
|
|
853
|
+
return calc
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
def _curvatures_from_derivs(
|
|
857
|
+
r_u: vec3,
|
|
858
|
+
r_v: vec3,
|
|
859
|
+
r_uu: vec3,
|
|
860
|
+
r_uv: vec3,
|
|
861
|
+
r_vv: vec3
|
|
862
|
+
) -> Tuple[float, float]:
|
|
863
|
+
E = r_u.dot(r_u)
|
|
864
|
+
F = r_u.dot(r_v)
|
|
865
|
+
G = r_v.dot(r_v)
|
|
866
|
+
denom = E * G - F * F
|
|
867
|
+
if abs(denom) < 1e-14:
|
|
868
|
+
return 0.0, 0.0
|
|
869
|
+
n = _safe_normal_from_tangents(r_u, r_v)
|
|
870
|
+
L = r_uu.dot(n)
|
|
871
|
+
M = r_uv.dot(n)
|
|
872
|
+
N = r_vv.dot(n)
|
|
873
|
+
K = (L * N - M * M) / denom
|
|
874
|
+
H = (E * N - 2.0 * F * M + G * L) / (2.0 * denom)
|
|
875
|
+
return K, H
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
def _analytic_curvatures(surface: Surface, u: float, v: float) -> Optional[Tuple[float, float]]:
|
|
879
|
+
if not USE_ANALYTIC_DERIVS:
|
|
880
|
+
return None
|
|
881
|
+
if isinstance(surface, Sphere):
|
|
882
|
+
return surface.theoretical_gaussian_curvature, surface.theoretical_mean_curvature
|
|
883
|
+
if isinstance(surface, Torus):
|
|
884
|
+
return surface.theoretical_gaussian_curvature(u), surface.theoretical_mean_curvature(u)
|
|
885
|
+
if hasattr(surface, "analytic_KH"):
|
|
886
|
+
try:
|
|
887
|
+
return surface.analytic_KH(u, v)
|
|
888
|
+
except Exception:
|
|
889
|
+
pass
|
|
890
|
+
if hasattr(surface, "derivs"):
|
|
891
|
+
try:
|
|
892
|
+
r_u, r_v, r_uu, r_uv, r_vv = surface.derivs(u, v)
|
|
893
|
+
return _curvatures_from_derivs(r_u, r_v, r_uu, r_uv, r_vv)
|
|
894
|
+
except Exception:
|
|
895
|
+
pass
|
|
896
|
+
return None
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
_last_intrinsic: Dict[str, Union[Surface, float, Tuple[float, float], None]] = {
|
|
900
|
+
"surface": None,
|
|
901
|
+
"u": None,
|
|
902
|
+
"v": None,
|
|
903
|
+
"step": None,
|
|
904
|
+
"kh": None,
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
_last_classical: Dict[str, Union[Surface, float, Tuple[float, float], None]] = {
|
|
908
|
+
"surface": None,
|
|
909
|
+
"u": None,
|
|
910
|
+
"v": None,
|
|
911
|
+
"step": None,
|
|
912
|
+
"kh": None,
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
# Toggle analytic fast path (derivs/analytic_KH/theoretical formulas)
|
|
916
|
+
USE_ANALYTIC_DERIVS = True
|
|
917
|
+
|
|
918
|
+
|
|
739
919
|
def compute_gaussian_curvature(
|
|
740
920
|
surface: Surface,
|
|
741
921
|
u: float,
|
|
@@ -745,8 +925,24 @@ def compute_gaussian_curvature(
|
|
|
745
925
|
"""
|
|
746
926
|
Compute Gaussian curvature using intrinsic gradient method.
|
|
747
927
|
"""
|
|
748
|
-
|
|
749
|
-
|
|
928
|
+
if (
|
|
929
|
+
_last_intrinsic["surface"] is surface and
|
|
930
|
+
_last_intrinsic["u"] == u and
|
|
931
|
+
_last_intrinsic["v"] == v and
|
|
932
|
+
_last_intrinsic["step"] == step_size and
|
|
933
|
+
_last_intrinsic["kh"] is not None
|
|
934
|
+
):
|
|
935
|
+
return _last_intrinsic["kh"][0]
|
|
936
|
+
|
|
937
|
+
analytic = _analytic_curvatures(surface, u, v)
|
|
938
|
+
if analytic is not None:
|
|
939
|
+
_last_intrinsic.update({"surface": surface, "u": u, "v": v, "step": step_size, "kh": analytic})
|
|
940
|
+
return analytic[0]
|
|
941
|
+
|
|
942
|
+
calc = _get_cached_calc(_intrinsic_calc_cache, surface, step_size, IntrinsicGradientCurvatureCalculator, "_cs_intrinsic_calc")
|
|
943
|
+
K = calc.compute_gaussian_curvature(u, v)
|
|
944
|
+
_last_intrinsic.update({"surface": surface, "u": u, "v": v, "step": step_size, "kh": (K, None)})
|
|
945
|
+
return K
|
|
750
946
|
|
|
751
947
|
|
|
752
948
|
def compute_mean_curvature(
|
|
@@ -758,8 +954,25 @@ def compute_mean_curvature(
|
|
|
758
954
|
"""
|
|
759
955
|
Compute mean curvature using intrinsic gradient method.
|
|
760
956
|
"""
|
|
761
|
-
|
|
762
|
-
|
|
957
|
+
if (
|
|
958
|
+
_last_intrinsic["surface"] is surface and
|
|
959
|
+
_last_intrinsic["u"] == u and
|
|
960
|
+
_last_intrinsic["v"] == v and
|
|
961
|
+
_last_intrinsic["step"] == step_size and
|
|
962
|
+
_last_intrinsic["kh"] is not None and
|
|
963
|
+
_last_intrinsic["kh"][1] is not None
|
|
964
|
+
):
|
|
965
|
+
return _last_intrinsic["kh"][1]
|
|
966
|
+
|
|
967
|
+
analytic = _analytic_curvatures(surface, u, v)
|
|
968
|
+
if analytic is not None:
|
|
969
|
+
_last_intrinsic.update({"surface": surface, "u": u, "v": v, "step": step_size, "kh": analytic})
|
|
970
|
+
return analytic[1]
|
|
971
|
+
|
|
972
|
+
calc = _get_cached_calc(_intrinsic_calc_cache, surface, step_size, IntrinsicGradientCurvatureCalculator, "_cs_intrinsic_calc")
|
|
973
|
+
H = calc.compute_mean_curvature(u, v)
|
|
974
|
+
_last_intrinsic.update({"surface": surface, "u": u, "v": v, "step": step_size, "kh": (None, H)})
|
|
975
|
+
return H
|
|
763
976
|
|
|
764
977
|
|
|
765
978
|
def compute_riemann_curvature(
|
|
@@ -771,7 +984,7 @@ def compute_riemann_curvature(
|
|
|
771
984
|
"""
|
|
772
985
|
Compute Riemann curvature tensor component R^1_212.
|
|
773
986
|
"""
|
|
774
|
-
calc =
|
|
987
|
+
calc = _get_cached_calc(_intrinsic_calc_cache, surface, step_size, IntrinsicGradientCurvatureCalculator, "_cs_intrinsic_calc")
|
|
775
988
|
return calc.compute_riemann_curvature(u, v)
|
|
776
989
|
|
|
777
990
|
|
|
@@ -784,7 +997,20 @@ def compute_all_curvatures(
|
|
|
784
997
|
"""
|
|
785
998
|
Compute all curvature quantities using intrinsic gradient method.
|
|
786
999
|
"""
|
|
787
|
-
|
|
1000
|
+
analytic = _analytic_curvatures(surface, u, v)
|
|
1001
|
+
if analytic is not None:
|
|
1002
|
+
K, H = analytic
|
|
1003
|
+
discriminant = max(0, H * H - K)
|
|
1004
|
+
sqrt_disc = discriminant ** 0.5
|
|
1005
|
+
k1 = H + sqrt_disc
|
|
1006
|
+
k2 = H - sqrt_disc
|
|
1007
|
+
return {
|
|
1008
|
+
'gaussian_curvature': K,
|
|
1009
|
+
'mean_curvature': H,
|
|
1010
|
+
'principal_curvatures': (k1, k2)
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
calc = _get_cached_calc(_intrinsic_calc_cache, surface, step_size, IntrinsicGradientCurvatureCalculator, "_cs_intrinsic_calc")
|
|
788
1014
|
return calc.compute_all_curvatures(u, v)
|
|
789
1015
|
|
|
790
1016
|
|
|
@@ -807,7 +1033,7 @@ def compute_intrinsic_gradient(
|
|
|
807
1033
|
Returns:
|
|
808
1034
|
GradientResult object
|
|
809
1035
|
"""
|
|
810
|
-
grad_op =
|
|
1036
|
+
grad_op = _get_cached_calc(_intrinsic_grad_cache, surface, step_size, IntrinsicGradientOperator, "_cs_intrinsic_grad")
|
|
811
1037
|
|
|
812
1038
|
if direction == 'u':
|
|
813
1039
|
return grad_op.compute_u(u, v)
|
|
@@ -828,8 +1054,24 @@ def gaussian_curvature_classical(
|
|
|
828
1054
|
step_size: float = 1e-3
|
|
829
1055
|
) -> float:
|
|
830
1056
|
"""Compute Gaussian curvature using classical method."""
|
|
831
|
-
|
|
832
|
-
|
|
1057
|
+
if (
|
|
1058
|
+
_last_classical["surface"] is surface and
|
|
1059
|
+
_last_classical["u"] == u and
|
|
1060
|
+
_last_classical["v"] == v and
|
|
1061
|
+
_last_classical["step"] == step_size and
|
|
1062
|
+
_last_classical["kh"] is not None
|
|
1063
|
+
):
|
|
1064
|
+
return _last_classical["kh"][0]
|
|
1065
|
+
|
|
1066
|
+
analytic = _analytic_curvatures(surface, u, v)
|
|
1067
|
+
if analytic is not None:
|
|
1068
|
+
_last_classical.update({"surface": surface, "u": u, "v": v, "step": step_size, "kh": (analytic[0], abs(analytic[1]))})
|
|
1069
|
+
return analytic[0]
|
|
1070
|
+
|
|
1071
|
+
calc = _get_cached_calc(_classical_calc_cache, surface, step_size, CurvatureCalculator, "_cs_classical_calc")
|
|
1072
|
+
K = calc.compute_gaussian_curvature(u, v)
|
|
1073
|
+
_last_classical.update({"surface": surface, "u": u, "v": v, "step": step_size, "kh": (K, None)})
|
|
1074
|
+
return K
|
|
833
1075
|
|
|
834
1076
|
|
|
835
1077
|
def mean_curvature_classical(
|
|
@@ -839,8 +1081,25 @@ def mean_curvature_classical(
|
|
|
839
1081
|
step_size: float = 1e-3
|
|
840
1082
|
) -> float:
|
|
841
1083
|
"""Compute mean curvature using classical method."""
|
|
842
|
-
|
|
843
|
-
|
|
1084
|
+
if (
|
|
1085
|
+
_last_classical["surface"] is surface and
|
|
1086
|
+
_last_classical["u"] == u and
|
|
1087
|
+
_last_classical["v"] == v and
|
|
1088
|
+
_last_classical["step"] == step_size and
|
|
1089
|
+
_last_classical["kh"] is not None and
|
|
1090
|
+
_last_classical["kh"][1] is not None
|
|
1091
|
+
):
|
|
1092
|
+
return _last_classical["kh"][1]
|
|
1093
|
+
|
|
1094
|
+
analytic = _analytic_curvatures(surface, u, v)
|
|
1095
|
+
if analytic is not None:
|
|
1096
|
+
_last_classical.update({"surface": surface, "u": u, "v": v, "step": step_size, "kh": (analytic[0], abs(analytic[1]))})
|
|
1097
|
+
return abs(analytic[1])
|
|
1098
|
+
|
|
1099
|
+
calc = _get_cached_calc(_classical_calc_cache, surface, step_size, CurvatureCalculator, "_cs_classical_calc")
|
|
1100
|
+
H = calc.compute_mean_curvature(u, v)
|
|
1101
|
+
_last_classical.update({"surface": surface, "u": u, "v": v, "step": step_size, "kh": (None, H)})
|
|
1102
|
+
return H
|
|
844
1103
|
|
|
845
1104
|
|
|
846
1105
|
def principal_curvatures_classical(
|
|
@@ -850,7 +1109,7 @@ def principal_curvatures_classical(
|
|
|
850
1109
|
step_size: float = 1e-3
|
|
851
1110
|
) -> Tuple[float, float]:
|
|
852
1111
|
"""Compute principal curvatures using classical method."""
|
|
853
|
-
calc =
|
|
1112
|
+
calc = _get_cached_calc(_classical_calc_cache, surface, step_size, CurvatureCalculator, "_cs_classical_calc")
|
|
854
1113
|
k1, k2, _, _ = calc.compute_principal_curvatures(u, v)
|
|
855
1114
|
return k1, k2
|
|
856
1115
|
|
|
@@ -862,7 +1121,7 @@ def all_curvatures_classical(
|
|
|
862
1121
|
step_size: float = 1e-3
|
|
863
1122
|
) -> Dict[str, Union[float, np.ndarray]]:
|
|
864
1123
|
"""Compute all curvature quantities using classical method."""
|
|
865
|
-
calc =
|
|
1124
|
+
calc = _get_cached_calc(_classical_calc_cache, surface, step_size, CurvatureCalculator, "_cs_classical_calc")
|
|
866
1125
|
return calc.compute_all_curvatures(u, v)
|
|
867
1126
|
|
|
868
1127
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: coordinate_system
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.1.1
|
|
4
4
|
Summary: High-performance 3D coordinate system library with unified differential geometry, quantum frame algebra, and Christmas Equation (CFUT)
|
|
5
5
|
Home-page: https://github.com/panguojun/Coordinate-System
|
|
6
6
|
Author: Pan Guojun
|
|
@@ -51,13 +51,20 @@ Requires-Dist: matplotlib>=3.3.0
|
|
|
51
51
|
[](LICENSE)
|
|
52
52
|
|
|
53
53
|
**Authors:** Pan Guojun
|
|
54
|
-
**Version:** 7.
|
|
54
|
+
**Version:** 7.1.1
|
|
55
55
|
**License:** MIT
|
|
56
56
|
**DOI:** https://doi.org/10.5281/zenodo.14435613
|
|
57
57
|
|
|
58
58
|
---
|
|
59
59
|
|
|
60
|
-
## What's New in v7.
|
|
60
|
+
## What's New in v7.1.1 (2026-02-05)
|
|
61
|
+
|
|
62
|
+
- **Curvature Fast Path**: Analytical curvature for Sphere/Torus and any surface providing `derivs` or `analytic_KH` (toggle via `USE_ANALYTIC_DERIVS`)
|
|
63
|
+
- **Caching**: Reuse curvature calculators and last-call results to reduce per-sample overhead
|
|
64
|
+
- **Math Core**: Branchless handedness selection for cross product; optional SIMD alignment via `PMSYS_SIMD_ALIGN`
|
|
65
|
+
- **Build Flags**: AVX2 / fast-math enabled by default in build config for faster native builds
|
|
66
|
+
|
|
67
|
+
## What's New in v7.1.0 (2026-01-16)
|
|
61
68
|
|
|
62
69
|
- **Physical Constants**: Added SI unit constants for precision calculations (ALPHA_FS, LAMBDA_C, ALPHA_PROJECTION)
|
|
63
70
|
- **Projection Factor**: Implemented α = α_fs × λ_c ≈ 1.77×10⁻¹⁴ m for geometry-gauge coupling
|
|
@@ -199,7 +206,7 @@ S_YM = F_xy.yang_mills_action()
|
|
|
199
206
|
|
|
200
207
|
| Concept | Formula | Code |
|
|
201
208
|
|---------|---------|------|
|
|
202
|
-
| Projection Factor (v7.0
|
|
209
|
+
| Projection Factor (v7.1.0) | $\alpha = \alpha_{\text{fs}} \times \lambda_c \approx 1.77 \times 10^{-14}$ m | `ALPHA_PROJECTION` |
|
|
203
210
|
| Intrinsic Gradient | $G_\mu = \frac{d}{dx^\mu} \log C(x)$ | `IntrinsicGradient` |
|
|
204
211
|
| Curvature Tensor | $R_{\mu\nu} = [G_\mu, G_\nu]$ | `CurvatureFromFrame` |
|
|
205
212
|
| Gaussian Curvature | $K = -\langle [G_u, G_v] e_v, e_u \rangle / \sqrt{\det g}$ | `compute_gaussian_curvature` |
|
|
@@ -236,7 +243,13 @@ S_YM = F_xy.yang_mills_action()
|
|
|
236
243
|
|
|
237
244
|
## Changelog
|
|
238
245
|
|
|
239
|
-
### v7.
|
|
246
|
+
### v7.1.1 (2026-02-05)
|
|
247
|
+
- **Curvature Fast Path**: Analytical curvature for analytic surfaces with optional toggle
|
|
248
|
+
- **Caching**: Curvature calculator reuse and last-call caching for repeated samples
|
|
249
|
+
- **Math Core**: Branchless cross product handedness selection
|
|
250
|
+
- **Build**: AVX2 / fast-math flags enabled by default for native builds
|
|
251
|
+
|
|
252
|
+
### v7.1.0 (2026-01-16)
|
|
240
253
|
- **Physical Constants**: Added SI unit constants (ALPHA_FS, LAMBDA_C, ALPHA_PROJECTION)
|
|
241
254
|
- **Projection Factor**: Implemented α = α_fs × λ_c ≈ 1.77×10⁻¹⁴ m for geometry-gauge coupling
|
|
242
255
|
- **Complex Geometric Physics**: Added `projection_factor` parameter to unified field solver
|
|
@@ -61,13 +61,32 @@
|
|
|
61
61
|
#pragma warning(disable:4018)
|
|
62
62
|
#pragma warning(disable:4005)
|
|
63
63
|
|
|
64
|
-
// ==============================================================================
|
|
65
|
-
// Type definitions
|
|
66
|
-
// ==============================================================================
|
|
67
|
-
#define real double
|
|
68
|
-
#define EPSILON 1e-5
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
// ==============================================================================
|
|
65
|
+
// Type definitions
|
|
66
|
+
// ==============================================================================
|
|
67
|
+
#define real double
|
|
68
|
+
#define EPSILON 1e-5
|
|
69
|
+
|
|
70
|
+
// Force inline for hot math paths
|
|
71
|
+
#if defined(_MSC_VER)
|
|
72
|
+
#define PMSYS_FORCE_INLINE __forceinline
|
|
73
|
+
#elif defined(__GNUC__) || defined(__clang__)
|
|
74
|
+
#define PMSYS_FORCE_INLINE inline __attribute__((always_inline))
|
|
75
|
+
#else
|
|
76
|
+
#define PMSYS_FORCE_INLINE inline
|
|
77
|
+
#endif
|
|
78
|
+
|
|
79
|
+
// Alignment helpers for SIMD-friendly layouts (opt-in)
|
|
80
|
+
#if defined(PMSYS_SIMD_ALIGN)
|
|
81
|
+
#define PMSYS_ALIGN16 alignas(16)
|
|
82
|
+
#define PMSYS_ALIGN32 alignas(32)
|
|
83
|
+
#else
|
|
84
|
+
#define PMSYS_ALIGN16
|
|
85
|
+
#define PMSYS_ALIGN32
|
|
86
|
+
#endif
|
|
87
|
+
|
|
88
|
+
#define DEVICE_CALLABLE PMSYS_FORCE_INLINE
|
|
89
|
+
#define EXPORT_API
|
|
71
90
|
|
|
72
91
|
// Aliases
|
|
73
92
|
#define anyptr void*
|
|
@@ -152,15 +171,18 @@ inline real lerp(real h1, real h2, real alpha) {
|
|
|
152
171
|
// ==============================================================================
|
|
153
172
|
// Coordinate System Handedness (Left/Right)
|
|
154
173
|
// ==============================================================================
|
|
155
|
-
namespace {
|
|
156
|
-
// Global handedness setting: true = left-handed (default), false = right-handed
|
|
157
|
-
bool g_use_left_handed_system = true;
|
|
158
|
-
|
|
174
|
+
namespace {
|
|
175
|
+
// Global handedness setting: true = left-handed (default), false = right-handed
|
|
176
|
+
bool g_use_left_handed_system = true;
|
|
177
|
+
// Cross product sign to avoid per-call branching
|
|
178
|
+
real g_cross_sign = -1.0;
|
|
179
|
+
}
|
|
159
180
|
|
|
160
181
|
// Set coordinate system handedness
|
|
161
|
-
inline void set_coordinate_handedness(bool use_left_handed) {
|
|
162
|
-
g_use_left_handed_system = use_left_handed;
|
|
163
|
-
|
|
182
|
+
inline void set_coordinate_handedness(bool use_left_handed) {
|
|
183
|
+
g_use_left_handed_system = use_left_handed;
|
|
184
|
+
g_cross_sign = use_left_handed ? -1.0 : 1.0;
|
|
185
|
+
}
|
|
164
186
|
|
|
165
187
|
// Get current coordinate system handedness
|
|
166
188
|
inline bool get_coordinate_handedness() {
|
|
@@ -231,7 +253,7 @@ inline void hash_combine(std::size_t& seed, const real& v) {
|
|
|
231
253
|
// 2D
|
|
232
254
|
// **********************************************************************
|
|
233
255
|
#pragma once
|
|
234
|
-
struct vector2
|
|
256
|
+
struct PMSYS_ALIGN16 vector2
|
|
235
257
|
{
|
|
236
258
|
static const vector2 ZERO;
|
|
237
259
|
static const vector2 ONE;
|
|
@@ -523,7 +545,7 @@ inline real vector2::sEPSILON = EPSILON;
|
|
|
523
545
|
// **********************************************************************
|
|
524
546
|
// 3D
|
|
525
547
|
// **********************************************************************
|
|
526
|
-
struct vector3
|
|
548
|
+
struct PMSYS_ALIGN32 vector3
|
|
527
549
|
{
|
|
528
550
|
static const vector3 ZERO;
|
|
529
551
|
static const vector3 ONE;
|
|
@@ -894,25 +916,15 @@ struct vector3
|
|
|
894
916
|
const vector3& v = (*this);
|
|
895
917
|
return v - n * v.dot(n);
|
|
896
918
|
}
|
|
897
|
-
DEVICE_CALLABLE vector3 cross(const vector3& v) const
|
|
898
|
-
{
|
|
899
|
-
//
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
return n;
|
|
907
|
-
} else {
|
|
908
|
-
// Right-handed system
|
|
909
|
-
vector3 n;
|
|
910
|
-
n.x = (y * v.z - z * v.y);
|
|
911
|
-
n.y = (z * v.x - x * v.z);
|
|
912
|
-
n.z = (x * v.y - y * v.x);
|
|
913
|
-
return n;
|
|
914
|
-
}
|
|
915
|
-
}
|
|
919
|
+
DEVICE_CALLABLE vector3 cross(const vector3& v) const
|
|
920
|
+
{
|
|
921
|
+
// Branchless handedness selection via precomputed sign
|
|
922
|
+
vector3 n;
|
|
923
|
+
n.x = (y * v.z - z * v.y) * g_cross_sign;
|
|
924
|
+
n.y = (z * v.x - x * v.z) * g_cross_sign;
|
|
925
|
+
n.z = (x * v.y - y * v.x) * g_cross_sign;
|
|
926
|
+
return n;
|
|
927
|
+
}
|
|
916
928
|
DEVICE_CALLABLE vector3 cross_left(const vector3& v) const
|
|
917
929
|
{
|
|
918
930
|
vector3 n;
|
|
@@ -1025,7 +1037,7 @@ inline real vector3::sEPSILON = EPSILON;
|
|
|
1025
1037
|
// **********************************************************************
|
|
1026
1038
|
// 4D vector
|
|
1027
1039
|
// **********************************************************************
|
|
1028
|
-
struct vector4
|
|
1040
|
+
struct PMSYS_ALIGN32 vector4
|
|
1029
1041
|
{
|
|
1030
1042
|
static const vector4 ZERO;
|
|
1031
1043
|
static const vector4 UX;
|
|
@@ -1606,7 +1618,7 @@ inline const vectorn vectorn::CENTER = vectorn(0.5);
|
|
|
1606
1618
|
and define the unit element ONE.
|
|
1607
1619
|
|
|
1608
1620
|
**************************************************************************/
|
|
1609
|
-
struct
|
|
1621
|
+
struct PMSYS_ALIGN32 quaternion
|
|
1610
1622
|
{
|
|
1611
1623
|
static const quaternion ONE;
|
|
1612
1624
|
static const quaternion UX;
|
|
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
|
|
|
8
8
|
|
|
9
9
|
[project]
|
|
10
10
|
name = "coordinate_system"
|
|
11
|
-
version = "7.
|
|
11
|
+
version = "7.1.1"
|
|
12
12
|
description = "High-performance 3D coordinate system library with unified differential geometry, quantum frame algebra, and Christmas Equation (CFUT)"
|
|
13
13
|
readme = "README.md"
|
|
14
14
|
requires-python = ">=3.7"
|
|
@@ -3,7 +3,7 @@ setup.py - Cross-platform setup for coordinate_system package
|
|
|
3
3
|
|
|
4
4
|
**Authors:** Pan Guojun
|
|
5
5
|
**DOI:** https://doi.org/10.5281/zenodo.14435613
|
|
6
|
-
Version: 7.
|
|
6
|
+
Version: 7.1.1
|
|
7
7
|
License: MIT
|
|
8
8
|
"""
|
|
9
9
|
|
|
@@ -33,34 +33,42 @@ with open('README.md', 'r', encoding='utf-8') as f:
|
|
|
33
33
|
extra_compile_args = []
|
|
34
34
|
extra_link_args = []
|
|
35
35
|
|
|
36
|
-
if system == 'Windows':
|
|
37
|
-
# MSVC compiler flags
|
|
38
|
-
extra_compile_args = [
|
|
39
|
-
'/std:c++17', # C++17 standard
|
|
40
|
-
'/O2', # Optimization
|
|
41
|
-
'/
|
|
42
|
-
'/
|
|
43
|
-
'/
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
'-
|
|
51
|
-
'-
|
|
52
|
-
'-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
'-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
'-
|
|
63
|
-
|
|
36
|
+
if system == 'Windows':
|
|
37
|
+
# MSVC compiler flags
|
|
38
|
+
extra_compile_args = [
|
|
39
|
+
'/std:c++17', # C++17 standard
|
|
40
|
+
'/O2', # Optimization
|
|
41
|
+
'/arch:AVX2', # Enable AVX2
|
|
42
|
+
'/fp:fast', # Fast floating-point (unsafe math optimizations)
|
|
43
|
+
'/W3', # Warning level
|
|
44
|
+
'/EHsc', # Exception handling
|
|
45
|
+
'/MD', # Runtime library
|
|
46
|
+
]
|
|
47
|
+
elif system == 'Linux':
|
|
48
|
+
# GCC compiler flags
|
|
49
|
+
extra_compile_args = [
|
|
50
|
+
'-std=c++17', # C++17 standard
|
|
51
|
+
'-O3', # Optimization
|
|
52
|
+
'-march=native', # Enable local CPU features (SSE/AVX)
|
|
53
|
+
'-ffast-math', # Fast math (unsafe)
|
|
54
|
+
'-funroll-loops', # Loop unrolling
|
|
55
|
+
'-Wall', # Warnings
|
|
56
|
+
'-fPIC', # Position independent code
|
|
57
|
+
'-fvisibility=hidden', # Hide symbols by default
|
|
58
|
+
]
|
|
59
|
+
elif system == 'Darwin': # macOS
|
|
60
|
+
# Clang compiler flags
|
|
61
|
+
extra_compile_args = [
|
|
62
|
+
'-std=c++17', # C++17 standard
|
|
63
|
+
'-O3', # Optimization
|
|
64
|
+
'-march=native', # Enable local CPU features
|
|
65
|
+
'-ffast-math', # Fast math (unsafe)
|
|
66
|
+
'-funroll-loops', # Loop unrolling
|
|
67
|
+
'-Wall', # Warnings
|
|
68
|
+
'-fPIC', # Position independent code
|
|
69
|
+
'-fvisibility=hidden', # Hide symbols by default
|
|
70
|
+
'-mmacosx-version-min=10.14', # macOS 10.14+
|
|
71
|
+
]
|
|
64
72
|
|
|
65
73
|
# Define the extension module
|
|
66
74
|
ext_modules = [
|
|
@@ -79,7 +87,7 @@ ext_modules = [
|
|
|
79
87
|
|
|
80
88
|
setup(
|
|
81
89
|
name='coordinate_system',
|
|
82
|
-
version='7.
|
|
90
|
+
version='7.1.1',
|
|
83
91
|
packages=find_packages(),
|
|
84
92
|
ext_modules=ext_modules, # Add extension modules
|
|
85
93
|
|
|
File without changes
|
|
File without changes
|
{coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system/curve_interpolation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{coordinate_system-7.0.2 → coordinate_system-7.1.1}/coordinate_system.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|