coordinate-system 5.2.1__cp313-cp313-win_amd64.whl → 6.0.0__cp313-cp313-win_amd64.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.
- coordinate_system/__init__.py +112 -144
- coordinate_system/coordinate_system.cp313-win_amd64.pyd +0 -0
- coordinate_system/differential_geometry.py +618 -298
- coordinate_system/fourier_spectral.py +110 -361
- coordinate_system/qframes.py +792 -0
- coordinate_system/test_quantum_upgrade.py +383 -0
- coordinate_system/visualization.py +695 -292
- coordinate_system-6.0.0.dist-info/METADATA +783 -0
- coordinate_system-6.0.0.dist-info/RECORD +14 -0
- coordinate_system/__pycache__/__init__.cpython-313.pyc +0 -0
- coordinate_system/__pycache__/curvature.cpython-313.pyc +0 -0
- coordinate_system/__pycache__/curve_interpolation.cpython-313.pyc +0 -0
- coordinate_system/__pycache__/differential_geometry.cpython-313.pyc +0 -0
- coordinate_system/__pycache__/fourier_spectral.cpython-313.pyc +0 -0
- coordinate_system/__pycache__/visualization.cpython-313.pyc +0 -0
- coordinate_system/fourier_frames.py +0 -530
- coordinate_system-5.2.1.dist-info/METADATA +0 -807
- coordinate_system-5.2.1.dist-info/RECORD +0 -19
- {coordinate_system-5.2.1.dist-info → coordinate_system-6.0.0.dist-info}/LICENSE +0 -0
- {coordinate_system-5.2.1.dist-info → coordinate_system-6.0.0.dist-info}/WHEEL +0 -0
- {coordinate_system-5.2.1.dist-info → coordinate_system-6.0.0.dist-info}/top_level.txt +0 -0
|
@@ -1,33 +1,94 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Differential Geometry Module
|
|
3
|
-
|
|
2
|
+
Differential Geometry Module
|
|
3
|
+
============================
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Comprehensive discrete differential geometry computations on surfaces,
|
|
6
|
+
combining classical methods and intrinsic gradient operator framework.
|
|
7
7
|
|
|
8
|
-
Key
|
|
9
|
-
|
|
8
|
+
Key Features:
|
|
9
|
+
- Surface parametrization (Sphere, Torus, custom surfaces)
|
|
10
|
+
- Metric tensor computation
|
|
11
|
+
- Intrinsic gradient operator for curvature calculation
|
|
12
|
+
- Classical curvature methods with high-order finite differences
|
|
13
|
+
- Gaussian, mean, principal, and Riemann curvatures
|
|
10
14
|
|
|
11
|
-
Author:
|
|
12
|
-
Date: 2025-
|
|
15
|
+
Author: Coordinate System Package
|
|
16
|
+
Date: 2025-12-03
|
|
13
17
|
"""
|
|
14
18
|
|
|
15
19
|
import math
|
|
16
20
|
import numpy as np
|
|
17
|
-
from typing import Tuple, Optional, Callable, Union
|
|
21
|
+
from typing import Tuple, Optional, Callable, Union, Dict, List
|
|
18
22
|
from .coordinate_system import coord3, vec3
|
|
19
23
|
|
|
20
24
|
|
|
21
|
-
#
|
|
25
|
+
# ============================================================
|
|
26
|
+
# High-Order Finite Difference Operators
|
|
27
|
+
# ============================================================
|
|
28
|
+
|
|
29
|
+
def derivative_5pt(f: Callable[[float], np.ndarray], x: float, h: float) -> np.ndarray:
|
|
30
|
+
"""
|
|
31
|
+
5-point finite difference formula for first derivative.
|
|
32
|
+
|
|
33
|
+
Accuracy: O(h^4)
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
f: Function to differentiate
|
|
37
|
+
x: Point at which to compute derivative
|
|
38
|
+
h: Step size
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
First derivative approximation
|
|
42
|
+
"""
|
|
43
|
+
return (-f(x + 2*h) + 8*f(x + h) - 8*f(x - h) + f(x - 2*h)) / (12*h)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def derivative_2nd_5pt(f: Callable[[float], np.ndarray], x: float, h: float) -> np.ndarray:
|
|
47
|
+
"""
|
|
48
|
+
5-point finite difference formula for second derivative.
|
|
49
|
+
|
|
50
|
+
Accuracy: O(h^4)
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
f: Function to differentiate
|
|
54
|
+
x: Point at which to compute derivative
|
|
55
|
+
h: Step size
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Second derivative approximation
|
|
59
|
+
"""
|
|
60
|
+
return (-f(x + 2*h) + 16*f(x + h) - 30*f(x) + 16*f(x - h) - f(x - 2*h)) / (12*h*h)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def richardson_extrapolation(f_h: float, f_2h: float, order: int = 4) -> float:
|
|
64
|
+
"""
|
|
65
|
+
Richardson extrapolation for accelerating convergence.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
f_h: Value computed with step size h
|
|
69
|
+
f_2h: Value computed with step size 2h
|
|
70
|
+
order: Order of the method
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Extrapolated value with improved accuracy
|
|
74
|
+
"""
|
|
75
|
+
return (2**order * f_h - f_2h) / (2**order - 1)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# ============================================================
|
|
79
|
+
# Surface Base Class and Common Surfaces
|
|
80
|
+
# ============================================================
|
|
22
81
|
|
|
23
82
|
class Surface:
|
|
24
83
|
"""
|
|
25
|
-
Base class for parametric surfaces r(u, v)
|
|
84
|
+
Base class for parametric surfaces r(u, v).
|
|
85
|
+
|
|
86
|
+
Subclasses must implement the position(u, v) method.
|
|
26
87
|
"""
|
|
27
88
|
|
|
28
89
|
def __init__(self, h: float = 1e-6):
|
|
29
90
|
"""
|
|
30
|
-
Initialize surface
|
|
91
|
+
Initialize surface.
|
|
31
92
|
|
|
32
93
|
Args:
|
|
33
94
|
h: Step size for numerical differentiation
|
|
@@ -35,23 +96,27 @@ class Surface:
|
|
|
35
96
|
self.h = h
|
|
36
97
|
|
|
37
98
|
def position(self, u: float, v: float) -> vec3:
|
|
38
|
-
"""
|
|
99
|
+
"""
|
|
100
|
+
Compute position on surface at parameters (u, v).
|
|
101
|
+
|
|
102
|
+
Must be implemented by subclasses.
|
|
103
|
+
"""
|
|
39
104
|
raise NotImplementedError("Subclass must implement position(u, v)")
|
|
40
105
|
|
|
41
106
|
def tangent_u(self, u: float, v: float) -> vec3:
|
|
42
|
-
"""Compute tangent vector in u direction"""
|
|
107
|
+
"""Compute tangent vector in u direction using central difference."""
|
|
43
108
|
r_plus = self.position(u + self.h, v)
|
|
44
109
|
r_minus = self.position(u - self.h, v)
|
|
45
110
|
return (r_plus - r_minus) * (1.0 / (2.0 * self.h))
|
|
46
111
|
|
|
47
112
|
def tangent_v(self, u: float, v: float) -> vec3:
|
|
48
|
-
"""Compute tangent vector in v direction"""
|
|
113
|
+
"""Compute tangent vector in v direction using central difference."""
|
|
49
114
|
r_plus = self.position(u, v + self.h)
|
|
50
115
|
r_minus = self.position(u, v - self.h)
|
|
51
116
|
return (r_plus - r_minus) * (1.0 / (2.0 * self.h))
|
|
52
117
|
|
|
53
118
|
def normal(self, u: float, v: float) -> vec3:
|
|
54
|
-
"""Compute unit normal vector"""
|
|
119
|
+
"""Compute unit normal vector."""
|
|
55
120
|
r_u = self.tangent_u(u, v)
|
|
56
121
|
r_v = self.tangent_v(u, v)
|
|
57
122
|
n = r_u.cross(r_v)
|
|
@@ -62,13 +127,16 @@ class Surface:
|
|
|
62
127
|
return vec3(0.0, 0.0, 1.0)
|
|
63
128
|
|
|
64
129
|
|
|
65
|
-
# ========== Common Surface Types ==========
|
|
66
|
-
|
|
67
130
|
class Sphere(Surface):
|
|
68
131
|
"""
|
|
69
|
-
Sphere surface
|
|
70
|
-
|
|
71
|
-
|
|
132
|
+
Sphere surface.
|
|
133
|
+
|
|
134
|
+
Parametrization: r(theta, phi) = R(sin(theta)cos(phi), sin(theta)sin(phi), cos(theta))
|
|
135
|
+
where theta in [0, pi] is polar angle, phi in [0, 2*pi] is azimuthal angle.
|
|
136
|
+
|
|
137
|
+
Theoretical curvatures:
|
|
138
|
+
- Gaussian curvature: K = 1/R^2
|
|
139
|
+
- Mean curvature: H = 1/R
|
|
72
140
|
"""
|
|
73
141
|
|
|
74
142
|
def __init__(self, radius: float = 1.0, h: float = 1e-6):
|
|
@@ -76,43 +144,75 @@ class Sphere(Surface):
|
|
|
76
144
|
self.R = radius
|
|
77
145
|
|
|
78
146
|
def position(self, theta: float, phi: float) -> vec3:
|
|
79
|
-
"""Position on sphere"""
|
|
147
|
+
"""Position on sphere."""
|
|
80
148
|
x = self.R * math.sin(theta) * math.cos(phi)
|
|
81
149
|
y = self.R * math.sin(theta) * math.sin(phi)
|
|
82
150
|
z = self.R * math.cos(theta)
|
|
83
151
|
return vec3(x, y, z)
|
|
84
152
|
|
|
153
|
+
@property
|
|
154
|
+
def theoretical_gaussian_curvature(self) -> float:
|
|
155
|
+
"""Theoretical Gaussian curvature K = 1/R^2."""
|
|
156
|
+
return 1.0 / (self.R * self.R)
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def theoretical_mean_curvature(self) -> float:
|
|
160
|
+
"""Theoretical mean curvature H = 1/R."""
|
|
161
|
+
return 1.0 / self.R
|
|
162
|
+
|
|
85
163
|
|
|
86
164
|
class Torus(Surface):
|
|
87
165
|
"""
|
|
88
|
-
Torus surface
|
|
89
|
-
|
|
166
|
+
Torus surface.
|
|
167
|
+
|
|
168
|
+
Parametrization: r(u, v) = ((R + r*cos(u))*cos(v), (R + r*cos(u))*sin(v), r*sin(u))
|
|
169
|
+
where R is major radius, r is minor radius.
|
|
170
|
+
|
|
171
|
+
Theoretical curvatures:
|
|
172
|
+
- Gaussian curvature: K = cos(u) / (r * (R + r*cos(u)))
|
|
173
|
+
- Mean curvature: H = (R + 2*r*cos(u)) / (2*r*(R + r*cos(u)))
|
|
90
174
|
"""
|
|
91
175
|
|
|
92
176
|
def __init__(self, major_radius: float = 3.0, minor_radius: float = 1.0, h: float = 1e-6):
|
|
93
177
|
super().__init__(h)
|
|
94
|
-
self.R = major_radius
|
|
95
|
-
self.r = minor_radius
|
|
178
|
+
self.R = major_radius
|
|
179
|
+
self.r = minor_radius
|
|
96
180
|
|
|
97
181
|
def position(self, u: float, v: float) -> vec3:
|
|
98
|
-
"""Position on torus"""
|
|
182
|
+
"""Position on torus."""
|
|
99
183
|
x = (self.R + self.r * math.cos(u)) * math.cos(v)
|
|
100
184
|
y = (self.R + self.r * math.cos(u)) * math.sin(v)
|
|
101
185
|
z = self.r * math.sin(u)
|
|
102
186
|
return vec3(x, y, z)
|
|
103
187
|
|
|
188
|
+
def theoretical_gaussian_curvature(self, u: float) -> float:
|
|
189
|
+
"""Theoretical Gaussian curvature at parameter u."""
|
|
190
|
+
return math.cos(u) / (self.r * (self.R + self.r * math.cos(u)))
|
|
191
|
+
|
|
192
|
+
def theoretical_mean_curvature(self, u: float) -> float:
|
|
193
|
+
"""Theoretical mean curvature at parameter u."""
|
|
194
|
+
return (self.R + 2 * self.r * math.cos(u)) / (2 * self.r * (self.R + self.r * math.cos(u)))
|
|
104
195
|
|
|
105
|
-
|
|
196
|
+
|
|
197
|
+
# ============================================================
|
|
198
|
+
# Metric Tensor
|
|
199
|
+
# ============================================================
|
|
106
200
|
|
|
107
201
|
class MetricTensor:
|
|
108
202
|
"""
|
|
109
|
-
First fundamental form (metric tensor) of a surface
|
|
110
|
-
|
|
203
|
+
First fundamental form (metric tensor) of a surface.
|
|
204
|
+
|
|
205
|
+
g_ij = <dr/du^i, dr/du^j>
|
|
206
|
+
|
|
207
|
+
Components:
|
|
208
|
+
- E = g_11 = <r_u, r_u>
|
|
209
|
+
- F = g_12 = <r_u, r_v>
|
|
210
|
+
- G = g_22 = <r_v, r_v>
|
|
111
211
|
"""
|
|
112
212
|
|
|
113
213
|
def __init__(self, E: float, F: float, G: float):
|
|
114
214
|
"""
|
|
115
|
-
Initialize metric tensor
|
|
215
|
+
Initialize metric tensor.
|
|
116
216
|
|
|
117
217
|
Args:
|
|
118
218
|
E: g_11 = <r_u, r_u>
|
|
@@ -126,7 +226,7 @@ class MetricTensor:
|
|
|
126
226
|
|
|
127
227
|
@classmethod
|
|
128
228
|
def from_surface(cls, surface: Surface, u: float, v: float) -> 'MetricTensor':
|
|
129
|
-
"""Create metric tensor from surface at point (u, v)"""
|
|
229
|
+
"""Create metric tensor from surface at point (u, v)."""
|
|
130
230
|
r_u = surface.tangent_u(u, v)
|
|
131
231
|
r_v = surface.tangent_v(u, v)
|
|
132
232
|
E = r_u.dot(r_u)
|
|
@@ -135,171 +235,193 @@ class MetricTensor:
|
|
|
135
235
|
return cls(E, F, G)
|
|
136
236
|
|
|
137
237
|
def determinant(self) -> float:
|
|
138
|
-
"""Get determinant of metric tensor"""
|
|
238
|
+
"""Get determinant of metric tensor."""
|
|
139
239
|
return self.det
|
|
140
240
|
|
|
241
|
+
def inverse(self) -> np.ndarray:
|
|
242
|
+
"""Get inverse metric tensor as numpy array."""
|
|
243
|
+
if abs(self.det) < 1e-14:
|
|
244
|
+
return np.eye(2)
|
|
245
|
+
return np.array([[self.G, -self.F], [-self.F, self.E]]) / self.det
|
|
246
|
+
|
|
247
|
+
def as_matrix(self) -> np.ndarray:
|
|
248
|
+
"""Get metric tensor as numpy array."""
|
|
249
|
+
return np.array([[self.E, self.F], [self.F, self.G]])
|
|
250
|
+
|
|
141
251
|
def __repr__(self) -> str:
|
|
142
252
|
return f"MetricTensor(E={self.E:.6f}, F={self.F:.6f}, G={self.G:.6f}, det={self.det:.6f})"
|
|
143
253
|
|
|
144
254
|
|
|
145
|
-
#
|
|
255
|
+
# ============================================================
|
|
256
|
+
# Gradient Result
|
|
257
|
+
# ============================================================
|
|
258
|
+
|
|
259
|
+
class GradientResult:
|
|
260
|
+
"""
|
|
261
|
+
Gradient result containing normal vector derivative.
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
def __init__(self, dn: vec3, direction: str):
|
|
265
|
+
"""
|
|
266
|
+
Initialize gradient result.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
dn: Normal vector derivative
|
|
270
|
+
direction: Parameter direction ('u' or 'v')
|
|
271
|
+
"""
|
|
272
|
+
self.dn = dn
|
|
273
|
+
self.direction = direction
|
|
274
|
+
|
|
275
|
+
def __repr__(self) -> str:
|
|
276
|
+
return f"GradientResult({self.direction}: [{self.dn.x:.6f}, {self.dn.y:.6f}, {self.dn.z:.6f}])"
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
# ============================================================
|
|
280
|
+
# Intrinsic Gradient Operator
|
|
281
|
+
# ============================================================
|
|
146
282
|
|
|
147
283
|
class IntrinsicGradientOperator:
|
|
148
284
|
"""
|
|
149
|
-
|
|
285
|
+
Intrinsic gradient operator implementation.
|
|
286
|
+
|
|
287
|
+
Key formula:
|
|
288
|
+
G_mu = (c(u+h) - c(u-h)) / (2h) / c(u)
|
|
289
|
+
Then extract normal derivative using .VZ()
|
|
150
290
|
"""
|
|
151
|
-
|
|
291
|
+
|
|
152
292
|
def __init__(self, surface: Surface, step_size: float = 1e-3):
|
|
153
293
|
self.surface = surface
|
|
154
294
|
self.h = step_size
|
|
155
295
|
|
|
156
296
|
def calc_intrinsic_frame(self, u: float, v: float) -> coord3:
|
|
157
297
|
"""
|
|
158
|
-
Calculate intrinsic frame
|
|
298
|
+
Calculate intrinsic frame at point (u, v).
|
|
299
|
+
|
|
300
|
+
For Sphere and Torus, uses analytical expressions.
|
|
301
|
+
For other surfaces, uses numerical derivatives.
|
|
159
302
|
"""
|
|
160
|
-
# 对于球面,使用解析表达式(与测试代码一致)
|
|
161
303
|
if isinstance(self.surface, Sphere):
|
|
162
304
|
R = self.surface.R
|
|
163
305
|
theta, phi = u, v
|
|
164
|
-
|
|
165
|
-
# 位置向量
|
|
306
|
+
|
|
166
307
|
pos = self.surface.position(theta, phi)
|
|
167
|
-
|
|
168
|
-
#
|
|
308
|
+
|
|
309
|
+
# Analytical tangent vectors
|
|
169
310
|
r_theta = vec3(
|
|
170
311
|
R * math.cos(theta) * math.cos(phi),
|
|
171
312
|
R * math.cos(theta) * math.sin(phi),
|
|
172
313
|
-R * math.sin(theta)
|
|
173
314
|
)
|
|
174
|
-
|
|
175
315
|
r_phi = vec3(
|
|
176
316
|
-R * math.sin(theta) * math.sin(phi),
|
|
177
317
|
R * math.sin(theta) * math.cos(phi),
|
|
178
318
|
0
|
|
179
319
|
)
|
|
180
|
-
|
|
181
|
-
# 单位法向量
|
|
320
|
+
|
|
182
321
|
n = r_theta.cross(r_phi).normalized()
|
|
183
|
-
|
|
184
|
-
# 单位切向量
|
|
185
322
|
e1 = r_theta.normalized()
|
|
186
323
|
e2 = r_phi.normalized()
|
|
187
|
-
|
|
324
|
+
|
|
188
325
|
elif isinstance(self.surface, Torus):
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
r = self.surface.r # 副半径
|
|
326
|
+
R = self.surface.R
|
|
327
|
+
r = self.surface.r
|
|
192
328
|
u_param, v_param = u, v
|
|
193
|
-
|
|
194
|
-
# 位置向量
|
|
329
|
+
|
|
195
330
|
pos = self.surface.position(u_param, v_param)
|
|
196
|
-
|
|
197
|
-
#
|
|
331
|
+
|
|
332
|
+
# Analytical tangent vectors
|
|
198
333
|
r_u = vec3(
|
|
199
334
|
-r * math.sin(u_param) * math.cos(v_param),
|
|
200
335
|
-r * math.sin(u_param) * math.sin(v_param),
|
|
201
336
|
r * math.cos(u_param)
|
|
202
337
|
)
|
|
203
|
-
|
|
204
338
|
r_v = vec3(
|
|
205
339
|
-(R + r * math.cos(u_param)) * math.sin(v_param),
|
|
206
340
|
(R + r * math.cos(u_param)) * math.cos(v_param),
|
|
207
341
|
0
|
|
208
342
|
)
|
|
209
|
-
|
|
210
|
-
# 单位法向量
|
|
343
|
+
|
|
211
344
|
n = r_u.cross(r_v).normalized()
|
|
212
|
-
|
|
213
|
-
# 单位切向量
|
|
214
345
|
e1 = r_u.normalized()
|
|
215
346
|
e2 = r_v.normalized()
|
|
216
|
-
|
|
347
|
+
|
|
217
348
|
else:
|
|
218
|
-
#
|
|
349
|
+
# Numerical method for general surfaces
|
|
219
350
|
pos = self.surface.position(u, v)
|
|
220
351
|
r_u = self.surface.tangent_u(u, v)
|
|
221
352
|
r_v = self.surface.tangent_v(u, v)
|
|
222
|
-
|
|
223
|
-
# 单位法向量
|
|
353
|
+
|
|
224
354
|
n = r_u.cross(r_v).normalized()
|
|
225
|
-
|
|
226
|
-
# 单位切向量
|
|
227
355
|
e1 = r_u.normalized()
|
|
228
356
|
e2 = r_v.normalized()
|
|
229
|
-
|
|
230
|
-
#
|
|
357
|
+
|
|
358
|
+
# Create intrinsic frame
|
|
231
359
|
frame = coord3()
|
|
232
360
|
frame.o = pos
|
|
233
361
|
frame.ux = e1
|
|
234
362
|
frame.uy = e2
|
|
235
363
|
frame.uz = n
|
|
236
|
-
|
|
364
|
+
|
|
237
365
|
return frame
|
|
238
366
|
|
|
239
|
-
def compute_both(self, u: float, v: float) -> Tuple[
|
|
367
|
+
def compute_both(self, u: float, v: float) -> Tuple[GradientResult, GradientResult, coord3]:
|
|
240
368
|
"""
|
|
241
|
-
Compute gradients
|
|
369
|
+
Compute gradients in both u and v directions using central differences.
|
|
370
|
+
|
|
371
|
+
Returns:
|
|
372
|
+
Tuple of (G_u, G_v, center_frame)
|
|
242
373
|
"""
|
|
243
|
-
# 计算中心点和偏移点的标架
|
|
244
374
|
c_center = self.calc_intrinsic_frame(u, v)
|
|
245
|
-
|
|
246
|
-
# u
|
|
375
|
+
|
|
376
|
+
# u direction: central difference
|
|
247
377
|
c_u_plus = self.calc_intrinsic_frame(u + self.h, v)
|
|
248
378
|
c_u_minus = self.calc_intrinsic_frame(u - self.h, v)
|
|
249
379
|
dn_du = ((c_u_plus - c_u_minus) / (2 * self.h)).VZ()
|
|
250
|
-
|
|
251
|
-
# v
|
|
380
|
+
|
|
381
|
+
# v direction: central difference
|
|
252
382
|
c_v_plus = self.calc_intrinsic_frame(u, v + self.h)
|
|
253
383
|
c_v_minus = self.calc_intrinsic_frame(u, v - self.h)
|
|
254
384
|
dn_dv = ((c_v_plus - c_v_minus) / (2 * self.h)).VZ()
|
|
255
|
-
|
|
256
|
-
# 创建梯度结果
|
|
385
|
+
|
|
257
386
|
G_u = GradientResult(dn_du, "u")
|
|
258
387
|
G_v = GradientResult(dn_dv, "v")
|
|
259
|
-
|
|
388
|
+
|
|
260
389
|
return G_u, G_v, c_center
|
|
261
390
|
|
|
391
|
+
def compute_u(self, u: float, v: float) -> GradientResult:
|
|
392
|
+
"""Compute gradient in u direction only."""
|
|
393
|
+
c_plus = self.calc_intrinsic_frame(u + self.h, v)
|
|
394
|
+
c_minus = self.calc_intrinsic_frame(u - self.h, v)
|
|
395
|
+
dn = ((c_plus - c_minus) / (2 * self.h)).VZ()
|
|
396
|
+
return GradientResult(dn, "u")
|
|
262
397
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
398
|
+
def compute_v(self, u: float, v: float) -> GradientResult:
|
|
399
|
+
"""Compute gradient in v direction only."""
|
|
400
|
+
c_plus = self.calc_intrinsic_frame(u, v + self.h)
|
|
401
|
+
c_minus = self.calc_intrinsic_frame(u, v - self.h)
|
|
402
|
+
dn = ((c_plus - c_minus) / (2 * self.h)).VZ()
|
|
403
|
+
return GradientResult(dn, "v")
|
|
267
404
|
|
|
268
|
-
def __init__(self, dn: vec3, direction: str):
|
|
269
|
-
"""
|
|
270
|
-
Initialize gradient result
|
|
271
405
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
"""
|
|
276
|
-
self.dn = dn
|
|
277
|
-
self.direction = direction
|
|
278
|
-
|
|
279
|
-
def __repr__(self) -> str:
|
|
280
|
-
return f"GradientResult({self.direction}: [{self.dn.x:.6f}, {self.dn.y:.6f}, {self.dn.z:.6f}])"
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
# ========== Curvature Calculator ==========
|
|
406
|
+
# ============================================================
|
|
407
|
+
# Intrinsic Gradient Curvature Calculator
|
|
408
|
+
# ============================================================
|
|
284
409
|
|
|
285
410
|
class IntrinsicGradientCurvatureCalculator:
|
|
286
411
|
"""
|
|
287
|
-
Curvature calculator using
|
|
412
|
+
Curvature calculator using intrinsic gradient method.
|
|
413
|
+
|
|
414
|
+
Computes curvatures from the shape operator derived
|
|
415
|
+
from the intrinsic gradient of the normal field.
|
|
288
416
|
"""
|
|
289
|
-
|
|
417
|
+
|
|
290
418
|
def __init__(self, surface: Surface, step_size: float = 1e-3):
|
|
291
419
|
self.surface = surface
|
|
292
420
|
self.h = step_size
|
|
293
421
|
self.grad_op = IntrinsicGradientOperator(surface, step_size)
|
|
294
422
|
|
|
295
|
-
def
|
|
296
|
-
"""
|
|
297
|
-
Compute Gaussian curvature - CORRECTED implementation
|
|
298
|
-
"""
|
|
299
|
-
# 计算梯度算子
|
|
300
|
-
G_u, G_v, _ = self.grad_op.compute_both(u, v)
|
|
301
|
-
|
|
302
|
-
# 计算切向量(使用解析表达式)
|
|
423
|
+
def _get_tangent_vectors(self, u: float, v: float) -> Tuple[vec3, vec3]:
|
|
424
|
+
"""Get tangent vectors (analytical for known surfaces, numerical otherwise)."""
|
|
303
425
|
if isinstance(self.surface, Sphere):
|
|
304
426
|
R = self.surface.R
|
|
305
427
|
theta, phi = u, v
|
|
@@ -314,42 +436,48 @@ class IntrinsicGradientCurvatureCalculator:
|
|
|
314
436
|
0
|
|
315
437
|
)
|
|
316
438
|
elif isinstance(self.surface, Torus):
|
|
317
|
-
R = self.surface.R
|
|
318
|
-
r = self.surface.r
|
|
319
|
-
u_param, v_param = u, v
|
|
439
|
+
R = self.surface.R
|
|
440
|
+
r = self.surface.r
|
|
320
441
|
r_u = vec3(
|
|
321
|
-
-r * math.sin(
|
|
322
|
-
-r * math.sin(
|
|
323
|
-
r * math.cos(
|
|
442
|
+
-r * math.sin(u) * math.cos(v),
|
|
443
|
+
-r * math.sin(u) * math.sin(v),
|
|
444
|
+
r * math.cos(u)
|
|
324
445
|
)
|
|
325
446
|
r_v = vec3(
|
|
326
|
-
-(R + r * math.cos(
|
|
327
|
-
(R + r * math.cos(
|
|
447
|
+
-(R + r * math.cos(u)) * math.sin(v),
|
|
448
|
+
(R + r * math.cos(u)) * math.cos(v),
|
|
328
449
|
0
|
|
329
450
|
)
|
|
330
451
|
else:
|
|
331
|
-
# 对于其他曲面,使用数值导数
|
|
332
452
|
r_u = self.surface.tangent_u(u, v)
|
|
333
453
|
r_v = self.surface.tangent_v(u, v)
|
|
334
454
|
|
|
335
|
-
|
|
455
|
+
return r_u, r_v
|
|
456
|
+
|
|
457
|
+
def compute_gaussian_curvature(self, u: float, v: float) -> float:
|
|
458
|
+
"""
|
|
459
|
+
Compute Gaussian curvature K = det(II) / det(I).
|
|
460
|
+
"""
|
|
461
|
+
G_u, G_v, _ = self.grad_op.compute_both(u, v)
|
|
462
|
+
r_u, r_v = self._get_tangent_vectors(u, v)
|
|
463
|
+
|
|
336
464
|
dn_du = G_u.dn
|
|
337
465
|
dn_dv = G_v.dn
|
|
338
466
|
|
|
339
|
-
#
|
|
467
|
+
# First fundamental form
|
|
340
468
|
E = r_u.dot(r_u)
|
|
341
469
|
F = r_u.dot(r_v)
|
|
342
470
|
G = r_v.dot(r_v)
|
|
343
471
|
metric_det = E * G - F * F
|
|
344
472
|
|
|
345
|
-
#
|
|
473
|
+
# Second fundamental form
|
|
346
474
|
L = -dn_du.dot(r_u)
|
|
347
475
|
M1 = -dn_du.dot(r_v)
|
|
348
476
|
M2 = -dn_dv.dot(r_u)
|
|
349
477
|
N = -dn_dv.dot(r_v)
|
|
350
478
|
M = (M1 + M2) / 2.0
|
|
351
479
|
|
|
352
|
-
#
|
|
480
|
+
# Gaussian curvature
|
|
353
481
|
if abs(metric_det) > 1e-14:
|
|
354
482
|
K = (L * N - M * M) / metric_det
|
|
355
483
|
else:
|
|
@@ -357,142 +485,12 @@ class IntrinsicGradientCurvatureCalculator:
|
|
|
357
485
|
|
|
358
486
|
return K
|
|
359
487
|
|
|
360
|
-
def compute_riemann_curvature(self, u: float, v: float, proportional_correction: bool = True) -> float:
|
|
361
|
-
"""
|
|
362
|
-
Compute Riemann curvature tensor component R^1_212
|
|
363
|
-
Based on the intrinsic gradient operator method with correct implementation
|
|
364
|
-
"""
|
|
365
|
-
delta = self.h
|
|
366
|
-
|
|
367
|
-
# 计算中心标架
|
|
368
|
-
c_center = self.grad_op.calc_intrinsic_frame(u, v)
|
|
369
|
-
|
|
370
|
-
# 计算u方向的内禀梯度算子(使用中心差分)
|
|
371
|
-
c_u_plus = self.grad_op.calc_intrinsic_frame(u + delta, v)
|
|
372
|
-
c_u_minus = self.grad_op.calc_intrinsic_frame(u - delta, v)
|
|
373
|
-
G_u = (c_u_plus - c_u_minus) / (2 * delta)
|
|
374
|
-
|
|
375
|
-
# 计算v方向的内禀梯度算子(使用中心差分)
|
|
376
|
-
c_v_plus = self.grad_op.calc_intrinsic_frame(u, v + delta)
|
|
377
|
-
c_v_minus = self.grad_op.calc_intrinsic_frame(u, v - delta)
|
|
378
|
-
G_v = (c_v_plus - c_v_minus) / (2 * delta)
|
|
379
|
-
|
|
380
|
-
# 计算李括号 [G_u, G_v] = G_u ∘ G_v - G_v ∘ G_u
|
|
381
|
-
# 这需要计算二阶混合偏导数
|
|
382
|
-
|
|
383
|
-
# 计算 ∂²c/∂u∂v 和 ∂²c/∂v∂u
|
|
384
|
-
c_uv_pp = self.grad_op.calc_intrinsic_frame(u + delta, v + delta)
|
|
385
|
-
c_uv_pm = self.grad_op.calc_intrinsic_frame(u + delta, v - delta)
|
|
386
|
-
c_uv_mp = self.grad_op.calc_intrinsic_frame(u - delta, v + delta)
|
|
387
|
-
c_uv_mm = self.grad_op.calc_intrinsic_frame(u - delta, v - delta)
|
|
388
|
-
|
|
389
|
-
# 二阶混合偏导数 ∂²c/∂u∂v
|
|
390
|
-
d2c_dudv = (c_uv_pp - c_uv_pm - c_uv_mp + c_uv_mm) / (4 * delta * delta)
|
|
391
|
-
|
|
392
|
-
# 对于对称的联络,∂²c/∂v∂u = ∂²c/∂u∂v,所以李括号简化为零
|
|
393
|
-
# 但是我们需要考虑标架的非对易性
|
|
394
|
-
|
|
395
|
-
# 更精确的方法:直接计算联络系数的变化
|
|
396
|
-
# 提取法向量的导数(这是关键)
|
|
397
|
-
dn_du = G_u.VZ() # 法向量在u方向的导数
|
|
398
|
-
dn_dv = G_v.VZ() # 法向量在v方向的导数
|
|
399
|
-
|
|
400
|
-
# 计算切向量(根据曲面类型)
|
|
401
|
-
if isinstance(self.surface, Sphere):
|
|
402
|
-
R = self.surface.R
|
|
403
|
-
theta, phi = u, v
|
|
404
|
-
r_u = vec3(
|
|
405
|
-
R * math.cos(theta) * math.cos(phi),
|
|
406
|
-
R * math.cos(theta) * math.sin(phi),
|
|
407
|
-
-R * math.sin(theta)
|
|
408
|
-
)
|
|
409
|
-
r_v = vec3(
|
|
410
|
-
-R * math.sin(theta) * math.sin(phi),
|
|
411
|
-
R * math.sin(theta) * math.cos(phi),
|
|
412
|
-
0
|
|
413
|
-
)
|
|
414
|
-
elif isinstance(self.surface, Torus):
|
|
415
|
-
R = self.surface.R
|
|
416
|
-
r = self.surface.r
|
|
417
|
-
u_param, v_param = u, v
|
|
418
|
-
r_u = vec3(
|
|
419
|
-
-r * math.sin(u_param) * math.cos(v_param),
|
|
420
|
-
-r * math.sin(u_param) * math.sin(v_param),
|
|
421
|
-
r * math.cos(u_param)
|
|
422
|
-
)
|
|
423
|
-
r_v = vec3(
|
|
424
|
-
-(R + r * math.cos(u_param)) * math.sin(v_param),
|
|
425
|
-
(R + r * math.cos(u_param)) * math.cos(v_param),
|
|
426
|
-
0
|
|
427
|
-
)
|
|
428
|
-
else:
|
|
429
|
-
r_u = self.surface.tangent_u(u, v)
|
|
430
|
-
r_v = self.surface.tangent_v(u, v)
|
|
431
|
-
|
|
432
|
-
# 计算第二基本形式系数
|
|
433
|
-
L = -dn_du.dot(r_u)
|
|
434
|
-
M = -dn_du.dot(r_v)
|
|
435
|
-
N = -dn_dv.dot(r_v)
|
|
436
|
-
|
|
437
|
-
# 计算度量张量
|
|
438
|
-
E = r_u.dot(r_u)
|
|
439
|
-
F = r_u.dot(r_v)
|
|
440
|
-
G_metric = r_v.dot(r_v)
|
|
441
|
-
det_g = E * G_metric - F * F
|
|
442
|
-
|
|
443
|
-
# 对于2D曲面,黎曼曲率张量只有一个独立分量
|
|
444
|
-
# R^1_212 与高斯曲率的关系为:K = R^1_212 / det(g)
|
|
445
|
-
# 所以 R^1_212 = K * det(g) = (LN - M²)
|
|
446
|
-
|
|
447
|
-
R_1212 = L * N - M * M
|
|
448
|
-
|
|
449
|
-
# 比例修正(对于某些参数化可能需要)
|
|
450
|
-
if proportional_correction and isinstance(self.surface, Sphere):
|
|
451
|
-
# 对于球面的某些参数化,可能需要额外的修正
|
|
452
|
-
# 但是基于第二基本形式的计算通常不需要
|
|
453
|
-
pass
|
|
454
|
-
|
|
455
|
-
return R_1212
|
|
456
|
-
|
|
457
488
|
def compute_mean_curvature(self, u: float, v: float) -> float:
|
|
458
489
|
"""
|
|
459
|
-
Compute mean curvature
|
|
490
|
+
Compute mean curvature H = (EN - 2FM + GL) / (2*det(I)).
|
|
460
491
|
"""
|
|
461
|
-
# 使用与高斯曲率相同的计算方法
|
|
462
492
|
G_u, G_v, _ = self.grad_op.compute_both(u, v)
|
|
463
|
-
|
|
464
|
-
# 计算切向量(使用解析表达式)
|
|
465
|
-
if isinstance(self.surface, Sphere):
|
|
466
|
-
R = self.surface.R
|
|
467
|
-
theta, phi = u, v
|
|
468
|
-
r_u = vec3(
|
|
469
|
-
R * math.cos(theta) * math.cos(phi),
|
|
470
|
-
R * math.cos(theta) * math.sin(phi),
|
|
471
|
-
-R * math.sin(theta)
|
|
472
|
-
)
|
|
473
|
-
r_v = vec3(
|
|
474
|
-
-R * math.sin(theta) * math.sin(phi),
|
|
475
|
-
R * math.sin(theta) * math.cos(phi),
|
|
476
|
-
0
|
|
477
|
-
)
|
|
478
|
-
elif isinstance(self.surface, Torus):
|
|
479
|
-
R = self.surface.R # 主半径
|
|
480
|
-
r = self.surface.r # 副半径
|
|
481
|
-
u_param, v_param = u, v
|
|
482
|
-
r_u = vec3(
|
|
483
|
-
-r * math.sin(u_param) * math.cos(v_param),
|
|
484
|
-
-r * math.sin(u_param) * math.sin(v_param),
|
|
485
|
-
r * math.cos(u_param)
|
|
486
|
-
)
|
|
487
|
-
r_v = vec3(
|
|
488
|
-
-(R + r * math.cos(u_param)) * math.sin(v_param),
|
|
489
|
-
(R + r * math.cos(u_param)) * math.cos(v_param),
|
|
490
|
-
0
|
|
491
|
-
)
|
|
492
|
-
else:
|
|
493
|
-
# 对于其他曲面,使用数值导数
|
|
494
|
-
r_u = self.surface.tangent_u(u, v)
|
|
495
|
-
r_v = self.surface.tangent_v(u, v)
|
|
493
|
+
r_u, r_v = self._get_tangent_vectors(u, v)
|
|
496
494
|
|
|
497
495
|
dn_du = G_u.dn
|
|
498
496
|
dn_dv = G_v.dn
|
|
@@ -508,7 +506,6 @@ class IntrinsicGradientCurvatureCalculator:
|
|
|
508
506
|
N = -dn_dv.dot(r_v)
|
|
509
507
|
M = (M1 + M2) / 2.0
|
|
510
508
|
|
|
511
|
-
# 平均曲率
|
|
512
509
|
if abs(metric_det) > 1e-14:
|
|
513
510
|
H = (G * L - 2 * F * M + E * N) / (2 * metric_det)
|
|
514
511
|
else:
|
|
@@ -516,14 +513,49 @@ class IntrinsicGradientCurvatureCalculator:
|
|
|
516
513
|
|
|
517
514
|
return H
|
|
518
515
|
|
|
519
|
-
def
|
|
516
|
+
def compute_riemann_curvature(self, u: float, v: float) -> float:
|
|
517
|
+
"""
|
|
518
|
+
Compute Riemann curvature tensor component R^1_212.
|
|
519
|
+
|
|
520
|
+
For 2D surfaces: R^1_212 = K * det(g) = LN - M^2
|
|
521
|
+
"""
|
|
522
|
+
G_u, G_v, _ = self.grad_op.compute_both(u, v)
|
|
523
|
+
r_u, r_v = self._get_tangent_vectors(u, v)
|
|
524
|
+
|
|
525
|
+
dn_du = G_u.dn
|
|
526
|
+
dn_dv = G_v.dn
|
|
527
|
+
|
|
528
|
+
L = -dn_du.dot(r_u)
|
|
529
|
+
M = -dn_du.dot(r_v)
|
|
530
|
+
N = -dn_dv.dot(r_v)
|
|
531
|
+
|
|
532
|
+
R_1212 = L * N - M * M
|
|
533
|
+
return R_1212
|
|
534
|
+
|
|
535
|
+
def compute_principal_curvatures(self, u: float, v: float) -> Tuple[float, float]:
|
|
536
|
+
"""
|
|
537
|
+
Compute principal curvatures k1 and k2.
|
|
538
|
+
|
|
539
|
+
Uses: k1,k2 = H +/- sqrt(H^2 - K)
|
|
540
|
+
"""
|
|
541
|
+
K = self.compute_gaussian_curvature(u, v)
|
|
542
|
+
H = self.compute_mean_curvature(u, v)
|
|
543
|
+
|
|
544
|
+
discriminant = max(0, H * H - K)
|
|
545
|
+
sqrt_disc = discriminant ** 0.5
|
|
546
|
+
|
|
547
|
+
k1 = H + sqrt_disc
|
|
548
|
+
k2 = H - sqrt_disc
|
|
549
|
+
|
|
550
|
+
return k1, k2
|
|
551
|
+
|
|
552
|
+
def compute_all_curvatures(self, u: float, v: float) -> Dict[str, Union[float, Tuple[float, float]]]:
|
|
520
553
|
"""
|
|
521
|
-
Compute all curvature quantities
|
|
554
|
+
Compute all curvature quantities at once.
|
|
522
555
|
"""
|
|
523
556
|
K = self.compute_gaussian_curvature(u, v)
|
|
524
557
|
H = self.compute_mean_curvature(u, v)
|
|
525
|
-
|
|
526
|
-
# 主曲率
|
|
558
|
+
|
|
527
559
|
discriminant = max(0, H * H - K)
|
|
528
560
|
sqrt_disc = discriminant ** 0.5
|
|
529
561
|
k1 = H + sqrt_disc
|
|
@@ -536,7 +568,172 @@ class IntrinsicGradientCurvatureCalculator:
|
|
|
536
568
|
}
|
|
537
569
|
|
|
538
570
|
|
|
539
|
-
#
|
|
571
|
+
# ============================================================
|
|
572
|
+
# Classical Curvature Calculator
|
|
573
|
+
# ============================================================
|
|
574
|
+
|
|
575
|
+
class CurvatureCalculator:
|
|
576
|
+
"""
|
|
577
|
+
High-precision discrete curvature calculator using classical differential geometry.
|
|
578
|
+
|
|
579
|
+
Uses high-order finite differences for computing derivatives
|
|
580
|
+
and fundamental forms.
|
|
581
|
+
"""
|
|
582
|
+
|
|
583
|
+
def __init__(self, surface: Surface, step_size: float = 1e-3):
|
|
584
|
+
self.surface = surface
|
|
585
|
+
self.h = step_size
|
|
586
|
+
|
|
587
|
+
def _position_array(self, u: float, v: float) -> np.ndarray:
|
|
588
|
+
"""Convert vec3 position to numpy array."""
|
|
589
|
+
pos = self.surface.position(u, v)
|
|
590
|
+
return np.array([pos.x, pos.y, pos.z])
|
|
591
|
+
|
|
592
|
+
def _compute_derivatives(self, u: float, v: float) -> Dict[str, np.ndarray]:
|
|
593
|
+
"""Compute surface derivatives using high-order finite differences."""
|
|
594
|
+
effective_h = max(self.h, 1e-6)
|
|
595
|
+
|
|
596
|
+
r_u = derivative_5pt(lambda uu: self._position_array(uu, v), u, effective_h)
|
|
597
|
+
r_v = derivative_5pt(lambda vv: self._position_array(u, vv), v, effective_h)
|
|
598
|
+
|
|
599
|
+
r_uu = derivative_2nd_5pt(lambda uu: self._position_array(uu, v), u, effective_h)
|
|
600
|
+
r_vv = derivative_2nd_5pt(lambda vv: self._position_array(u, vv), v, effective_h)
|
|
601
|
+
r_uv = derivative_5pt(
|
|
602
|
+
lambda vv: derivative_5pt(
|
|
603
|
+
lambda uu: self._position_array(uu, vv), u, effective_h
|
|
604
|
+
), v, effective_h
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
return {
|
|
608
|
+
'r_u': r_u, 'r_v': r_v,
|
|
609
|
+
'r_uu': r_uu, 'r_vv': r_vv, 'r_uv': r_uv
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
def compute_fundamental_forms(self, u: float, v: float) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
613
|
+
"""
|
|
614
|
+
Compute first and second fundamental forms.
|
|
615
|
+
|
|
616
|
+
Returns:
|
|
617
|
+
(g, h, n): First form, second form, normal vector
|
|
618
|
+
"""
|
|
619
|
+
derivs = self._compute_derivatives(u, v)
|
|
620
|
+
r_u = derivs['r_u']
|
|
621
|
+
r_v = derivs['r_v']
|
|
622
|
+
r_uu = derivs['r_uu']
|
|
623
|
+
r_vv = derivs['r_vv']
|
|
624
|
+
r_uv = derivs['r_uv']
|
|
625
|
+
|
|
626
|
+
# First fundamental form
|
|
627
|
+
E = np.dot(r_u, r_u)
|
|
628
|
+
F = np.dot(r_u, r_v)
|
|
629
|
+
G = np.dot(r_v, r_v)
|
|
630
|
+
g = np.array([[E, F], [F, G]])
|
|
631
|
+
|
|
632
|
+
# Normal vector
|
|
633
|
+
n_vec = np.cross(r_u, r_v)
|
|
634
|
+
n_norm = np.linalg.norm(n_vec)
|
|
635
|
+
if n_norm > 1e-14:
|
|
636
|
+
n = n_vec / n_norm
|
|
637
|
+
else:
|
|
638
|
+
n = np.array([0., 0., 1.])
|
|
639
|
+
|
|
640
|
+
# Second fundamental form
|
|
641
|
+
L = np.dot(r_uu, n)
|
|
642
|
+
M = np.dot(r_uv, n)
|
|
643
|
+
N = np.dot(r_vv, n)
|
|
644
|
+
h = np.array([[L, M], [M, N]])
|
|
645
|
+
|
|
646
|
+
return g, h, n
|
|
647
|
+
|
|
648
|
+
def compute_gaussian_curvature(self, u: float, v: float) -> float:
|
|
649
|
+
"""Compute Gaussian curvature K = det(II) / det(I)."""
|
|
650
|
+
g, h, _ = self.compute_fundamental_forms(u, v)
|
|
651
|
+
|
|
652
|
+
det_g = np.linalg.det(g)
|
|
653
|
+
det_h = np.linalg.det(h)
|
|
654
|
+
|
|
655
|
+
if abs(det_g) < 1e-14:
|
|
656
|
+
return 0.0
|
|
657
|
+
|
|
658
|
+
return det_h / det_g
|
|
659
|
+
|
|
660
|
+
def compute_mean_curvature(self, u: float, v: float) -> float:
|
|
661
|
+
"""Compute mean curvature H."""
|
|
662
|
+
g, h, _ = self.compute_fundamental_forms(u, v)
|
|
663
|
+
|
|
664
|
+
det_g = np.linalg.det(g)
|
|
665
|
+
if abs(det_g) < 1e-14:
|
|
666
|
+
return 0.0
|
|
667
|
+
|
|
668
|
+
trace_term = g[1, 1] * h[0, 0] - 2 * g[0, 1] * h[0, 1] + g[0, 0] * h[1, 1]
|
|
669
|
+
H = trace_term / (2 * det_g)
|
|
670
|
+
|
|
671
|
+
return abs(H)
|
|
672
|
+
|
|
673
|
+
def compute_principal_curvatures(self, u: float, v: float) -> Tuple[float, float, np.ndarray, np.ndarray]:
|
|
674
|
+
"""
|
|
675
|
+
Compute principal curvatures and principal directions.
|
|
676
|
+
|
|
677
|
+
Returns:
|
|
678
|
+
(k1, k2, dir1, dir2): Principal curvatures and directions
|
|
679
|
+
"""
|
|
680
|
+
g, h, _ = self.compute_fundamental_forms(u, v)
|
|
681
|
+
derivs = self._compute_derivatives(u, v)
|
|
682
|
+
r_u = derivs['r_u']
|
|
683
|
+
r_v = derivs['r_v']
|
|
684
|
+
|
|
685
|
+
det_g = np.linalg.det(g)
|
|
686
|
+
if abs(det_g) < 1e-14:
|
|
687
|
+
return 0.0, 0.0, np.array([1., 0., 0.]), np.array([0., 1., 0.])
|
|
688
|
+
|
|
689
|
+
# Shape operator S = g^{-1} h
|
|
690
|
+
g_inv = np.linalg.inv(g)
|
|
691
|
+
S = g_inv @ h
|
|
692
|
+
|
|
693
|
+
# Eigenvalue decomposition
|
|
694
|
+
eigenvalues, eigenvectors = np.linalg.eig(S)
|
|
695
|
+
k1, k2 = eigenvalues.real
|
|
696
|
+
|
|
697
|
+
# Ensure k1 >= k2 by absolute value
|
|
698
|
+
if abs(k1) < abs(k2):
|
|
699
|
+
k1, k2 = k2, k1
|
|
700
|
+
eigenvectors = eigenvectors[:, [1, 0]]
|
|
701
|
+
|
|
702
|
+
# Convert to 3D directions
|
|
703
|
+
dir1_2d = eigenvectors[:, 0]
|
|
704
|
+
dir2_2d = eigenvectors[:, 1]
|
|
705
|
+
|
|
706
|
+
dir1_3d = dir1_2d[0] * r_u + dir1_2d[1] * r_v
|
|
707
|
+
dir2_3d = dir2_2d[0] * r_u + dir2_2d[1] * r_v
|
|
708
|
+
|
|
709
|
+
dir1_3d = dir1_3d / (np.linalg.norm(dir1_3d) + 1e-14)
|
|
710
|
+
dir2_3d = dir2_3d / (np.linalg.norm(dir2_3d) + 1e-14)
|
|
711
|
+
|
|
712
|
+
return k1, k2, dir1_3d, dir2_3d
|
|
713
|
+
|
|
714
|
+
def compute_all_curvatures(self, u: float, v: float) -> Dict[str, Union[float, np.ndarray]]:
|
|
715
|
+
"""Compute all curvature quantities at once."""
|
|
716
|
+
g, h, n = self.compute_fundamental_forms(u, v)
|
|
717
|
+
K = self.compute_gaussian_curvature(u, v)
|
|
718
|
+
H = self.compute_mean_curvature(u, v)
|
|
719
|
+
k1, k2, dir1, dir2 = self.compute_principal_curvatures(u, v)
|
|
720
|
+
|
|
721
|
+
return {
|
|
722
|
+
'K': K,
|
|
723
|
+
'H': H,
|
|
724
|
+
'k1': k1,
|
|
725
|
+
'k2': k2,
|
|
726
|
+
'dir1': dir1,
|
|
727
|
+
'dir2': dir2,
|
|
728
|
+
'g': g,
|
|
729
|
+
'h': h,
|
|
730
|
+
'n': n
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
# ============================================================
|
|
735
|
+
# Convenience Functions - Intrinsic Gradient Method (Default)
|
|
736
|
+
# ============================================================
|
|
540
737
|
|
|
541
738
|
def compute_gaussian_curvature(
|
|
542
739
|
surface: Surface,
|
|
@@ -545,15 +742,7 @@ def compute_gaussian_curvature(
|
|
|
545
742
|
step_size: float = 1e-3
|
|
546
743
|
) -> float:
|
|
547
744
|
"""
|
|
548
|
-
Compute Gaussian curvature
|
|
549
|
-
|
|
550
|
-
Args:
|
|
551
|
-
surface: Surface object
|
|
552
|
-
u, v: Parameter values
|
|
553
|
-
step_size: Step size for numerical differentiation
|
|
554
|
-
|
|
555
|
-
Returns:
|
|
556
|
-
Gaussian curvature value
|
|
745
|
+
Compute Gaussian curvature using intrinsic gradient method.
|
|
557
746
|
"""
|
|
558
747
|
calc = IntrinsicGradientCurvatureCalculator(surface, step_size)
|
|
559
748
|
return calc.compute_gaussian_curvature(u, v)
|
|
@@ -566,7 +755,7 @@ def compute_mean_curvature(
|
|
|
566
755
|
step_size: float = 1e-3
|
|
567
756
|
) -> float:
|
|
568
757
|
"""
|
|
569
|
-
Compute mean curvature
|
|
758
|
+
Compute mean curvature using intrinsic gradient method.
|
|
570
759
|
"""
|
|
571
760
|
calc = IntrinsicGradientCurvatureCalculator(surface, step_size)
|
|
572
761
|
return calc.compute_mean_curvature(u, v)
|
|
@@ -576,23 +765,13 @@ def compute_riemann_curvature(
|
|
|
576
765
|
surface: Surface,
|
|
577
766
|
u: float,
|
|
578
767
|
v: float,
|
|
579
|
-
step_size: float = 1e-3
|
|
580
|
-
proportional_correction: bool = True
|
|
768
|
+
step_size: float = 1e-3
|
|
581
769
|
) -> float:
|
|
582
770
|
"""
|
|
583
|
-
Compute Riemann curvature tensor component R^1_212
|
|
584
|
-
|
|
585
|
-
Args:
|
|
586
|
-
surface: Surface object
|
|
587
|
-
u, v: Parameter values
|
|
588
|
-
step_size: Step size for numerical differentiation
|
|
589
|
-
proportional_correction: Apply proportional correction for spherical coordinates
|
|
590
|
-
|
|
591
|
-
Returns:
|
|
592
|
-
Riemann curvature tensor component R^1_212
|
|
771
|
+
Compute Riemann curvature tensor component R^1_212.
|
|
593
772
|
"""
|
|
594
773
|
calc = IntrinsicGradientCurvatureCalculator(surface, step_size)
|
|
595
|
-
return calc.compute_riemann_curvature(u, v
|
|
774
|
+
return calc.compute_riemann_curvature(u, v)
|
|
596
775
|
|
|
597
776
|
|
|
598
777
|
def compute_all_curvatures(
|
|
@@ -600,9 +779,9 @@ def compute_all_curvatures(
|
|
|
600
779
|
u: float,
|
|
601
780
|
v: float,
|
|
602
781
|
step_size: float = 1e-3
|
|
603
|
-
) ->
|
|
782
|
+
) -> Dict[str, Union[float, Tuple[float, float]]]:
|
|
604
783
|
"""
|
|
605
|
-
Compute all curvature quantities
|
|
784
|
+
Compute all curvature quantities using intrinsic gradient method.
|
|
606
785
|
"""
|
|
607
786
|
calc = IntrinsicGradientCurvatureCalculator(surface, step_size)
|
|
608
787
|
return calc.compute_all_curvatures(u, v)
|
|
@@ -616,7 +795,7 @@ def compute_intrinsic_gradient(
|
|
|
616
795
|
step_size: float = 1e-3
|
|
617
796
|
) -> GradientResult:
|
|
618
797
|
"""
|
|
619
|
-
Compute intrinsic gradient in specified direction
|
|
798
|
+
Compute intrinsic gradient in specified direction.
|
|
620
799
|
|
|
621
800
|
Args:
|
|
622
801
|
surface: Surface object
|
|
@@ -628,24 +807,142 @@ def compute_intrinsic_gradient(
|
|
|
628
807
|
GradientResult object
|
|
629
808
|
"""
|
|
630
809
|
grad_op = IntrinsicGradientOperator(surface, step_size)
|
|
631
|
-
|
|
810
|
+
|
|
632
811
|
if direction == 'u':
|
|
633
|
-
|
|
634
|
-
c_plus = grad_op.calc_intrinsic_frame(u + step_size, v)
|
|
635
|
-
c_minus = grad_op.calc_intrinsic_frame(u - step_size, v)
|
|
636
|
-
dn = ((c_plus - c_minus) / (2 * step_size)).VZ()
|
|
812
|
+
return grad_op.compute_u(u, v)
|
|
637
813
|
elif direction == 'v':
|
|
638
|
-
|
|
639
|
-
c_plus = grad_op.calc_intrinsic_frame(u, v + step_size)
|
|
640
|
-
c_minus = grad_op.calc_intrinsic_frame(u, v - step_size)
|
|
641
|
-
dn = ((c_plus - c_minus) / (2 * step_size)).VZ()
|
|
814
|
+
return grad_op.compute_v(u, v)
|
|
642
815
|
else:
|
|
643
816
|
raise ValueError(f"direction must be 'u' or 'v', got: {direction}")
|
|
644
817
|
|
|
645
|
-
|
|
818
|
+
|
|
819
|
+
# ============================================================
|
|
820
|
+
# Convenience Functions - Classical Method
|
|
821
|
+
# ============================================================
|
|
822
|
+
|
|
823
|
+
def gaussian_curvature_classical(
|
|
824
|
+
surface: Surface,
|
|
825
|
+
u: float,
|
|
826
|
+
v: float,
|
|
827
|
+
step_size: float = 1e-3
|
|
828
|
+
) -> float:
|
|
829
|
+
"""Compute Gaussian curvature using classical method."""
|
|
830
|
+
calc = CurvatureCalculator(surface, step_size)
|
|
831
|
+
return calc.compute_gaussian_curvature(u, v)
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
def mean_curvature_classical(
|
|
835
|
+
surface: Surface,
|
|
836
|
+
u: float,
|
|
837
|
+
v: float,
|
|
838
|
+
step_size: float = 1e-3
|
|
839
|
+
) -> float:
|
|
840
|
+
"""Compute mean curvature using classical method."""
|
|
841
|
+
calc = CurvatureCalculator(surface, step_size)
|
|
842
|
+
return calc.compute_mean_curvature(u, v)
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
def principal_curvatures_classical(
|
|
846
|
+
surface: Surface,
|
|
847
|
+
u: float,
|
|
848
|
+
v: float,
|
|
849
|
+
step_size: float = 1e-3
|
|
850
|
+
) -> Tuple[float, float]:
|
|
851
|
+
"""Compute principal curvatures using classical method."""
|
|
852
|
+
calc = CurvatureCalculator(surface, step_size)
|
|
853
|
+
k1, k2, _, _ = calc.compute_principal_curvatures(u, v)
|
|
854
|
+
return k1, k2
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
def all_curvatures_classical(
|
|
858
|
+
surface: Surface,
|
|
859
|
+
u: float,
|
|
860
|
+
v: float,
|
|
861
|
+
step_size: float = 1e-3
|
|
862
|
+
) -> Dict[str, Union[float, np.ndarray]]:
|
|
863
|
+
"""Compute all curvature quantities using classical method."""
|
|
864
|
+
calc = CurvatureCalculator(surface, step_size)
|
|
865
|
+
return calc.compute_all_curvatures(u, v)
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
# ============================================================
|
|
869
|
+
# Backward Compatibility Aliases
|
|
870
|
+
# ============================================================
|
|
871
|
+
|
|
872
|
+
def gaussian_curvature(
|
|
873
|
+
surface: Surface,
|
|
874
|
+
u: float,
|
|
875
|
+
v: float,
|
|
876
|
+
step_size: float = 1e-3
|
|
877
|
+
) -> float:
|
|
878
|
+
"""Compute Gaussian curvature (default: intrinsic gradient method)."""
|
|
879
|
+
return compute_gaussian_curvature(surface, u, v, step_size)
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
def mean_curvature(
|
|
883
|
+
surface: Surface,
|
|
884
|
+
u: float,
|
|
885
|
+
v: float,
|
|
886
|
+
step_size: float = 1e-3
|
|
887
|
+
) -> float:
|
|
888
|
+
"""Compute mean curvature (default: intrinsic gradient method)."""
|
|
889
|
+
return compute_mean_curvature(surface, u, v, step_size)
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
def principal_curvatures(
|
|
893
|
+
surface: Surface,
|
|
894
|
+
u: float,
|
|
895
|
+
v: float,
|
|
896
|
+
step_size: float = 1e-3
|
|
897
|
+
) -> Tuple[float, float]:
|
|
898
|
+
"""Compute principal curvatures (default: intrinsic gradient method)."""
|
|
899
|
+
calc = IntrinsicGradientCurvatureCalculator(surface, step_size)
|
|
900
|
+
return calc.compute_principal_curvatures(u, v)
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
def all_curvatures(
|
|
904
|
+
surface: Surface,
|
|
905
|
+
u: float,
|
|
906
|
+
v: float,
|
|
907
|
+
step_size: float = 1e-3
|
|
908
|
+
) -> Dict[str, Union[float, Tuple[float, float]]]:
|
|
909
|
+
"""Compute all curvature quantities (default: intrinsic gradient method)."""
|
|
910
|
+
return compute_all_curvatures(surface, u, v, step_size)
|
|
646
911
|
|
|
647
912
|
|
|
648
|
-
#
|
|
913
|
+
# ============================================================
|
|
914
|
+
# Method Comparison
|
|
915
|
+
# ============================================================
|
|
916
|
+
|
|
917
|
+
def compare_methods(
|
|
918
|
+
surface: Surface,
|
|
919
|
+
u: float,
|
|
920
|
+
v: float,
|
|
921
|
+
step_size: float = 1e-3
|
|
922
|
+
) -> Dict[str, float]:
|
|
923
|
+
"""
|
|
924
|
+
Compare classical and intrinsic gradient curvature methods.
|
|
925
|
+
|
|
926
|
+
Returns:
|
|
927
|
+
Dictionary with curvature values from both methods and error metrics
|
|
928
|
+
"""
|
|
929
|
+
K_classical = gaussian_curvature_classical(surface, u, v, step_size)
|
|
930
|
+
K_intrinsic = compute_gaussian_curvature(surface, u, v, step_size)
|
|
931
|
+
|
|
932
|
+
difference = abs(K_classical - K_intrinsic)
|
|
933
|
+
relative_error = difference / abs(K_classical) if abs(K_classical) > 1e-14 else 0.0
|
|
934
|
+
|
|
935
|
+
return {
|
|
936
|
+
'K_classical': K_classical,
|
|
937
|
+
'K_intrinsic': K_intrinsic,
|
|
938
|
+
'difference': difference,
|
|
939
|
+
'relative_error': relative_error
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
# ============================================================
|
|
944
|
+
# Export
|
|
945
|
+
# ============================================================
|
|
649
946
|
|
|
650
947
|
__all__ = [
|
|
651
948
|
# Surface classes
|
|
@@ -655,12 +952,35 @@ __all__ = [
|
|
|
655
952
|
|
|
656
953
|
# Core classes
|
|
657
954
|
'MetricTensor',
|
|
955
|
+
'GradientResult',
|
|
658
956
|
'IntrinsicGradientOperator',
|
|
659
957
|
'IntrinsicGradientCurvatureCalculator',
|
|
958
|
+
'CurvatureCalculator',
|
|
660
959
|
|
|
661
|
-
#
|
|
960
|
+
# Intrinsic gradient method functions (default)
|
|
662
961
|
'compute_gaussian_curvature',
|
|
663
962
|
'compute_mean_curvature',
|
|
963
|
+
'compute_riemann_curvature',
|
|
664
964
|
'compute_all_curvatures',
|
|
665
965
|
'compute_intrinsic_gradient',
|
|
666
|
-
|
|
966
|
+
|
|
967
|
+
# Classical method functions
|
|
968
|
+
'gaussian_curvature_classical',
|
|
969
|
+
'mean_curvature_classical',
|
|
970
|
+
'principal_curvatures_classical',
|
|
971
|
+
'all_curvatures_classical',
|
|
972
|
+
|
|
973
|
+
# Backward compatibility aliases
|
|
974
|
+
'gaussian_curvature',
|
|
975
|
+
'mean_curvature',
|
|
976
|
+
'principal_curvatures',
|
|
977
|
+
'all_curvatures',
|
|
978
|
+
|
|
979
|
+
# Method comparison
|
|
980
|
+
'compare_methods',
|
|
981
|
+
|
|
982
|
+
# Utility functions
|
|
983
|
+
'derivative_5pt',
|
|
984
|
+
'derivative_2nd_5pt',
|
|
985
|
+
'richardson_extrapolation',
|
|
986
|
+
]
|