sdf-sampler 0.1.0__tar.gz → 0.3.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 (38) hide show
  1. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/.gitignore +1 -0
  2. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/CHANGELOG.md +26 -0
  3. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/PKG-INFO +150 -39
  4. sdf_sampler-0.3.0/README.md +293 -0
  5. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/pyproject.toml +5 -2
  6. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/__init__.py +1 -1
  7. sdf_sampler-0.3.0/src/sdf_sampler/__main__.py +17 -0
  8. sdf_sampler-0.3.0/src/sdf_sampler/cli.py +681 -0
  9. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/sampler.py +62 -0
  10. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/uv.lock +1 -1
  11. sdf_sampler-0.1.0/.github_token.env +0 -1
  12. sdf_sampler-0.1.0/README.md +0 -182
  13. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/LICENSE +0 -0
  14. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/algorithms/__init__.py +0 -0
  15. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/algorithms/flood_fill.py +0 -0
  16. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/algorithms/normal_idw.py +0 -0
  17. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/algorithms/normal_offset.py +0 -0
  18. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/algorithms/pocket.py +0 -0
  19. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/algorithms/voxel_grid.py +0 -0
  20. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/algorithms/voxel_regions.py +0 -0
  21. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/analyzer.py +0 -0
  22. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/config.py +0 -0
  23. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/io.py +0 -0
  24. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/models/__init__.py +0 -0
  25. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/models/analysis.py +0 -0
  26. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/models/constraints.py +0 -0
  27. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/models/samples.py +0 -0
  28. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/sampling/__init__.py +0 -0
  29. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/sampling/box.py +0 -0
  30. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/sampling/brush.py +0 -0
  31. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/sampling/ray_carve.py +0 -0
  32. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/sampling/sphere.py +0 -0
  33. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/tests/__init__.py +0 -0
  34. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/tests/test_analyzer.py +0 -0
  35. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/tests/test_equivalence.py +0 -0
  36. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/tests/test_integration.py +0 -0
  37. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/tests/test_models.py +0 -0
  38. {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/tests/test_sampler.py +0 -0
@@ -79,3 +79,4 @@ dmypy.json
79
79
 
80
80
  # Secrets
81
81
  .pypi_token.env
82
+ *.env
@@ -5,6 +5,32 @@ All notable changes to sdf-sampler will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.0] - 2025-01-29
9
+
10
+ ### Added
11
+
12
+ - **Full parameter control** in CLI and SDK
13
+ - All analysis options exposed: `--min-gap-size`, `--cone-angle`, `--idw-sample-count`, etc.
14
+ - All sampling options exposed: `--samples-per-primitive`, `--inverse-square-falloff`, etc.
15
+ - Output mode control: `--flood-fill-output`, `--voxel-regions-output` (boxes/samples/both)
16
+ - **Surface point inclusion**
17
+ - `--include-surface-points` flag to include original points with phi=0
18
+ - `--surface-point-ratio` to control fraction included (default 10%)
19
+ - SDK: `sampler.generate(..., include_surface_points=True, surface_point_ratio=0.1)`
20
+
21
+ ## [0.2.0] - 2025-01-29
22
+
23
+ ### Added
24
+
25
+ - **Command-Line Interface** for batch processing
26
+ - `sdf-sampler pipeline` - Full workflow (analyze + sample + export)
27
+ - `sdf-sampler analyze` - Detect SOLID/EMPTY regions
28
+ - `sdf-sampler sample` - Generate training samples from constraints
29
+ - `sdf-sampler info` - Inspect point clouds, constraints, and sample files
30
+ - Support for `python -m sdf_sampler` invocation
31
+ - Console script entry point (`sdf-sampler` command)
32
+ - Comprehensive README with SDK and CLI documentation
33
+
8
34
  ## [0.1.0] - 2025-01-29
9
35
 
10
36
  ### Added
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sdf-sampler
3
- Version: 0.1.0
3
+ Version: 0.3.0
4
4
  Summary: Auto-analysis and sampling of point clouds for SDF (Signed Distance Field) training data generation
5
- Project-URL: Repository, https://github.com/chiark/sdf-sampler
5
+ Project-URL: Repository, https://github.com/Chiark-Collective/sdf-sampler
6
6
  Author-email: Liam <liam@example.com>
