coordinate-system 4.1.0__tar.gz → 5.2.0__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.
Files changed (22) hide show
  1. {coordinate_system-4.1.0/coordinate_system.egg-info → coordinate_system-5.2.0}/PKG-INFO +47 -4
  2. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/README.md +42 -1
  3. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/coordinate_system/__init__.py +42 -4
  4. coordinate_system-5.2.0/coordinate_system/curve_interpolation.py +507 -0
  5. coordinate_system-5.2.0/coordinate_system/fourier_frames.py +530 -0
  6. coordinate_system-5.2.0/coordinate_system/visualization.py +666 -0
  7. {coordinate_system-4.1.0 → coordinate_system-5.2.0/coordinate_system.egg-info}/PKG-INFO +47 -4
  8. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/coordinate_system.egg-info/SOURCES.txt +4 -0
  9. coordinate_system-5.2.0/coordinate_system.egg-info/requires.txt +2 -0
  10. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/coordinate_system_binding.cpp +1 -1
  11. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/pmsys_minimal.hpp +0 -11
  12. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/setup.py +8 -5
  13. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/LICENSE +0 -0
  14. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/MANIFEST.in +0 -0
  15. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/MATHEMATICAL_FOUNDATION.md +0 -0
  16. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/coordinate_system/curvature.py +0 -0
  17. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/coordinate_system/differential_geometry.py +0 -0
  18. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/coordinate_system/fourier_spectral.py +0 -0
  19. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/coordinate_system.egg-info/dependency_links.txt +0 -0
  20. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/coordinate_system.egg-info/not-zip-safe +0 -0
  21. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/coordinate_system.egg-info/top_level.txt +0 -0
  22. {coordinate_system-4.1.0 → coordinate_system-5.2.0}/setup.cfg +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: coordinate_system
3
- Version: 4.1.0
4
- Summary: High-performance 3D coordinate system library with right-handed conventions, Intrinsic Gradient Operator method for curvature computation, achieving machine-precision accuracy
3
+ Version: 5.2.0
4
+ Summary: High-performance 3D coordinate system with C2-continuous curve interpolation, Fourier frames with operator overloading, and advanced 3D visualization
5
5
  Home-page: https://github.com/panguojun/Coordinate-System
6
6
  Author: PanGuoJun
7
7
  Author-email: 18858146@qq.com
@@ -9,7 +9,7 @@ License: MIT
9
9
  Project-URL: Bug Reports, https://github.com/panguojun/Coordinate-System/issues
10
10
  Project-URL: Source, https://github.com/panguojun/Coordinate-System
11
11
  Project-URL: Documentation, https://github.com/panguojun/Coordinate-System/blob/main/README.md
12
- Keywords: 3d math vector quaternion coordinate-system geometry graphics spatial-computing differential-geometry curvature metric-tensor connection-operator discrete-geometry surface-curvature intrinsic-gradient-operator
12
+ Keywords: 3d math vector quaternion coordinate-system geometry graphics spatial-computing differential-geometry curvature curve-interpolation c2-continuity frenet-frames fourier-transform operator-overloading quantum-coordinates heisenberg-uncertainty visualization rgb-frames catmull-rom squad
13
13
  Platform: Windows
14
14
  Platform: Linux
15
15
  Platform: macOS
@@ -37,6 +37,8 @@ Classifier: Operating System :: MacOS
37
37
  Requires-Python: >=3.7
38
38
  Description-Content-Type: text/markdown
39
39
  License-File: LICENSE
40
+ Requires-Dist: numpy>=1.19.0
41
+ Requires-Dist: matplotlib>=3.3.0
40
42
 
41
43
  # Coordinate System Library
42
44
 
@@ -48,9 +50,50 @@ License-File: LICENSE
48
50
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
49
51
 
50
52
  **Author:** PanGuoJun
51
- **Version:** 4.0.2
53
+ **Version:** 5.2.0
52
54
  **License:** MIT
53
55
 
