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.
@@ -42,9 +42,8 @@ from .curvature import (
42
42
  )
43
43
 
44
44
  # Intrinsic Gradient Operator method (v2.4.0+)
45
- from .intrinsic_gradient_curvature import (
46
- IntrinsicGradientOperator,
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)
@@ -784,16 +784,30 @@ def gaussian_curvature_lie(
784
784
 
785
785
 
786
786
  # ========== Intrinsic Gradient Operator Method ==========
787
- # Import from the new module
788
- from .intrinsic_gradient_curvature import (
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__ = [