7
7
  License: MIT
8
8
  License-File: LICENSE
@@ -60,7 +60,88 @@ For additional I/O format support (PLY, LAS/LAZ):
60
60
  pip install sdf-sampler[io]
61
61
  ```
62
62
 
63
- ## Quick Start
63
+ ## Command-Line Interface
64
+
65
+ sdf-sampler provides a CLI for common workflows:
66
+
67
+ ```bash
68
+ # Run as module
69
+ python -m sdf_sampler --help
70
+
71
+ # Or use the installed command
72
+ sdf-sampler --help
73
+ ```
74
+
75
+ ### Commands
76
+
77
+ #### `pipeline` - Full workflow (recommended)
78
+
79
+ Run the complete pipeline: analyze point cloud → generate samples → export.
80
+
81
+ ```bash
82
+ # Basic usage
83
+ sdf-sampler pipeline scan.ply -o training_data.parquet
84
+
85
+ # With options
86
+ sdf-sampler pipeline scan.ply \
87
+ -o training_data.parquet \
88
+ -n 50000 \
89
+ -s inverse_square \
90
+ --save-constraints constraints.json \
91
+ -v
92
+ ```
93
+
94
+ Options:
95
+ - `-o, --output`: Output parquet file (default: `<input>_samples.parquet`)
96
+ - `-n, --total-samples`: Number of samples to generate (default: 10000)
97
+ - `-s, --strategy`: Sampling strategy: `constant`, `density`, `inverse_square` (default: `inverse_square`)
98
+ - `-a, --algorithms`: Specific algorithms to run (default: all)
99
+ - `--save-constraints`: Also save constraints to JSON
100
+ - `--seed`: Random seed for reproducibility
101
+ - `-v, --verbose`: Verbose output
102
+
103
+ #### `analyze` - Detect regions
104
+
105
+ Analyze a point cloud to detect SOLID/EMPTY regions.
106
+
107
+ ```bash
108
+ sdf-sampler analyze scan.ply -o constraints.json -v
109
+ ```
110
+
111
+ Options:
112
+ - `-o, --output`: Output JSON file (default: `<input>_constraints.json`)
113
+ - `-a, --algorithms`: Algorithms to run (see below)
114
+ - `--no-hull-filter`: Disable hull filtering
115
+ - `-v, --verbose`: Verbose output
116
+
117
+ #### `sample` - Generate training samples
118
+
119
+ Generate training samples from a constraints file.
120
+
121
+ ```bash
122
+ sdf-sampler sample scan.ply constraints.json -o samples.parquet -n 50000
123
+ ```
124
+
125
+ Options:
126
+ - `-o, --output`: Output parquet file
127
+ - `-n, --total-samples`: Number of samples (default: 10000)
128
+ - `-s, --strategy`: Sampling strategy (default: `inverse_square`)
129
+ - `--seed`: Random seed
130
+ - `-v, --verbose`: Verbose output
131
+
132
+ #### `info` - Inspect files
133
+
134
+ Show information about point clouds, constraints, or sample files.
135
+
136
+ ```bash
137
+ sdf-sampler info scan.ply
138
+ sdf-sampler info constraints.json
139
+ sdf-sampler info samples.parquet
140
+ ```
141
+
142
+ ## Python SDK
143
+
144
+ ### Quick Start
64
145
 
65
146
  ```python
66
147
  from sdf_sampler import SDFAnalyzer, SDFSampler, load_point_cloud