56
+
57
+ ---
58
+
59
+ ## What's New in v5.2.0 (2025-12-01)
60
+
61
+ ### Fourier Frames with Operator Overloading
62
+
63
+ **New Module: `fourier_frames`** - Elegant quantum coordinate transformations
64
+
65
+ - **Operator Overloading Support**: Intuitive syntax for Fourier transforms
66
+ - `F_p @ psi` - Matrix multiplication operator
67
+ - `F_p * psi` - Multiplication operator
68
+ - `psi | F_p` - Bra-ket notation
69
+ - `~F_x` - Dual frame operator
70
+ - **Quantum State Representation**: `QuantumState` and `StateVector` classes
71
+ - **Heisenberg Uncertainty**: Built-in uncertainty principle computations
72
+ - **Position <-> Momentum Transforms**: Seamless coordinate basis transformations
73
+
74
+ ### C2-Continuous Curve Interpolation
75
+
76
+ **Enhanced Module: `curve_interpolation`** - Second-order smooth curves
77
+
78
+ - **C2-Continuity**: Catmull-Rom spline for position interpolation
79
+ - **SQUAD Interpolation**: Spherical Quadrangle for smooth quaternion interpolation
80
+ - **13x Smoother**: Reduced maximum curvature from 13.93 to 1.05
81
+
82
+ ### Advanced 3D Visualization
83
+
84
+ **New Module: `visualization`** - Professional curve and frame rendering
85
+
86
+ - **RGB Frenet Frames**: Red=Tangent, Green=Normal, Blue=Binormal
87
+ - **3D Curve Rendering**: High-quality visualization with matplotlib
88
+ - **Export Support**: Save PNG/PDF outputs
89
+
90
+ ### Documentation
91
+
92
+ - [Curve Interpolation Guide](CURVE_INTERPOLATION_README.md)
93
+ - [C2 Interpolation Guide](C2_INTERPOLATION_GUIDE.md)
94
+ - Full English documentation for global distribution
95
+
96
+
54
97
  ## 🆕 What's New in v4.0.0
55
98
 
56
99
  ### 🎯 Fourier Spectral Geometry Analysis
@@ -8,9 +8,50 @@
8
8
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
9
9
 
10
10
  **Author:** PanGuoJun
11
- **Version:** 4.0.2
11
+ **Version:** 5.2.0
12
12
  **License:** MIT
13
13
 
14
+
15
+ ---
16
+
17
+ ## What's New in v5.2.0 (2025-12-01)
18
+
19
+ ### Fourier Frames with Operator Overloading
20
+
21
+ **New Module: `fourier_frames`** - Elegant quantum coordinate transformations
22
+
23
+ - **Operator Overloading Support**: Intuitive syntax for Fourier transforms
24
+ - `F_p @ psi` - Matrix multiplication operator
25
+ - `F_p * psi` - Multiplication operator
26
+ - `psi | F_p` - Bra-ket notation
27
+ - `~F_x` - Dual frame operator
28
+ - **Quantum State Representation**: `QuantumState` and `StateVector` classes
29
+ - **Heisenberg Uncertainty**: Built-in uncertainty principle computations
30
+ - **Position <-> Momentum Transforms**: Seamless coordinate basis transformations
31
+
32
+ ### C2-Continuous Curve Interpolation
33
+
34
+ **Enhanced Module: `curve_interpolation`** - Second-order smooth curves
35
+
36
+ - **C2-Continuity**: Catmull-Rom spline for position interpolation
37
+ - **SQUAD Interpolation**: Spherical Quadrangle for smooth quaternion interpolation
38
+ - **13x Smoother**: Reduced maximum curvature from 13.93 to 1.05
39
+
40
+ ### Advanced 3D Visualization
41
+
42
+ **New Module: `visualization`** - Professional curve and frame rendering
43
+
44
+ - **RGB Frenet Frames**: Red=Tangent, Green=Normal, Blue=Binormal
45
+ - **3D Curve Rendering**: High-quality visualization with matplotlib
46
+ - **Export Support**: Save PNG/PDF outputs
47
+
48
+ ### Documentation
49
+
50
+ - [Curve Interpolation Guide](CURVE_INTERPOLATION_README.md)
51
+ - [C2 Interpolation Guide](C2_INTERPOLATION_GUIDE.md)
52
+ - Full English documentation for global distribution
53
+
54
+
14
55
  ## 🆕 What's New in v4.0.0
15
56
 
16
57
  ### 🎯 Fourier Spectral Geometry Analysis
