sdf-sampler 0.5.0__tar.gz → 0.6.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.
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/PKG-INFO +1 -1
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/pyproject.toml +1 -1
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/sampling/box.py +8 -4
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/tests/test_sampler.py +158 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/.gitignore +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/CHANGELOG.md +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/LICENSE +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/README.md +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/__init__.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/__main__.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/algorithms/__init__.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/algorithms/flood_fill.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/algorithms/normal_idw.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/algorithms/normal_offset.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/algorithms/pocket.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/algorithms/voxel_grid.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/algorithms/voxel_regions.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/analyzer.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/cli.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/config.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/io.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/models/__init__.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/models/analysis.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/models/constraints.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/models/samples.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/sampler.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/sampling/__init__.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/sampling/brush.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/sampling/ray_carve.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/src/sdf_sampler/sampling/sphere.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/tests/__init__.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/tests/test_analyzer.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/tests/test_equivalence.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/tests/test_integration.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/tests/test_models.py +0 -0
- {sdf_sampler-0.5.0 → sdf_sampler-0.6.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sdf-sampler
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Auto-analysis and sampling of point clouds for SDF (Signed Distance Field) training data generation
|
|
5
5
|
Project-URL: Repository, https://github.com/Chiark-Collective/sdf-sampler
|
|
6
6
|
Author-email: Liam <liam@example.com>
|
|
@@ -80,17 +80,18 @@ def sample_box_inverse_square(
|
|
|
80
80
|
"""Generate samples from a box with inverse-square density distribution.
|
|
81
81
|
|
|
82
82
|
Samples more points near the surface (point cloud) and fewer far away.
|
|
83
|
+
Uses actual distance to surface for phi values (signed distance).
|
|
83
84
|
|
|
84
85
|
Args:
|
|
85
86
|
constraint: Box constraint to sample
|
|
86
87
|
rng: Random number generator
|
|
87
|
-
near_band: Near-band width for
|
|
88
|
+
near_band: Near-band width for density weighting (not phi assignment)
|
|
88
89
|
n_samples: Number of samples to generate
|
|
89
90
|
surface_tree: KDTree of surface points for distance computation
|
|
90
91
|
falloff: Falloff exponent (higher = faster falloff)
|
|
91
92
|
|
|
92
93
|
Returns:
|
|
93
|
-
List of TrainingSample objects
|
|
94
|
+
List of TrainingSample objects with phi = actual signed distance to surface
|
|
94
95
|
"""
|
|
95
96
|
samples = []
|
|
96
97
|
center = np.array(constraint.center)
|
|
@@ -109,8 +110,11 @@ def sample_box_inverse_square(
|
|
|
109
110
|
weight = (near_band / min_dist) ** falloff
|
|
110
111
|
|
|
111
112
|
if rng.random() < min(1.0, weight):
|
|
112
|
-
|
|
113
|
-
phi
|
|
113
|
+
# Use actual distance to surface for phi, with sign based on constraint type
|
|
114
|
+
# EMPTY regions have positive phi (outside surface)
|
|
115
|
+
# SOLID regions have negative phi (inside surface)
|
|
116
|
+
sign = 1.0 if constraint.sign == SignConvention.EMPTY else -1.0
|
|
117
|
+
phi = sign * float(dist_to_surface)
|
|
114
118
|
|
|
115
119
|
samples.append(
|
|
116
120
|
TrainingSample(
|
|
@@ -229,3 +229,161 @@ class TestSamplerSignConvention:
|
|
|
229
229
|
for s in samples:
|
|
230
230
|
assert s.phi > 0, f"EMPTY sample should have positive phi, got {s.phi}"
|
|
231
231
|
assert s.is_free
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class TestBoxInverseSquarePhiValues:
|
|
235
|
+
"""Tests that box inverse_square samples use actual distance to surface for phi."""
|
|
236
|
+
|
|
237
|
+
@pytest.fixture
|
|
238
|
+
def plane_surface(self):
|
|
239
|
+
"""Flat plane at z=0 for easy distance calculation."""
|
|
240
|
+
x = np.linspace(-2, 2, 50)
|
|
241
|
+
y = np.linspace(-2, 2, 50)
|
|
242
|
+
xx, yy = np.meshgrid(x, y)
|
|
243
|
+
xyz = np.column_stack([xx.ravel(), yy.ravel(), np.zeros(2500)])
|
|
244
|
+
return xyz
|
|
245
|
+
|
|
246
|
+
@pytest.fixture
|
|
247
|
+
def box_above_plane(self):
|
|
248
|
+
"""Box constraint above z=0 plane (empty region above surface)."""
|
|
249
|
+
return {
|
|
250
|
+
"type": "box",
|
|
251
|
+
"sign": "empty", # Above surface = empty/positive phi
|
|
252
|
+
"center": (0.0, 0.0, 0.5), # Center at z=0.5
|
|
253
|
+
"half_extents": (1.0, 1.0, 0.5), # Extends from z=0 to z=1
|
|
254
|
+
"weight": 1.0,
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@pytest.fixture
|
|
258
|
+
def box_below_plane(self):
|
|
259
|
+
"""Box constraint below z=0 plane (solid region below surface)."""
|
|
260
|
+
return {
|
|
261
|
+
"type": "box",
|
|
262
|
+
"sign": "solid", # Below surface = solid/negative phi
|
|
263
|
+
"center": (0.0, 0.0, -0.5), # Center at z=-0.5
|
|
264
|
+
"half_extents": (1.0, 1.0, 0.5), # Extends from z=-1 to z=0
|
|
265
|
+
"weight": 1.0,
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
def test_inverse_square_phi_is_actual_distance(self, plane_surface, box_above_plane):
|
|
269
|
+
"""Verify phi is based on distance to nearest surface point, not constant near_band."""
|
|
270
|
+
sampler = SDFSampler(config=SamplerConfig(total_samples=500))
|
|
271
|
+
samples = sampler.generate(
|
|
272
|
+
xyz=plane_surface,
|
|
273
|
+
constraints=[box_above_plane],
|
|
274
|
+
strategy=SamplingStrategy.INVERSE_SQUARE,
|
|
275
|
+
seed=42,
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
assert len(samples) > 0, "Should generate samples"
|
|
279
|
+
|
|
280
|
+
for s in samples:
|
|
281
|
+
# For a flat plane at z=0, distance to surface is approximately |z|
|
|
282
|
+
# (exact value depends on nearest point in the discrete point cloud)
|
|
283
|
+
z_distance = abs(s.z)
|
|
284
|
+
|
|
285
|
+
# phi should be approximately |z| (within grid spacing tolerance)
|
|
286
|
+
# Grid spacing is ~0.08 units, so allow some tolerance
|
|
287
|
+
assert abs(s.phi - z_distance) < 0.1, (
|
|
288
|
+
f"phi should be approximately equal to |z| distance. "
|
|
289
|
+
f"Got phi={s.phi}, z={s.z}, expected ~{z_distance}"
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# phi should definitely be positive for empty constraint
|
|
293
|
+
assert s.phi > 0, f"EMPTY sample should have positive phi, got {s.phi}"
|
|
294
|
+
|
|
295
|
+
def test_inverse_square_phi_varies_with_distance(self, plane_surface, box_above_plane):
|
|
296
|
+
"""Verify phi values vary based on sample distance from surface."""
|
|
297
|
+
sampler = SDFSampler(config=SamplerConfig(total_samples=500))
|
|
298
|
+
samples = sampler.generate(
|
|
299
|
+
xyz=plane_surface,
|
|
300
|
+
constraints=[box_above_plane],
|
|
301
|
+
strategy=SamplingStrategy.INVERSE_SQUARE,
|
|
302
|
+
seed=42,
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
phi_values = [s.phi for s in samples]
|
|
306
|
+
|
|
307
|
+
# Phi should vary (not be constant ±near_band)
|
|
308
|
+
phi_std = np.std(phi_values)
|
|
309
|
+
assert phi_std > 0.01, (
|
|
310
|
+
f"phi values should vary with distance, got std={phi_std}. "
|
|
311
|
+
"This suggests phi is constant (bug: using near_band instead of distance)"
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Should have a range of values, not just near_band=0.02
|
|
315
|
+
phi_min, phi_max = min(phi_values), max(phi_values)
|
|
316
|
+
phi_range = phi_max - phi_min
|
|
317
|
+
assert phi_range > 0.1, (
|
|
318
|
+
f"phi range should be > 0.1, got {phi_range}. "
|
|
319
|
+
"Values: min={phi_min}, max={phi_max}"
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
def test_inverse_square_solid_has_negative_phi(self, plane_surface, box_below_plane):
|
|
323
|
+
"""Verify solid box samples have negative phi with magnitude proportional to distance."""
|
|
324
|
+
sampler = SDFSampler(config=SamplerConfig(total_samples=500))
|
|
325
|
+
samples = sampler.generate(
|
|
326
|
+
xyz=plane_surface,
|
|
327
|
+
constraints=[box_below_plane],
|
|
328
|
+
strategy=SamplingStrategy.INVERSE_SQUARE,
|
|
329
|
+
seed=42,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
for s in samples:
|
|
333
|
+
# SOLID constraint should always have negative phi
|
|
334
|
+
assert s.phi < 0, f"SOLID sample should have negative phi, got {s.phi}"
|
|
335
|
+
|
|
336
|
+
# For flat plane at z=0, solid samples are at z<0
|
|
337
|
+
# Distance to nearest surface point is approximately |z|
|
|
338
|
+
z_distance = abs(s.z)
|
|
339
|
+
|
|
340
|
+
# phi magnitude should be approximately |z| (within grid spacing tolerance)
|
|
341
|
+
assert abs(abs(s.phi) - z_distance) < 0.1, (
|
|
342
|
+
f"phi magnitude should be approximately |z|. "
|
|
343
|
+
f"Got phi={s.phi}, z={s.z}, expected ~{-z_distance}"
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
def test_inverse_square_phi_correlates_with_z_coordinate(self, plane_surface, box_above_plane):
|
|
347
|
+
"""For plane at z=0, phi should be correlated with |z| coordinate."""
|
|
348
|
+
sampler = SDFSampler(config=SamplerConfig(total_samples=200))
|
|
349
|
+
samples = sampler.generate(
|
|
350
|
+
xyz=plane_surface,
|
|
351
|
+
constraints=[box_above_plane],
|
|
352
|
+
strategy=SamplingStrategy.INVERSE_SQUARE,
|
|
353
|
+
seed=123,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
# Collect z values and phi values
|
|
357
|
+
z_values = np.array([abs(s.z) for s in samples])
|
|
358
|
+
phi_values = np.array([s.phi for s in samples])
|
|
359
|
+
|
|
360
|
+
# phi should be positively correlated with |z|
|
|
361
|
+
# (samples further from z=0 should have larger phi)
|
|
362
|
+
correlation = np.corrcoef(z_values, phi_values)[0, 1]
|
|
363
|
+
assert correlation > 0.9, (
|
|
364
|
+
f"phi should be strongly correlated with |z|. "
|
|
365
|
+
f"Got correlation={correlation:.3f}"
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
def test_inverse_square_not_constant_near_band(self, plane_surface, box_above_plane):
|
|
369
|
+
"""Explicitly verify phi is NOT the constant near_band value."""
|
|
370
|
+
near_band = 0.02 # Default near_band value
|
|
371
|
+
sampler = SDFSampler(config=SamplerConfig(total_samples=200, near_band=near_band))
|
|
372
|
+
samples = sampler.generate(
|
|
373
|
+
xyz=plane_surface,
|
|
374
|
+
constraints=[box_above_plane],
|
|
375
|
+
strategy=SamplingStrategy.INVERSE_SQUARE,
|
|
376
|
+
seed=42,
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
# Count how many samples have phi approximately equal to near_band
|
|
380
|
+
near_band_count = sum(1 for s in samples if abs(abs(s.phi) - near_band) < 0.001)
|
|
381
|
+
total = len(samples)
|
|
382
|
+
|
|
383
|
+
# With actual distance-based phi, very few samples should be exactly at near_band
|
|
384
|
+
# (only those that happen to be exactly 0.02 away from surface)
|
|
385
|
+
ratio = near_band_count / total
|
|
386
|
+
assert ratio < 0.1, (
|
|
387
|
+
f"{near_band_count}/{total} ({ratio:.0%}) samples have phi≈±near_band. "
|
|
388
|
+
"This suggests phi is still using constant near_band instead of actual distance."
|
|
389
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|