coordinate-system 2.5.1__cp313-cp313-win_amd64.whl → 2.5.4__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 +3 -6
- coordinate_system/coord3_wrapper.py +300 -0
- coordinate_system/coordinate_system.cp313-win_amd64.pyd +0 -0
- coordinate_system/curvature.py +20 -6
- coordinate_system/differential_geometry.py +332 -327
- {coordinate_system-2.5.1.dist-info → coordinate_system-2.5.4.dist-info}/METADATA +1 -1
- coordinate_system-2.5.4.dist-info/RECORD +10 -0
- coordinate_system/intrinsic_gradient_curvature.py +0 -533
- coordinate_system/two_stage_curvature.py +0 -283
- coordinate_system-2.5.1.dist-info/RECORD +0 -11
- {coordinate_system-2.5.1.dist-info → coordinate_system-2.5.4.dist-info}/LICENSE +0 -0
- {coordinate_system-2.5.1.dist-info → coordinate_system-2.5.4.dist-info}/WHEEL +0 -0
- {coordinate_system-2.5.1.dist-info → coordinate_system-2.5.4.dist-info}/top_level.txt +0 -0
coordinate_system/__init__.py
CHANGED
|
@@ -42,9 +42,8 @@ from .curvature import (
|
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
# Intrinsic Gradient Operator method (v2.4.0+)
|
|
45
|
-
from
|
|
46
|
-
|
|
47
|
-
IntrinsicGradientCurvatureCalculator,
|
|
45
|
+
# Now imported from curvature module for backward compatibility
|
|
46
|
+
from .curvature import (
|
|
48
47
|
intrinsic_gradient_gaussian_curvature,
|
|
49
48
|
intrinsic_gradient_mean_curvature,
|
|
50
49
|
intrinsic_gradient_principal_curvatures,
|
|
@@ -78,9 +77,7 @@ __all__ = [
|
|
|
78
77
|
'gaussian_curvature_lie',
|
|
79
78
|
'compare_methods',
|
|
80
79
|
|
|
81
|
-
# Intrinsic Gradient Operator method (v2.4.0
|
|
82
|
-
'IntrinsicGradientOperator',
|
|
83
|
-
'IntrinsicGradientCurvatureCalculator',
|
|
80
|
+
# Intrinsic Gradient Operator method (v2.4.0+, merged into other modules)
|
|
84
81
|
'intrinsic_gradient_gaussian_curvature',
|
|
85
82
|
'intrinsic_gradient_mean_curvature',
|
|
86
83
|
'intrinsic_gradient_principal_curvatures',
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Coord3 Wrapper for Intrinsic Gradient Operator
|
|
3
|
+
===============================================
|
|
4
|
+
|
|
5
|
+
This module provides a Python wrapper for coord3 that correctly implements
|
|
6
|
+
the mathematical operations needed for the intrinsic gradient operator.
|
|
7
|
+
|
|
8
|
+
Key insight: Use coord3's normalize() to separate rotation and scaling,
|
|
9
|
+
which is a core advantage of our coordinate system design over matrices.
|
|
10
|
+
|
|
11
|
+
Solution: Implement subtraction that properly handles the rotation-scale separation.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
from .coordinate_system import coord3 as _coord3, vec3
|
|
16
|
+
import math
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Coord3Wrapper:
|
|
20
|
+
"""
|
|
21
|
+
Wrapper for coord3 that implements correct arithmetic for gradient operators
|
|
22
|
+
|
|
23
|
+
The intrinsic gradient operator requires:
|
|
24
|
+
G_μ = (c(u+h) - c(u)) / h
|
|
25
|
+
|
|
26
|
+
This implementation leverages coord3's ability to separate rotation and scaling.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, coord3_obj=None):
|
|
30
|
+
"""
|
|
31
|
+
Initialize from a coord3 object or create new
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
coord3_obj: Original coord3 object from C++ binding
|
|
35
|
+
"""
|
|
36
|
+
if coord3_obj is None:
|
|
37
|
+
self._c3 = _coord3()
|
|
38
|
+
else:
|
|
39
|
+
self._c3 = coord3_obj
|
|
40
|
+
# Ensure normalization to separate rotation and scale
|
|
41
|
+
self.normalize_axes()
|
|
42
|
+
|
|
43
|
+
def normalize_axes(self):
|
|
44
|
+
"""
|
|
45
|
+
Normalize axes and extract scale factors
|
|
46
|
+
|
|
47
|
+
This is the key operation that separates rotation from scaling,
|
|
48
|
+
which is the core advantage of coord3 over matrix representations.
|
|
49
|
+
"""
|
|
50
|
+
# Extract scale factors from current axes
|
|
51
|
+
sx = math.sqrt(self._c3.ux.dot(self._c3.ux))
|
|
52
|
+
sy = math.sqrt(self._c3.uy.dot(self._c3.uy))
|
|
53
|
+
sz = math.sqrt(self._c3.uz.dot(self._c3.uz))
|
|
54
|
+
|
|
55
|
+
# Normalize axes (pure rotation)
|
|
56
|
+
if sx > 1e-10:
|
|
57
|
+
self._c3.ux = self._c3.ux / sx
|
|
58
|
+
if sy > 1e-10:
|
|
59
|
+
self._c3.uy = self._c3.uy / sy
|
|
60
|
+
if sz > 1e-10:
|
|
61
|
+
self._c3.uz = self._c3.uz / sz
|
|
62
|
+
|
|
63
|
+
# Store scale factors separately
|
|
64
|
+
self._c3.s = vec3(sx, sy, sz)
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def o(self):
|
|
68
|
+
"""Origin/position"""
|
|
69
|
+
return self._c3.o
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def ux(self):
|
|
73
|
+
"""X axis direction (unit vector)"""
|
|
74
|
+
return self._c3.ux
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def uy(self):
|
|
78
|
+
"""Y axis direction (unit vector)"""
|
|
79
|
+
return self._c3.uy
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def uz(self):
|
|
83
|
+
"""Z axis direction (unit vector)"""
|
|
84
|
+
return self._c3.uz
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def s(self):
|
|
88
|
+
"""Scale factors"""
|
|
89
|
+
return self._c3.s
|
|
90
|
+
|
|
91
|
+
def VX(self):
|
|
92
|
+
"""Scaled X axis = ux * s.x"""
|
|
93
|
+
return vec3(
|
|
94
|
+
self.ux.x * self.s.x,
|
|
95
|
+
self.ux.y * self.s.x,
|
|
96
|
+
self.ux.z * self.s.x
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def VY(self):
|
|
100
|
+
"""Scaled Y axis = uy * s.y"""
|
|
101
|
+
return vec3(
|
|
102
|
+
self.uy.x * self.s.y,
|
|
103
|
+
self.uy.y * self.s.y,
|
|
104
|
+
self.uy.z * self.s.y
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def VZ(self):
|
|
108
|
+
"""Scaled Z axis = uz * s.z"""
|
|
109
|
+
return vec3(
|
|
110
|
+
self.uz.x * self.s.z,
|
|
111
|
+
self.uz.y * self.s.z,
|
|
112
|
+
self.uz.z * self.s.z
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def __sub__(self, other):
|
|
116
|
+
"""
|
|
117
|
+
Subtraction for gradient operator with proper rotation-scale separation
|
|
118
|
+
|
|
119
|
+
This leverages coord3's design advantage:
|
|
120
|
+
1. Both frames are normalized (rotation and scale separated)
|
|
121
|
+
2. Compute differences in rotation (unit vectors)
|
|
122
|
+
3. Compute differences in scale (scale factors)
|
|
123
|
+
4. Keep them separated for proper gradient computation
|
|
124
|
+
"""
|
|
125
|
+
result = Coord3Wrapper()
|
|
126
|
+
|
|
127
|
+
# Ensure both frames are normalized
|
|
128
|
+
self.normalize_axes()
|
|
129
|
+
if isinstance(other, Coord3Wrapper):
|
|
130
|
+
other.normalize_axes()
|
|
131
|
+
|
|
132
|
+
# Origin difference
|
|
133
|
+
result._c3.o = self.o - other.o
|
|
134
|
+
|
|
135
|
+
# Rotation differences (unit vectors)
|
|
136
|
+
result._c3.ux = self.ux - other.ux
|
|
137
|
+
result._c3.uy = self.uy - other.uy
|
|
138
|
+
result._c3.uz = self.uz - other.uz
|
|
139
|
+
|
|
140
|
+
# Scale differences (kept separate)
|
|
141
|
+
result._c3.s = self.s - other.s
|
|
142
|
+
|
|
143
|
+
return result
|
|
144
|
+
|
|
145
|
+
def __truediv__(self, scalar):
|
|
146
|
+
"""
|
|
147
|
+
Division by scalar for gradient operator
|
|
148
|
+
|
|
149
|
+
Used in G_μ = (c(u+h) - c(u)) / h
|
|
150
|
+
"""
|
|
151
|
+
if isinstance(scalar, (int, float)):
|
|
152
|
+
result = Coord3Wrapper()
|
|
153
|
+
|
|
154
|
+
# Divide all components by scalar
|
|
155
|
+
result._c3.o = self.o / scalar
|
|
156
|
+
result._c3.ux = self.ux / scalar
|
|
157
|
+
result._c3.uy = self.uy / scalar
|
|
158
|
+
result._c3.uz = self.uz / scalar
|
|
159
|
+
result._c3.s = self.s / scalar
|
|
160
|
+
|
|
161
|
+
return result
|
|
162
|
+
else:
|
|
163
|
+
raise TypeError(f"Division only supported by scalar, got {type(scalar)}")
|
|
164
|
+
|
|
165
|
+
def compute_metric_det(self):
|
|
166
|
+
"""
|
|
167
|
+
Compute determinant of metric tensor for 2D surface
|
|
168
|
+
|
|
169
|
+
For a surface parametrized by (u,v):
|
|
170
|
+
g = [[E, F], [F, G]]
|
|
171
|
+
where E = |∂r/∂u|², F = ∂r/∂u · ∂r/∂v, G = |∂r/∂v|²
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
det(g) = E*G - F²
|
|
175
|
+
"""
|
|
176
|
+
# Get scaled tangent vectors
|
|
177
|
+
vx = self.VX() # ∂r/∂u direction
|
|
178
|
+
vy = self.VY() # ∂r/∂v direction
|
|
179
|
+
|
|
180
|
+
# First fundamental form coefficients
|
|
181
|
+
E = vx.dot(vx) # g_uu
|
|
182
|
+
F = vx.dot(vy) # g_uv
|
|
183
|
+
G = vy.dot(vy) # g_vv
|
|
184
|
+
|
|
185
|
+
# Determinant
|
|
186
|
+
return E * G - F * F
|
|
187
|
+
|
|
188
|
+
def to_coord3(self):
|
|
189
|
+
"""Convert back to original coord3"""
|
|
190
|
+
return self._c3
|
|
191
|
+
|
|
192
|
+
@classmethod
|
|
193
|
+
def from_surface_frame(cls, surface, u, v, compute_frame_func):
|
|
194
|
+
"""
|
|
195
|
+
Create from surface using frame computation function
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
surface: Surface object
|
|
199
|
+
u, v: Parameters
|
|
200
|
+
compute_frame_func: Function to compute embedding frame
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Coord3Wrapper with proper metric information
|
|
204
|
+
"""
|
|
205
|
+
frame = compute_frame_func(surface, u, v)
|
|
206
|
+
return cls(frame)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def create_embedding_frame_with_normalize(surface, u, v):
|
|
210
|
+
"""
|
|
211
|
+
Create embedding frame for surface at point (u,v) using coord3's normalize
|
|
212
|
+
|
|
213
|
+
This implementation leverages coord3's design advantage:
|
|
214
|
+
1. Compute tangent vectors r_u, r_v
|
|
215
|
+
2. Build initial frame with scaled axes
|
|
216
|
+
3. Use normalize() to separate rotation and scaling
|
|
217
|
+
4. Apply Gram-Schmidt in the normalized space
|
|
218
|
+
5. Preserve metric information in scale factors
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
coord3 with proper embedding frame (rotation and scale separated)
|
|
222
|
+
"""
|
|
223
|
+
# Get position
|
|
224
|
+
pos = surface.position(u, v)
|
|
225
|
+
|
|
226
|
+
# Compute tangent vectors using finite differences
|
|
227
|
+
h = 1e-6
|
|
228
|
+
pos_u_plus = surface.position(u + h, v)
|
|
229
|
+
pos_u_minus = surface.position(u - h, v)
|
|
230
|
+
r_u = (pos_u_plus - pos_u_minus) / (2 * h)
|
|
231
|
+
|
|
232
|
+
pos_v_plus = surface.position(u, v + h)
|
|
233
|
+
pos_v_minus = surface.position(u, v - h)
|
|
234
|
+
r_v = (pos_v_plus - pos_v_minus) / (2 * h)
|
|
235
|
+
|
|
236
|
+
# Create initial frame with tangent vectors (not normalized)
|
|
237
|
+
c = _coord3()
|
|
238
|
+
c.o = pos
|
|
239
|
+
c.ux = r_u # Will be normalized later
|
|
240
|
+
c.uy = r_v # Will be normalized later
|
|
241
|
+
|
|
242
|
+
# Compute initial normal (may not be unit)
|
|
243
|
+
n = r_u.cross(r_v)
|
|
244
|
+
c.uz = n
|
|
245
|
+
|
|
246
|
+
# Use coord3's normalize to separate rotation and scale
|
|
247
|
+
# This automatically extracts scale factors and normalizes axes
|
|
248
|
+
c.normalize() # This is the key operation!
|
|
249
|
+
|
|
250
|
+
# Now apply Gram-Schmidt to ensure orthogonality
|
|
251
|
+
# (while preserving the extracted scale factors)
|
|
252
|
+
|
|
253
|
+
# Get current scale factors (preserved from normalize)
|
|
254
|
+
s1 = c.s.x
|
|
255
|
+
s2 = c.s.y
|
|
256
|
+
s3 = c.s.z
|
|
257
|
+
|
|
258
|
+
# Gram-Schmidt on normalized axes
|
|
259
|
+
e1 = c.ux # Already normalized
|
|
260
|
+
|
|
261
|
+
# Orthogonalize e2 against e1
|
|
262
|
+
e2_temp = c.uy - e1 * c.uy.dot(e1)
|
|
263
|
+
e2_len = math.sqrt(e2_temp.dot(e2_temp))
|
|
264
|
+
if e2_len > 1e-10:
|
|
265
|
+
e2 = e2_temp / e2_len
|
|
266
|
+
else:
|
|
267
|
+
# Degenerate case: create orthogonal vector
|
|
268
|
+
if abs(e1.x) < 0.9:
|
|
269
|
+
e2 = vec3(1, 0, 0) - e1 * e1.x
|
|
270
|
+
else:
|
|
271
|
+
e2 = vec3(0, 1, 0) - e1 * e1.y
|
|
272
|
+
e2 = e2.normalized()
|
|
273
|
+
|
|
274
|
+
# Compute orthogonal normal
|
|
275
|
+
e3 = e1.cross(e2)
|
|
276
|
+
e3_len = math.sqrt(e3.dot(e3))
|
|
277
|
+
if e3_len > 1e-10:
|
|
278
|
+
e3 = e3 / e3_len
|
|
279
|
+
else:
|
|
280
|
+
e3 = vec3(0, 0, 1)
|
|
281
|
+
|
|
282
|
+
# Update frame with orthonormal axes
|
|
283
|
+
c.ux = e1
|
|
284
|
+
c.uy = e2
|
|
285
|
+
c.uz = e3
|
|
286
|
+
|
|
287
|
+
# Preserve the original scale factors (metric information)
|
|
288
|
+
# This is crucial for curvature computation
|
|
289
|
+
c.s = vec3(s1, s2, 1.0)
|
|
290
|
+
|
|
291
|
+
return c
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def create_embedding_frame(surface, u, v):
|
|
295
|
+
"""
|
|
296
|
+
Create embedding frame for surface - backward compatibility wrapper
|
|
297
|
+
|
|
298
|
+
Calls the new implementation that uses normalize().
|
|
299
|
+
"""
|
|
300
|
+
return create_embedding_frame_with_normalize(surface, u, v)
|
|
Binary file
|
coordinate_system/curvature.py
CHANGED
|
@@ -784,16 +784,30 @@ def gaussian_curvature_lie(
|
|
|
784
784
|
|
|
785
785
|
|
|
786
786
|
# ========== Intrinsic Gradient Operator Method ==========
|
|
787
|
-
#
|
|
788
|
-
from .
|
|
787
|
+
# Now imported from differential_geometry module
|
|
788
|
+
from .differential_geometry import (
|
|
789
789
|
IntrinsicGradientOperator,
|
|
790
790
|
IntrinsicGradientCurvatureCalculator,
|
|
791
|
-
intrinsic_gradient_gaussian_curvature,
|
|
792
|
-
intrinsic_gradient_mean_curvature,
|
|
793
|
-
intrinsic_gradient_principal_curvatures,
|
|
794
|
-
intrinsic_gradient_all_curvatures,
|
|
795
791
|
)
|
|
796
792
|
|
|
793
|
+
# Define convenience functions for backward compatibility
|
|
794
|
+
def intrinsic_gradient_gaussian_curvature(surface, u, v, step_size=1e-3):
|
|
795
|
+
calc = IntrinsicGradientCurvatureCalculator(surface, step_size)
|
|
796
|
+
return calc.compute_gaussian_curvature(u, v)
|
|
797
|
+
|
|
798
|
+
def intrinsic_gradient_mean_curvature(surface, u, v, step_size=1e-3):
|
|
799
|
+
calc = IntrinsicGradientCurvatureCalculator(surface, step_size)
|
|
800
|
+
return calc.compute_mean_curvature(u, v)
|
|
801
|
+
|
|
802
|
+
def intrinsic_gradient_principal_curvatures(surface, u, v, step_size=1e-3):
|
|
803
|
+
calc = IntrinsicGradientCurvatureCalculator(surface, step_size)
|
|
804
|
+
result = calc.compute_all_curvatures(u, v)
|
|
805
|
+
return result['principal_curvatures']
|
|
806
|
+
|
|
807
|
+
def intrinsic_gradient_all_curvatures(surface, u, v, step_size=1e-3):
|
|
808
|
+
calc = IntrinsicGradientCurvatureCalculator(surface, step_size)
|
|
809
|
+
return calc.compute_all_curvatures(u, v)
|
|
810
|
+
|
|
797
811
|
# ========== Export ==========
|
|
798
812
|
|
|
799
813
|
__all__ = [
|