@@ -86,28 +167,13 @@ samples = sampler.generate(
86
167
  sampler.export_parquet(samples, "training_data.parquet")
87
168
  ```
88
169
 
89
- ## Features
90
-
91
- ### Auto-Analysis Algorithms
92
-
93
- - **flood_fill**: Detects EMPTY (outside) regions by ray propagation from sky
94
- - **voxel_regions**: Detects SOLID (underground) regions
95
- - **normal_offset**: Generates paired SOLID/EMPTY boxes along surface normals
96
- - **normal_idw**: Inverse distance weighted sampling along normals
97
- - **pocket**: Detects interior cavities
98
-
99
- ### Sampling Strategies
100
-
101
- - **CONSTANT**: Fixed number of samples per constraint
102
- - **DENSITY**: Samples proportional to constraint volume
103
- - **INVERSE_SQUARE**: More samples near surface, fewer far away (recommended)
104
-
105
- ## API Reference
106
-
107
170
  ### SDFAnalyzer
108
171
 
172
+ Analyzes point clouds to detect SOLID and EMPTY regions.
173
+
109
174
  ```python
110
- from sdf_sampler import SDFAnalyzer, AnalyzerConfig
175
+ from sdf_sampler import SDFAnalyzer
176
+ from sdf_sampler.config import AnalyzerConfig, AutoAnalysisOptions
111
177
 
112
178
  # With default config
113
179
  analyzer = SDFAnalyzer()
@@ -136,10 +202,23 @@ print(f"EMPTY: {result.summary.empty_constraints}")
136
202
  constraints = result.constraints
137
203
  ```
138
204
 
205
+ #### Analysis Algorithms
206
+
207
+ | Algorithm | Description | Output |
208
+ |-----------|-------------|--------|
209
+ | `flood_fill` | Detects EMPTY (outside) regions by ray propagation from sky | Box or SamplePoint constraints |
210
+ | `voxel_regions` | Detects SOLID (underground) regions | Box or SamplePoint constraints |
211
+ | `normal_offset` | Generates paired SOLID/EMPTY boxes along surface normals | Box constraints |
212
+ | `normal_idw` | Inverse distance weighted sampling along normals | SamplePoint constraints |
213
+ | `pocket` | Detects interior cavities | Pocket constraints |
214
+
139
215
  ### SDFSampler
140
216
 
217
+ Generates training samples from constraints.
218
+
141
219
  ```python
142
- from sdf_sampler import SDFSampler, SamplerConfig
220
+ from sdf_sampler import SDFSampler
221
+ from sdf_sampler.config import SamplerConfig
143
222
 
144
223
  # With default config
145
224
  sampler = SDFSampler()
@@ -167,6 +246,14 @@ sampler.export_parquet(samples, "output.parquet")
167
246
  df = sampler.to_dataframe(samples)
168
247
  ```
169
248
 
249
+ #### Sampling Strategies
250
+
251
+ | Strategy | Description |
252
+ |----------|-------------|
253
+ | `constant` | Fixed number of samples per constraint |
254
+ | `density` | Samples proportional to constraint volume |
255
+ | `inverse_square` | More samples near surface, fewer far away (recommended) |
256
+
170
257
  ### Constraint Types
171
258
 
172
259
  The analyzer generates various constraint types:
@@ -180,6 +267,22 @@ Each constraint has:
180
267
  - `sign`: "solid" (negative SDF) or "empty" (positive SDF)
181
268
  - `weight`: Sample weight (default 1.0)
182
269
 
270
+ ### I/O Helpers
271
+
272
+ ```python
273
+ from sdf_sampler import load_point_cloud, export_parquet
274
+
275
+ # Load various formats
276
+ xyz, normals = load_point_cloud("scan.ply") # PLY (requires trimesh)
277
+ xyz, normals = load_point_cloud("scan.las") # LAS/LAZ (requires laspy)
278
+ xyz, normals = load_point_cloud("scan.csv") # CSV with x,y,z columns
279
+ xyz, normals = load_point_cloud("scan.npz") # NumPy archive
280
+ xyz, normals = load_point_cloud("scan.parquet") # Parquet
281
+
282
+ # Export samples
283
+ export_parquet(samples, "output.parquet")
284
+ ```
285
+
183
286
  ## Output Format
184
287
 
185
288
  The exported parquet file contains columns:
@@ -194,32 +297,40 @@ The exported parquet file contains columns:
194
297
  | is_surface | bool | Whether sample is on surface |
195
298
  | is_free | bool | Whether sample is in free space (EMPTY) |
196
299
 
197
- ## Configuration Options
300
+ ## Configuration Reference
198
301
 
199
302
  ### AnalyzerConfig
200
303
 
201
304
  | Option | Default | Description |
202
305
  |--------|---------|-------------|
203
- | min_gap_size | 0.10 | Minimum gap size for flood fill (meters) |
204
- | max_grid_dim | 200 | Maximum voxel grid dimension |
205
- | cone_angle | 15.0 | Ray propagation cone half-angle (degrees) |
206
- | normal_offset_pairs | 40 | Number of box pairs for normal_offset |
207
- | idw_sample_count | 1000 | Total IDW samples |
208
- | idw_max_distance | 0.5 | Maximum IDW distance (meters) |
209
- | hull_filter_enabled | True | Filter outside X-Y alpha shape |
210
- | hull_alpha | 1.0 | Alpha shape parameter |
306
+ | `min_gap_size` | 0.10 | Minimum gap size for flood fill (meters) |
307
+ | `max_grid_dim` | 200 | Maximum voxel grid dimension |
308
+ | `cone_angle` | 15.0 | Ray propagation cone half-angle (degrees) |
309
+ | `normal_offset_pairs` | 40 | Number of box pairs for normal_offset |
310
+ | `idw_sample_count` | 1000 | Total IDW samples |
311
+ | `idw_max_distance` | 0.5 | Maximum IDW distance (meters) |
312
+ | `hull_filter_enabled` | True | Filter outside X-Y alpha shape |
313
+ | `hull_alpha` | 1.0 | Alpha shape parameter |
211
314
 
212
315
  ### SamplerConfig
213
316
 
214
317
  | Option | Default | Description |
215
318
  |--------|---------|-------------|
216
- | total_samples | 10000 | Default total samples |
217
- | samples_per_primitive | 100 | Samples per constraint (CONSTANT) |
218
- | samples_per_cubic_meter | 10000 | Sample density (DENSITY) |
219
- | inverse_square_base_samples | 100 | Base samples (INVERSE_SQUARE) |
220
- | inverse_square_falloff | 2.0 | Falloff exponent |
221
- | near_band | 0.02 | Near-band width |
222
- | seed | 0 | Random seed |
319
+ | `total_samples` | 10000 | Default total samples |
320
+ | `samples_per_primitive` | 100 | Samples per constraint (CONSTANT) |
321
+ | `samples_per_cubic_meter` | 10000 | Sample density (DENSITY) |
322
+ | `inverse_square_base_samples` | 100 | Base samples (INVERSE_SQUARE) |
323
+ | `inverse_square_falloff` | 2.0 | Falloff exponent |
324
+ | `near_band` | 0.02 | Near-band width |
325
+ | `seed` | 0 | Random seed |
326
+
327
+ ## Integration with Ubik
328
+
329
+ sdf-sampler is the core analysis engine for [Ubik](https://github.com/Chiark-Collective/ubik), an interactive web application for SDF labeling. Use sdf-sampler directly for:
330
+
331
+ - Automated batch processing pipelines
332
+ - Integration into ML training workflows
333
+ - Custom analysis scripts
223
334
 
224
335
  ## License
225
336
 
@@ -0,0 +1,293 @@
1
+ # sdf-sampler
2
+
3
+ Auto-analysis and sampling of point clouds for SDF (Signed Distance Field) training data generation.
4
+
5
+ A lightweight, standalone Python package for generating SDF training hints from point clouds. Automatically detects SOLID (inside) and EMPTY (outside) regions and generates training samples suitable for SDF regression models.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install sdf-sampler
11
+ ```
12
+
13
+ For additional I/O format support (PLY, LAS/LAZ):
14
+
15
+ ```bash
16
+ pip install sdf-sampler[io]
17
+ ```
18
+
19
+ ## Command-Line Interface
20
+
21
+ sdf-sampler provides a CLI for common workflows:
22
+
23
+ ```bash
24
+ # Run as module
25
+ python -m sdf_sampler --help
26
+
27
+ # Or use the installed command
28
+ sdf-sampler --help
29
+ ```
30
+
31
+ ### Commands
32
+
33
+ #### `pipeline` - Full workflow (recommended)
34
+
35
+ Run the complete pipeline: analyze point cloud → generate samples → export.
36
+
37
+ ```bash
38
+ # Basic usage
39
+ sdf-sampler pipeline scan.ply -o training_data.parquet
40
+
41
+ # With options
42
+ sdf-sampler pipeline scan.ply \
43
+ -o training_data.parquet \
44
+ -n 50000 \
45
+ -s inverse_square \
46
+ --save-constraints constraints.json \
47
+ -v
48
+ ```
49
+
50
+ Options:
51
+ - `-o, --output`: Output parquet file (default: `<input>_samples.parquet`)
52
+ - `-n, --total-samples`: Number of samples to generate (default: 10000)
53
+ - `-s, --strategy`: Sampling strategy: `constant`, `density`, `inverse_square` (default: `inverse_square`)
54
+ - `-a, --algorithms`: Specific algorithms to run (default: all)
55
+ - `--save-constraints`: Also save constraints to JSON
56
+ - `--seed`: Random seed for reproducibility
57
+ - `-v, --verbose`: Verbose output
58
+
59
+ #### `analyze` - Detect regions
60
+
61
+ Analyze a point cloud to detect SOLID/EMPTY regions.
62
+
63
+ ```bash
64
+ sdf-sampler analyze scan.ply -o constraints.json -v
65
+ ```
66
+
67
+ Options:
68
+ - `-o, --output`: Output JSON file (default: `<input>_constraints.json`)
69
+ - `-a, --algorithms`: Algorithms to run (see below)
70
+ - `--no-hull-filter`: Disable hull filtering
71
+ - `-v, --verbose`: Verbose output
72
+
73
+ #### `sample` - Generate training samples
74
+
75
+ Generate training samples from a constraints file.
76
+
77
+ ```bash
78
+ sdf-sampler sample scan.ply constraints.json -o samples.parquet -n 50000
79
+ ```
80
+
81
+ Options:
82
+ - `-o, --output`: Output parquet file
83
+ - `-n, --total-samples`: Number of samples (default: 10000)
84
+ - `-s, --strategy`: Sampling strategy (default: `inverse_square`)
85
+ - `--seed`: Random seed
86
+ - `-v, --verbose`: Verbose output
87
+
88
+ #### `info` - Inspect files
89
+
90
+ Show information about point clouds, constraints, or sample files.
91
+
92
+ ```bash
93
+ sdf-sampler info scan.ply
94
+ sdf-sampler info constraints.json
95
+ sdf-sampler info samples.parquet
96
+ ```
97
+
98
+ ## Python SDK
99
+
100
+ ### Quick Start
101
+
102
+ ```python
103
+ from sdf_sampler import SDFAnalyzer, SDFSampler, load_point_cloud
104
+
105
+ # 1. Load point cloud (supports PLY, LAS, CSV, NPZ, Parquet)
106
+ xyz, normals = load_point_cloud("scan.ply")
107
+
108
+ # 2. Auto-analyze to detect EMPTY/SOLID regions
109
+ analyzer = SDFAnalyzer()
110
+ result = analyzer.analyze(xyz=xyz, normals=normals)
111
+ print(f"Generated {len(result.constraints)} constraints")
112
+
113
+ # 3. Generate training samples
114
+ sampler = SDFSampler()
115
+ samples = sampler.generate(
116
+ xyz=xyz,
117
+ constraints=result.constraints,
118
+ strategy="inverse_square",
119
+ total_samples=50000,
120
+ )
121
+
122
+ # 4. Export to parquet
123
+ sampler.export_parquet(samples, "training_data.parquet")
124
+ ```
125
+
126
+ ### SDFAnalyzer
127
+
128
+ Analyzes point clouds to detect SOLID and EMPTY regions.
129
+
130
+ ```python
131
+ from sdf_sampler import SDFAnalyzer
132
+ from sdf_sampler.config import AnalyzerConfig, AutoAnalysisOptions
133
+
134
+ # With default config
135
+ analyzer = SDFAnalyzer()
136
+
137
+ # With custom config
138
+ analyzer = SDFAnalyzer(config=AnalyzerConfig(
139
+ min_gap_size=0.10, # Minimum gap for flood fill
140
+ max_grid_dim=200, # Maximum voxel grid dimension
141
+ cone_angle=15.0, # Ray propagation cone angle
142
+ hull_filter_enabled=True, # Filter outside X-Y hull
143
+ ))
144
+
145
+ # Run analysis
146
+ result = analyzer.analyze(
147
+ xyz=xyz, # (N, 3) point positions
148
+ normals=normals, # (N, 3) point normals (optional)
149
+ algorithms=["flood_fill", "voxel_regions"], # Which algorithms to run
150
+ )
151
+
152
+ # Access results
153
+ print(f"Total constraints: {result.summary.total_constraints}")
154
+ print(f"SOLID: {result.summary.solid_constraints}")
155
+ print(f"EMPTY: {result.summary.empty_constraints}")
156
+
157
+ # Get constraint dicts for sampling
158
+ constraints = result.constraints
159
+ ```
160
+
161
+ #### Analysis Algorithms
162
+
163
+ | Algorithm | Description | Output |
164
+ |-----------|-------------|--------|
165
+ | `flood_fill` | Detects EMPTY (outside) regions by ray propagation from sky | Box or SamplePoint constraints |
166
+ | `voxel_regions` | Detects SOLID (underground) regions | Box or SamplePoint constraints |
167
+ | `normal_offset` | Generates paired SOLID/EMPTY boxes along surface normals | Box constraints |
168
+ | `normal_idw` | Inverse distance weighted sampling along normals | SamplePoint constraints |
169
+ | `pocket` | Detects interior cavities | Pocket constraints |
170
+
171
+ ### SDFSampler
172
+
173
+ Generates training samples from constraints.
174
+
175
+ ```python
176
+ from sdf_sampler import SDFSampler
177
+ from sdf_sampler.config import SamplerConfig
178
+
179
+ # With default config
180
+ sampler = SDFSampler()
181
+
182
+ # With custom config
183
+ sampler = SDFSampler(config=SamplerConfig(
184
+ total_samples=10000,
185
+ inverse_square_base_samples=100,
186
+ inverse_square_falloff=2.0,
187
+ near_band=0.02,
188
+ ))
189
+
190
+ # Generate samples
191
+ samples = sampler.generate(
192
+ xyz=xyz, # Point cloud for distance computation
193
+ constraints=constraints, # From analyzer.analyze().constraints
194
+ strategy="inverse_square", # Sampling strategy
195
+ seed=42, # For reproducibility
196
+ )
197
+
198
+ # Export
199
+ sampler.export_parquet(samples, "output.parquet")
200
+
201
+ # Or get DataFrame
202
+ df = sampler.to_dataframe(samples)
203
+ ```
204
+
205
+ #### Sampling Strategies
206
+
207
+ | Strategy | Description |
208
+ |----------|-------------|
209
+ | `constant` | Fixed number of samples per constraint |
210
+ | `density` | Samples proportional to constraint volume |
211
+ | `inverse_square` | More samples near surface, fewer far away (recommended) |
212
+
213
+ ### Constraint Types
214
+
215
+ The analyzer generates various constraint types:
216
+
217
+ - **BoxConstraint**: Axis-aligned bounding box
218
+ - **SphereConstraint**: Spherical region
219
+ - **SamplePointConstraint**: Direct point with signed distance
220
+ - **PocketConstraint**: Detected cavity region
221
+
222
+ Each constraint has:
223
+ - `sign`: "solid" (negative SDF) or "empty" (positive SDF)
224
+ - `weight`: Sample weight (default 1.0)
225
+
226
+ ### I/O Helpers
227
+
228
+ ```python
229
+ from sdf_sampler import load_point_cloud, export_parquet
230
+
231
+ # Load various formats
232
+ xyz, normals = load_point_cloud("scan.ply") # PLY (requires trimesh)
233
+ xyz, normals = load_point_cloud("scan.las") # LAS/LAZ (requires laspy)
234
+ xyz, normals = load_point_cloud("scan.csv") # CSV with x,y,z columns
235
+ xyz, normals = load_point_cloud("scan.npz") # NumPy archive
236
+ xyz, normals = load_point_cloud("scan.parquet") # Parquet
237
+
238
+ # Export samples
239
+ export_parquet(samples, "output.parquet")
240
+ ```
241
+
242
+ ## Output Format
243
+
244
+ The exported parquet file contains columns:
245
+
246
+ | Column | Type | Description |
247
+ |--------|------|-------------|
248
+ | x, y, z | float | 3D position |
249
+ | phi | float | Signed distance (negative=solid, positive=empty) |
250
+ | nx, ny, nz | float | Normal vector (if available) |
251
+ | weight | float | Sample weight |
252
+ | source | string | Sample origin (e.g., "box_solid", "flood_fill_empty") |
253
+ | is_surface | bool | Whether sample is on surface |
254
+ | is_free | bool | Whether sample is in free space (EMPTY) |
255
+
256
+ ## Configuration Reference
257
+
258
+ ### AnalyzerConfig
259
+
260
+ | Option | Default | Description |
261
+ |--------|---------|-------------|
262
+ | `min_gap_size` | 0.10 | Minimum gap size for flood fill (meters) |
263
+ | `max_grid_dim` | 200 | Maximum voxel grid dimension |
264
+ | `cone_angle` | 15.0 | Ray propagation cone half-angle (degrees) |
265
+ | `normal_offset_pairs` | 40 | Number of box pairs for normal_offset |
266
+ | `idw_sample_count` | 1000 | Total IDW samples |
267
+ | `idw_max_distance` | 0.5 | Maximum IDW distance (meters) |
268
+ | `hull_filter_enabled` | True | Filter outside X-Y alpha shape |
269
+ | `hull_alpha` | 1.0 | Alpha shape parameter |
270
+
271
+ ### SamplerConfig
272
+
273
+ | Option | Default | Description |
274
+ |--------|---------|-------------|
275
+ | `total_samples` | 10000 | Default total samples |
276
+ | `samples_per_primitive` | 100 | Samples per constraint (CONSTANT) |
277
+ | `samples_per_cubic_meter` | 10000 | Sample density (DENSITY) |
278
+ | `inverse_square_base_samples` | 100 | Base samples (INVERSE_SQUARE) |
279
+ | `inverse_square_falloff` | 2.0 | Falloff exponent |
280
+ | `near_band` | 0.02 | Near-band width |
281
+ | `seed` | 0 | Random seed |
282
+
283
+ ## Integration with Ubik
284
+
285
+ sdf-sampler is the core analysis engine for [Ubik](https://github.com/Chiark-Collective/ubik), an interactive web application for SDF labeling. Use sdf-sampler directly for:
286
+
287
+ - Automated batch processing pipelines
288
+ - Integration into ML training workflows
289
+ - Custom analysis scripts
290
+
291
+ ## License
292
+
293
+ MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sdf-sampler"
3
- version = "0.1.0"
3
+ version = "0.3.0"
4
4
  description = "Auto-analysis and sampling of point clouds for SDF (Signed Distance Field) training data generation"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -42,8 +42,11 @@ dev = [
42
42
  ]
43
43
  all = ["sdf-sampler[io,dev]"]
44
44
 
45
+ [project.scripts]
46
+ sdf-sampler = "sdf_sampler.cli:main"
47
+
45
48
  [project.urls]
46
- Repository = "https://github.com/chiark/sdf-sampler"
49
+ Repository = "https://github.com/Chiark-Collective/sdf-sampler"
47
50
 
48
51
  [build-system]
49
52
  requires = ["hatchling"]
@@ -47,7 +47,7 @@ from sdf_sampler.models import (
47
47
  )
48
48
  from sdf_sampler.sampler import SDFSampler
49
49
 
50
- __version__ = "0.1.0"
50
+ __version__ = "0.3.0"
51
51
 
52
52
  __all__ = [
53
53
  # Main classes
@@ -0,0 +1,17 @@
1
+ # ABOUTME: Entry point for running sdf-sampler as a module
2
+ # ABOUTME: Enables `python -m sdf_sampler` invocation
3
+
4
+ """
5
+ Run sdf-sampler as a module.
6
+
7
+ Usage:
8
+ python -m sdf_sampler --help
9
+ python -m sdf_sampler analyze input.ply -o constraints.json
10
+ python -m sdf_sampler sample input.ply constraints.json -o samples.parquet
11
+ python -m sdf_sampler pipeline input.ply -o samples.parquet
12
+ """
13
+
14
+ from sdf_sampler.cli import main
15
+
16
+ if __name__ == "__main__":
17
+ raise SystemExit(main())