@@ -50,17 +50,17 @@ from .fourier_spectral import (
50
50
  FourierTransformer,
51
51
  SpectralAnalyzer,
52
52
  FrameFieldSpectrum,
53
-
53
+
54
54
  # GPU accelerated transforms
55
55
  GPUFourierTransformer,
56
56
  BatchCoordTransformer,
57
-
57
+
58
58
  # Spectral geometry operations
59
59
  spectral_intrinsic_gradient,
60
60
  spectral_curvature_calculator,
61
61
  berry_phase_calculator,
62
62
  topological_invariant_analyzer,
63
-
63
+
64
64
  # Utility functions
65
65
  fft2_coord_field,
66
66
  ifft2_spectrum,
@@ -68,6 +68,35 @@ from .fourier_spectral import (
68
68
  radial_spectrum_average,
69
69
  )
70
70
 
71
+ # Visualization module (v5.0.0+)
72
+ from .visualization import (
73
+ # Visualizer classes
74
+ CoordinateSystemVisualizer,
75
+ CurveVisualizer,
76
+ ParametricCurve,
77
+
78
+ # Convenience functions
79
+ visualize_coord_system,
80
+ visualize_curve,
81
+ )
82
+
83
+ # Curve interpolation module (v5.1.0+)
84
+ from .curve_interpolation import (
85
+ # Main class
86
+ InterpolatedCurve,
87
+
88
+ # Functions
89
+ generate_frenet_frames,
90
+ frame_field_spline,
91
+ frame_field_spline_c2,
92
+ reconstruct_curve_from_polygon,
93
+ compute_curvature_profile,
94
+
95
+ # Utility functions
96
+ catmull_rom,
97
+ squad_interp,
98
+ )
99
+
71
100
  __all__ = [
72
101
  # Constants
73
102
  'ZERO3','UNITX','UNITY','UNITZ','ONE3','ONE4','ONEC',
@@ -106,8 +135,17 @@ __all__ = [
106
135
  'GPUFourierTransformer', 'BatchCoordTransformer',
107
136
  'spectral_intrinsic_gradient', 'spectral_curvature_calculator',
108
137
  'berry_phase_calculator', 'topological_invariant_analyzer',
109
- 'fft2_coord_field', 'ifft2_spectrum',
138
+ 'fft2_coord_field', 'ifft2_spectrum',
110
139
  'compute_spectral_density', 'radial_spectrum_average',
140
+
141
+ # Visualization (v5.0.0+)
142
+ 'CoordinateSystemVisualizer', 'CurveVisualizer', 'ParametricCurve',
143
+ 'visualize_coord_system', 'visualize_curve',
144
+
145
+ # Curve interpolation (v5.1.0+)
146
+ 'InterpolatedCurve', 'generate_frenet_frames', 'frame_field_spline',
147
+ 'frame_field_spline_c2', 'reconstruct_curve_from_polygon', 'compute_curvature_profile',
148
+ 'catmull_rom', 'squad_interp',
111
149
  ]
112
150
 
113
151
  # Constants for unit vectors and zero point
@@ -0,0 +1,507 @@
1
+ """
2
+ Frame Field Curve Interpolation System
3
+ =======================================
4
+
5
+ Frame field spline interpolation based on C++ implementation,
6
+ providing geometrically continuous curve reconstruction equivalent to NURBS.
7
+
8
+ Main Features:
9
+ - Generate Frenet frames from discrete points
10
+ - Frame field interpolation with multiple parameterization methods
11
+ - C2-continuous high-order interpolation
12
+ - B-spline and frame field hybrid interpolation
13
+ - Curvature distribution analysis
14
+
15
+ Author: PanGuoJun
16
+ Date: 2025-12-01
17
+ """
18
+
19
+ import math
20
+ import numpy as np
21
+ from typing import List, Tuple, Optional
22
+
23
+ try:
24
+ from .coordinate_system import vec3, coord3, quat
25
+ except ImportError:
26
+ import coordinate_system
27
+ vec3 = coordinate_system.vec3
28
+ coord3 = coordinate_system.coord3
29
+ quat = coordinate_system.quat
30
+
31
+
32
+ # ========== Utility Functions ==========
33
+
34
+ def catmull_rom(p0: vec3, p1: vec3, p2: vec3, p3: vec3, t: float) -> vec3:
35
+ """
36
+ Catmull-Rom spline interpolation for smooth position interpolation
37
+
38
+ Args:
39
+ p0, p1, p2, p3: Four control points
40
+ t: Parameter [0, 1] between p1 and p2
41
+
42
+ Returns:
43
+ Interpolated position
44
+ """
45
+ t2 = t * t
46
+ t3 = t2 * t
47
+
48
+ # Catmull-Rom basis functions
49
+ result = (
50
+ p0 * (-0.5 * t3 + 1.0 * t2 - 0.5 * t) +
51
+ p1 * ( 1.5 * t3 - 2.5 * t2 + 1.0) +
52
+ p2 * (-1.5 * t3 + 2.0 * t2 + 0.5 * t) +
53
+ p3 * ( 0.5 * t3 - 0.5 * t2)
54
+ )
55
+
56
+ return result
57
+
58
+
59
+ def squad_interp(q0: quat, q1: quat, q2: quat, q3: quat, t: float) -> quat:
60
+ """
61
+ SQUAD (Spherical Quadrangle) quaternion interpolation for C2-continuous rotation
62
+
63
+ Args:
64
+ q0, q1, q2, q3: Four quaternions
65
+ t: Parameter [0, 1] between q1 and q2
66
+
67
+ Returns:
68
+ Interpolated quaternion
69
+ """
70
+ # Compute intermediate control quaternions for smooth interpolation
71
+ def compute_intermediate(qa: quat, qb: quat, qc: quat) -> quat:
72
+ """Compute intermediate control quaternion"""
73
+ # Ensure shortest path
74
+ if qa.dot(qb) < 0:
75
+ qb = quat(-qb.w, -qb.x, -qb.y, -qb.z)
76
+ if qb.dot(qc) < 0:
77
+ qc = quat(-qc.w, -qc.x, -qc.y, -qc.z)
78
+
79
+ # Log map
80
+ inv_qb = qb.inverse()
81
+ log_qa_qb = (inv_qb * qa).ln()
82
+ log_qc_qb = (inv_qb * qc).ln()
83
+
84
+ # Intermediate control point
85
+ intermediate = qb * (((log_qa_qb + log_qc_qb) * -0.25).exp())
86
+ return intermediate
87
+
88
+ # Compute control quaternions
89
+ try:
90
+ s1 = compute_intermediate(q0, q1, q2)
91
+ s2 = compute_intermediate(q1, q2, q3)
92
+ except:
93
+ # Fallback to simple slerp if SQUAD fails
94
+ return quat.slerp(q1, q2, t)
95
+
96
+ # SQUAD interpolation: slerp(slerp(q1, q2, t), slerp(s1, s2, t), 2t(1-t))
97
+ slerp1 = quat.slerp(q1, q2, t)
98
+ slerp2 = quat.slerp(s1, s2, t)
99
+ h = 2.0 * t * (1.0 - t)
100
+
101
+ return quat.slerp(slerp1, slerp2, h)
102
+
103
+
104
+ # ========== Core Functions ==========
105
+
106
+ def generate_frenet_frames(points: List[vec3]) -> List[coord3]:
107
+ """
108
+ Generate Frenet-like frames from discrete point sequence
109
+
110
+ Args:
111
+ points: Discrete point sequence
112
+
113
+ Returns:
114
+ Corresponding frame sequence
115
+ """
116
+ frames = []
117
+ n = len(points)
118
+
119
+ if n < 3:
120
+ # Use simple frames when not enough points
121
+ for i in range(n):
122
+ if i == 0:
123
+ tangent = (points[1] - points[0]).normalized()
124
+ elif i == n-1:
125
+ tangent = (points[n-1] - points[n-2]).normalized()
126
+ else:
127
+ tangent = (points[i+1] - points[i-1]).normalized()
128
+
129
+ # Construct orthogonal frame
130
+ UZ = vec3(0, 0, 1)
131
+ UY = vec3(0, 1, 0)
132
+
133
+ if abs(1.0 - abs(tangent.dot(UZ))) > 0.1:
134
+ normal = tangent.cross(UZ).normalized()
135
+ else:
136
+ normal = tangent.cross(UY).normalized()
137
+
138
+ binormal = tangent.cross(normal).normalized()
139
+
140
+ frame = coord3.from_axes(tangent, normal, binormal)
141
+ frame.o = points[i]
142
+ frames.append(frame)
143
+
144
+ return frames
145
+
146
+ # Calculate Frenet frame for each point
147
+ for i in range(n):
148
+ if i == 0:
149
+ # Start point: forward difference
150
+ tangent = (points[1] - points[0]).normalized()
151
+ v1 = points[1] - points[0]
152
+ v2 = points[2] - points[1]
153
+ binormal = v1.cross(v2).normalized()
154
+
155
+ if binormal.length() < 1e-6:
156
+ UZ = vec3(0, 0, 1)
157
+ UY = vec3(0, 1, 0)
158
+ if abs(1.0 - abs(tangent.dot(UZ))) > 0.1:
159
+ binormal = tangent.cross(UZ).normalized()
160
+ else:
161
+ binormal = tangent.cross(UY).normalized()
162
+
163
+ elif i == n-1:
164
+ # End point: backward difference
165
+ tangent = (points[n-1] - points[n-2]).normalized()
166
+ v1 = points[n-2] - points[n-3]
167
+ v2 = points[n-1] - points[n-2]
168
+ binormal = v1.cross(v2).normalized()
169
+
170
+ if binormal.length() < 1e-6:
171
+ UZ = vec3(0, 0, 1)
172
+ UY = vec3(0, 1, 0)
173
+ if abs(1.0 - abs(tangent.dot(UZ))) > 0.1:
174
+ binormal = tangent.cross(UZ).normalized()
175
+ else:
176
+ binormal = tangent.cross(UY).normalized()
177
+ else:
178
+ # Interior point: central difference
179
+ tangent = (points[i+1] - points[i-1]).normalized()
180
+ v1 = points[i] - points[i-1]
181
+ v2 = points[i+1] - points[i]
182
+ binormal = v1.cross(v2).normalized()
183
+
184
+ if binormal.length() < 1e-6:
185
+ UZ = vec3(0, 0, 1)
186
+ UY = vec3(0, 1, 0)
187
+ if abs(1.0 - abs(tangent.dot(UZ))) > 0.1:
188
+ binormal = tangent.cross(UZ).normalized()
189
+ else:
190
+ binormal = tangent.cross(UY).normalized()
191
+
192
+ # Re-orthogonalize
193
+ normal = binormal.cross(tangent).normalized()
194
+ binormal = tangent.cross(normal).normalized()
195
+
196
+ frame = coord3.from_axes(tangent, normal, binormal)
197
+ frame.o = points[i]
198
+ frames.append(frame)
199
+
200
+ return frames
201
+
202
+
203
+ def frame_field_spline(frames: List[coord3], t: float, curve_type: int = 1) -> coord3:
204
+ """
205
+ Frame field spline interpolation
206
+
207
+ Args:
208
+ frames: Frame sequence
209
+ t: Global parameter [0,1]
210
+ curve_type: Curve type (0=uniform, 1=chord-length, 2=centripetal)
211
+
212
+ Returns:
213
+ Interpolated frame
214
+ """
215
+ if not frames:
216
+ return coord3()
217
+ if len(frames) == 1:
218
+ return frames[0]
219
+
220
+ n = len(frames)
221
+
222
+ # Compute node vector
223
+ nodes = [0.0]
224
+
225
+ if curve_type == 0:
226
+ # Uniform parameterization
227
+ for i in range(1, n):
228
+ nodes.append(i / (n - 1))
229
+ else:
230
+ # Chord-length or centripetal parameterization
231
+ total_length = 0.0
232
+ segment_lengths = []
233
+
234
+ for i in range(n - 1):
235
+ dist = (frames[i+1].o - frames[i].o).length()
236
+ if curve_type == 2:
237
+ dist = math.sqrt(dist)
238
+ segment_lengths.append(dist)
239
+ total_length += dist
240
+
241
+ for i in range(1, n):
242
+ nodes.append(nodes[i-1] + segment_lengths[i-1] / total_length)
243
+
244
+ # Find the segment containing parameter t
245
+ segment_index = 0
246
+ for i in range(n - 1):
247
+ if nodes[i] <= t <= nodes[i+1]:
248
+ segment_index = i
249
+ break
250
+ if t >= nodes[n-1]:
251
+ segment_index = n - 2
252
+
253
+ # Local parameter
254
+ local_t = (t - nodes[segment_index]) / (nodes[segment_index+1] - nodes[segment_index])
255
+ local_t = max(0.0, min(1.0, local_t))
256
+
257
+ # SE(3) interpolation using slerp
258
+ return coord3.slerp(frames[segment_index], frames[segment_index+1], local_t)
259
+
260
+
261
+ def frame_field_spline_c2(frames: List[coord3], t: float) -> coord3:
262
+ """
263
+ C2-continuous frame field spline with high-order continuity
264
+ Uses Catmull-Rom for position and SQUAD for rotation
265
+
266
+ Args:
267
+ frames: Frame sequence (requires at least 4 frames)
268
+ t: Global parameter [0,1]
269
+
270
+ Returns:
271
+ Interpolated frame with C2 continuity
272
+ """
273
+ if len(frames) < 4:
274
+ # Fallback to C1 continuity when not enough frames
275
+ return frame_field_spline(frames, t, 1)
276
+
277
+ n = len(frames)
278
+
279
+ # Compute node vector using chord-length parameterization
280
+ nodes = [0.0]
281
+ total_length = 0.0
282
+ segment_lengths = []
283
+
284
+ for i in range(n - 1):
285
+ dist = (frames[i+1].o - frames[i].o).length()
286
+ segment_lengths.append(dist)
287
+ total_length += dist
288
+
289
+ for i in range(1, n):
290
+ nodes.append(nodes[i-1] + segment_lengths[i-1] / total_length)
291
+
292
+ # Find the segment containing parameter t
293
+ i = 0
294
+ for i in range(n - 1):
295
+ if t >= nodes[i] and t <= nodes[i+1]:
296
+ break
297
+ if t >= nodes[n-1]:
298
+ i = n - 2
299
+
300
+ # Local parameter
301
+ local_t = (t - nodes[i]) / (nodes[i+1] - nodes[i])
302
+ local_t = max(0.0, min(1.0, local_t))
303
+
304
+ # Get 4 control frames for interpolation
305
+ i0 = max(0, i - 1)
306
+ i1 = i
307
+ i2 = i + 1
308
+ i3 = min(n - 1, i + 2)
309
+
310
+ # Position using Catmull-Rom spline
311
+ pos = catmull_rom(frames[i0].o, frames[i1].o, frames[i2].o, frames[i3].o, local_t)
312
+
313
+ # Rotation using SQUAD interpolation
314
+ q0 = frames[i0].Q()
315
+ q1 = frames[i1].Q()
316
+ q2 = frames[i2].Q()
317
+ q3 = frames[i3].Q()
318
+
319
+ rot = squad_interp(q0, q1, q2, q3, local_t)
320
+
321
+ return coord3(pos, rot)
322
+
323
+
324
+ def reconstruct_curve_from_polygon(
325
+ polygon: List[vec3],
326
+ samples: int = 100,
327
+ curve_type: int = 1
328
+ ) -> List[vec3]:
329
+ """
330
+ Reconstruct curve from polygon vertices using frame field spline
331
+
332
+ Args:
333
+ polygon: Input polygon vertices
334
+ samples: Number of sample points
335
+ curve_type: Curve type (0=uniform, 1=chord-length, 2=centripetal, 3=C2-continuous)
336
+
337
+ Returns:
338
+ Reconstructed curve point sequence
339
+ """
340
+ if len(polygon) < 2:
341
+ return []
342
+
343
+ # Generate frame field from polygon vertices
344
+ frames = generate_frenet_frames(polygon)
345
+
346
+ # Interpolate and sample
347
+ curve_points = []
348
+ for i in range(samples):
349
+ t = i / (samples - 1)
350
+
351
+ if curve_type == 3:
352
+ # C2-continuous interpolation
353
+ interpolated_frame = frame_field_spline_c2(frames, t)
354
+ else:
355
+ # Standard interpolation
356
+ interpolated_frame = frame_field_spline(frames, t, curve_type)
357
+
358
+ curve_points.append(interpolated_frame.o)
359
+
360
+ return curve_points
361
+
362
+
363
+ def compute_curvature_profile(curve: List[vec3]) -> List[float]:
364
+ """
365
+ Compute curvature distribution of reconstructed curve
366
+
367
+ Args:
368
+ curve: Curve point sequence
369
+
370
+ Returns:
371
+ Curvature value sequence
372
+ """
373
+ if len(curve) < 3:
374
+ return []
375
+
376
+ curvatures = []
377
+
378
+ for i in range(1, len(curve) - 1):
379
+ v1 = curve[i] - curve[i-1]
380
+ v2 = curve[i+1] - curve[i]
381
+
382
+ # Calculate angle change
383
+ dot_product = v1.normalized().dot(v2.normalized())
384
+ dot_product = max(-1.0, min(1.0, dot_product))
385
+ delta_angle = math.acos(dot_product)
386
+
387
+ avg_length = (v1.length() + v2.length()) * 0.5
388
+
389
+ # Curvature = angle change rate / arc length
390
+ curvature = delta_angle / (avg_length + 1e-6)
391
+ curvatures.append(curvature)
392
+
393
+ # Use boundary values for endpoints
394
+ if curvatures:
395
+ curvatures.insert(0, curvatures[0])
396
+ curvatures.append(curvatures[-1])
397
+
398
+ return curvatures
399
+
400
+
401
+ # ========== Main Class ==========
402
+
403
+ class InterpolatedCurve:
404
+ """
405
+ Interpolated curve class - Encapsulates frame field interpolation functionality
406
+ """
407
+
408
+ def __init__(self, control_points: List[vec3], curve_type: int = 1, c2_continuity: bool = False):
409
+ """
410
+ Initialize interpolated curve
411
+
412
+ Args:
413
+ control_points: Control point list
414
+ curve_type: Curve type (0=uniform, 1=chord-length, 2=centripetal)
415
+ c2_continuity: Enable C2-continuous interpolation (requires 4+ points)
416
+ """
417
+ self.control_points = control_points
418
+ self.curve_type = curve_type
419
+ self.c2_continuity = c2_continuity
420
+ self.frames = generate_frenet_frames(control_points)
421
+
422
+ def evaluate(self, t: float) -> vec3:
423
+ """
424
+ Evaluate position at parameter t
425
+
426
+ Args:
427
+ t: Parameter value [0, 1]
428
+
429
+ Returns:
430
+ Point on curve
431
+ """
432
+ frame = self.evaluate_frame(t)
433
+ return frame.o
434
+
435
+ def evaluate_frame(self, t: float) -> coord3:
436
+ """
437
+ Evaluate complete frame at parameter t
438
+
439
+ Args:
440
+ t: Parameter value [0, 1]
441
+
442
+ Returns:
443
+ Frame on curve
444
+ """
445
+ if self.c2_continuity and len(self.frames) >= 4:
446
+ return frame_field_spline_c2(self.frames, t)
447
+ else:
448
+ return frame_field_spline(self.frames, t, self.curve_type)
449
+
450
+ def sample(self, num_samples: int = 100) -> List[vec3]:
451
+ """
452
+ Sample curve points
453
+
454
+ Args:
455
+ num_samples: Number of sample points
456
+
457
+ Returns:
458
+ Sampled point list
459
+ """
460
+ curve_points = []
461
+ for i in range(num_samples):
462
+ t = i / (num_samples - 1)
463
+ curve_points.append(self.evaluate(t))
464
+ return curve_points
465
+
466
+ def sample_frames(self, num_samples: int = 100) -> List[coord3]:
467
+ """
468
+ Sample frames
469
+
470
+ Args:
471
+ num_samples: Number of sample points
472
+
473
+ Returns:
474
+ Frame list
475
+ """
476
+ sampled_frames = []
477
+ for i in range(num_samples):
478
+ t = i / (num_samples - 1)
479
+ sampled_frames.append(self.evaluate_frame(t))
480
+ return sampled_frames
481
+
482
+ def get_curvature_profile(self, num_samples: int = 100) -> List[float]:
483
+ """
484
+ Get curvature distribution
485
+
486
+ Args:
487
+ num_samples: Number of sample points
488
+
489
+ Returns:
490
+ Curvature value list
491
+ """
492
+ curve_points = self.sample(num_samples)
493
+ return compute_curvature_profile(curve_points)
494
+
495
+
496
+ # ========== Export ==========
497
+
498
+ __all__ = [
499
+ 'generate_frenet_frames',
500
+ 'frame_field_spline',
501
+ 'frame_field_spline_c2',
502
+ 'reconstruct_curve_from_polygon',
503
+ 'compute_curvature_profile',
504
+ 'InterpolatedCurve',
505
+ 'catmull_rom',
506
+ 'squad_interp',
507
+ ]