morphit-rs 0.2.0__tar.gz → 0.2.1__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.
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/Cargo.lock +8 -8
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/Cargo.toml +1 -1
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/PKG-INFO +1 -1
- {morphit_rs-0.2.0/crates/morphit-robot → morphit_rs-0.2.1/crates/morphit}/README.md +13 -4
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/config.rs +8 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/mesh_prep.rs +8 -5
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/state.rs +38 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/trainer.rs +63 -5
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/src/session.rs +3 -2
- {morphit_rs-0.2.0/crates/morphit → morphit_rs-0.2.1/crates/morphit-robot}/README.md +13 -4
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/src/config.rs +11 -4
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/python/morphit_rs/compat.py +2 -1
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/README.md +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/Cargo.toml +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/contains.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/density.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/device.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/distances.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/error.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/exec.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/lib.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/loss/boundary.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/loss/containment.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/loss/coverage.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/loss/fd.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/loss/hausdorff.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/loss/mesh_containment.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/loss/mod.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/loss/overlap.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/loss/physics.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/loss/sqem.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/loss/surface.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/math.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/mesh.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/mesh_io/dae.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/mesh_io/mod.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/mesh_io/ply.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/mesh_io/write.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/metrics.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/optim.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/par.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/quality.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/result.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/sampling.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/search/gpu/mod.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/search/gpu/search.wgsl +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/search/gpu/session.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/search/gpu/tests.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/search/mod.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/src/shapes.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/link0.obj +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/mesh_prep/disjoint.obj +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/mesh_prep/groups_box.obj +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/mesh_prep/inward_inner_shell.obj +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/mesh_prep/open_overlap.obj +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/mesh_prep/sliver.obj +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/mesh_prep/three_bodies.obj +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/mesh_prep/two_overlapping.obj +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/py_link0_b_n16.json +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/py_link0_obj_n16.json +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/py_link0_objmass_n16.json +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/py_mesh_prep.json +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/mesh_prep_parity.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/python_parity.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/web_examples.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/Cargo.toml +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/README.md +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/src/config.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/src/convert.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/src/errors.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/src/export.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/src/lib.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/src/logging.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/src/mesh.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/src/result.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/src/robot.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/tests/conftest.py +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/tests/test_compat.py +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/tests/test_config.py +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/tests/test_export.py +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/tests/test_logging.py +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/tests/test_mesh.py +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/tests/test_robot.py +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-py/tests/test_session.py +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/Cargo.toml +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/src/assemble.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/src/color.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/src/examples.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/src/history.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/src/inspect.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/src/kinematics.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/src/lib.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/src/object_model.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/src/pack.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/src/paths.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/src/quality.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/src/vfs.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/src/xml.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/tests/assemble_parity.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/tests/fixtures/py_assemble.json +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/tests/fixtures/py_inspect.json +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/tests/fixtures/py_object_model.json +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/tests/inspect_parity.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/tests/object_model_parity.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/tests/vfs_parity.rs +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/pyproject.toml +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/python/morphit_rs/__init__.py +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/python/morphit_rs/_native.pyi +0 -0
- {morphit_rs-0.2.0 → morphit_rs-0.2.1}/python/morphit_rs/py.typed +0 -0
|
@@ -4178,7 +4178,7 @@ dependencies = [
|
|
|
4178
4178
|
|
|
4179
4179
|
[[package]]
|
|
4180
4180
|
name = "morphit"
|
|
4181
|
-
version = "0.2.
|
|
4181
|
+
version = "0.2.1"
|
|
4182
4182
|
dependencies = [
|
|
4183
4183
|
"bytemuck",
|
|
4184
4184
|
"futures-channel",
|
|
@@ -4204,7 +4204,7 @@ dependencies = [
|
|
|
4204
4204
|
|
|
4205
4205
|
[[package]]
|
|
4206
4206
|
name = "morphit-capi"
|
|
4207
|
-
version = "0.2.
|
|
4207
|
+
version = "0.2.1"
|
|
4208
4208
|
dependencies = [
|
|
4209
4209
|
"cbindgen",
|
|
4210
4210
|
"morphit",
|
|
@@ -4218,7 +4218,7 @@ dependencies = [
|
|
|
4218
4218
|
|
|
4219
4219
|
[[package]]
|
|
4220
4220
|
name = "morphit-cli"
|
|
4221
|
-
version = "0.2.
|
|
4221
|
+
version = "0.2.1"
|
|
4222
4222
|
dependencies = [
|
|
4223
4223
|
"assert_cmd",
|
|
4224
4224
|
"clap",
|
|
@@ -4233,7 +4233,7 @@ dependencies = [
|
|
|
4233
4233
|
|
|
4234
4234
|
[[package]]
|
|
4235
4235
|
name = "morphit-py"
|
|
4236
|
-
version = "0.2.
|
|
4236
|
+
version = "0.2.1"
|
|
4237
4237
|
dependencies = [
|
|
4238
4238
|
"morphit",
|
|
4239
4239
|
"morphit-robot",
|
|
@@ -4249,7 +4249,7 @@ dependencies = [
|
|
|
4249
4249
|
|
|
4250
4250
|
[[package]]
|
|
4251
4251
|
name = "morphit-robot"
|
|
4252
|
-
version = "0.2.
|
|
4252
|
+
version = "0.2.1"
|
|
4253
4253
|
dependencies = [
|
|
4254
4254
|
"morphit",
|
|
4255
4255
|
"rand 0.9.5",
|
|
@@ -4265,7 +4265,7 @@ dependencies = [
|
|
|
4265
4265
|
|
|
4266
4266
|
[[package]]
|
|
4267
4267
|
name = "morphit-server"
|
|
4268
|
-
version = "0.2.
|
|
4268
|
+
version = "0.2.1"
|
|
4269
4269
|
dependencies = [
|
|
4270
4270
|
"axum",
|
|
4271
4271
|
"clap",
|
|
@@ -4287,7 +4287,7 @@ dependencies = [
|
|
|
4287
4287
|
|
|
4288
4288
|
[[package]]
|
|
4289
4289
|
name = "morphit-studio"
|
|
4290
|
-
version = "0.2.
|
|
4290
|
+
version = "0.2.1"
|
|
4291
4291
|
dependencies = [
|
|
4292
4292
|
"bevy",
|
|
4293
4293
|
"bevy_editor_cam",
|
|
@@ -4312,7 +4312,7 @@ dependencies = [
|
|
|
4312
4312
|
|
|
4313
4313
|
[[package]]
|
|
4314
4314
|
name = "morphit-wasm"
|
|
4315
|
-
version = "0.2.
|
|
4315
|
+
version = "0.2.1"
|
|
4316
4316
|
dependencies = [
|
|
4317
4317
|
"console_error_panic_hook",
|
|
4318
4318
|
"js-sys",
|
|
@@ -217,7 +217,7 @@ result.save("bunny.json")?;
|
|
|
217
217
|
([details](#using-the-release-package-from-cmake)):
|
|
218
218
|
|
|
219
219
|
```cmake
|
|
220
|
-
find_package(morphit 0.
|
|
220
|
+
find_package(morphit 0.2 CONFIG REQUIRED)
|
|
221
221
|
target_link_libraries(app PRIVATE morphit::morphit_cpp) # C: morphit::morphit
|
|
222
222
|
```
|
|
223
223
|
|
|
@@ -626,7 +626,7 @@ lib/cmake/morphit/morphit-config.cmake (+ version file, system libraries of
|
|
|
626
626
|
Unpack it anywhere and point `CMAKE_PREFIX_PATH` at the folder:
|
|
627
627
|
|
|
628
628
|
```cmake
|
|
629
|
-
find_package(morphit 0.
|
|
629
|
+
find_package(morphit 0.2 CONFIG REQUIRED)
|
|
630
630
|
target_link_libraries(app PRIVATE morphit::morphit) # shared library
|
|
631
631
|
# target_link_libraries(app PRIVATE morphit::morphit_static) # static, with its system libraries
|
|
632
632
|
# target_link_libraries(app PRIVATE morphit::morphit_cpp) # C++17 wrapper (also _cpp_static)
|
|
@@ -638,7 +638,7 @@ cmake -S . -B build -DCMAKE_PREFIX_PATH=/path/to/morphit-capi-0.2.0-x86_64-unkno
|
|
|
638
638
|
|
|
639
639
|
On Windows copy `morphit_capi.dll` next to the executable
|
|
640
640
|
(`$<TARGET_RUNTIME_DLLS:app>` lists it); the static library there is built
|
|
641
|
-
against the DLL runtime (`/MD`). A `0.
|
|
641
|
+
against the DLL runtime (`/MD`). A `0.2` request accepts any `0.2.x`. The
|
|
642
642
|
examples in `crates/morphit-capi/examples/c` use the package when
|
|
643
643
|
`CMAKE_PREFIX_PATH` points at one (`-DMORPHIT_STATIC=ON` for the static
|
|
644
644
|
library), and `tools/ci/test_capi_package.sh <archive>` builds and runs them
|
|
@@ -831,6 +831,9 @@ exits 0 when it answers, which is what the Docker health check runs.
|
|
|
831
831
|
packs the mesh exactly as loaded (the `X-Morphit-Mesh-Prep` report then says
|
|
832
832
|
`disabled`), and `convex_hull=true` packs the convex hull of each body. The
|
|
833
833
|
web UI has both under Advanced settings, Mesh preparation.
|
|
834
|
+
`advanced` also accepts `prune_contained_spheres` (default `true`; see
|
|
835
|
+
[Differences from the Python code](#differences-from-the-python-code)), under
|
|
836
|
+
Advanced settings, Final prune.
|
|
834
837
|
|
|
835
838
|
Robot sessions live in the server's memory and in the session directory, and
|
|
836
839
|
expire after the TTL. Run a single instance. Uploads are capped at 100 MB per
|
|
@@ -1133,6 +1136,12 @@ one-time device setup per process; ¹ optimizer loop only.
|
|
|
1133
1136
|
- Mesh preparation can be switched off (`model.union_overlapping_bodies`) and
|
|
1134
1137
|
extended with convex hulls (`model.convex_hull`); Python always merges and
|
|
1135
1138
|
never builds hulls. Both keys are stored in the result's config.
|
|
1139
|
+
- The final prune also removes spheres that lie entirely inside another
|
|
1140
|
+
sphere (`training.prune_contained_spheres`, on by default; `false` keeps
|
|
1141
|
+
them as Python does). They add nothing to the covered volume, so coverage
|
|
1142
|
+
and surface distance are unchanged, but they no longer count twice in the
|
|
1143
|
+
per-sphere masses. The key is stored in the result's config, and the HTTP
|
|
1144
|
+
API accepts it in `advanced`.
|
|
1136
1145
|
- Float64 throughout. GPU support is a search accelerator with CPU-identical
|
|
1137
1146
|
results rather than a float32 port; `model.device` therefore never changes
|
|
1138
1147
|
the output.
|
|
@@ -1206,7 +1215,7 @@ token. For PyPI, add a *pending trusted publisher* on pypi.org (project
|
|
|
1206
1215
|
and create an environment named `pypi`; no token is needed. npm publishes
|
|
1207
1216
|
only through trusted publishing (2FA-bypass tokens are being deprecated), and
|
|
1208
1217
|
a trusted publisher can only be added to an existing package: publish the
|
|
1209
|
-
first version by hand (`npm publish morphit-rs-<version>.tgz --access public`
|
|
1218
|
+
first version by hand (`npm publish ./morphit-rs-<version>.tgz --access public`
|
|
1210
1219
|
with the tarball from the GitHub Release; the release's `npm-publish` job
|
|
1211
1220
|
fails until then and can be re-run afterwards), then add a trusted publisher
|
|
1212
1221
|
for `morphit-rs` on npmjs.com (workflow `release.yml`, environment `npm`,
|
|
@@ -257,6 +257,10 @@ pub struct TrainingConfig {
|
|
|
257
257
|
pub density_control_warmup_steps: usize,
|
|
258
258
|
pub density_control_cooling_factor: f64,
|
|
259
259
|
pub density_control_min_radius_fraction: f64,
|
|
260
|
+
/// The final prune also removes spheres lying entirely inside another
|
|
261
|
+
/// sphere; they add nothing to the covered volume. Not in Python; on by
|
|
262
|
+
/// default.
|
|
263
|
+
pub prune_contained_spheres: bool,
|
|
260
264
|
}
|
|
261
265
|
|
|
262
266
|
impl Default for TrainingConfig {
|
|
@@ -291,6 +295,7 @@ impl Default for TrainingConfig {
|
|
|
291
295
|
density_control_warmup_steps: 10,
|
|
292
296
|
density_control_cooling_factor: 0.85,
|
|
293
297
|
density_control_min_radius_fraction: 0.001,
|
|
298
|
+
prune_contained_spheres: true,
|
|
294
299
|
}
|
|
295
300
|
}
|
|
296
301
|
}
|
|
@@ -668,6 +673,9 @@ mod tests {
|
|
|
668
673
|
assert_eq!(c.random_seed, None);
|
|
669
674
|
c.set("model.per_sphere_mass", json!(true)).unwrap();
|
|
670
675
|
assert!(c.model.per_sphere_mass);
|
|
676
|
+
assert_eq!(c.get("training.prune_contained_spheres").unwrap(), json!(true));
|
|
677
|
+
c.set("training.prune_contained_spheres", json!(false)).unwrap();
|
|
678
|
+
assert!(!c.training.prune_contained_spheres);
|
|
671
679
|
assert_eq!(c.get("training.iterations").unwrap(), json!(120));
|
|
672
680
|
}
|
|
673
681
|
|
|
@@ -267,7 +267,7 @@ fn prepare(
|
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
let n = kept.len();
|
|
270
|
-
let (uv, uf) = match union(&kept) {
|
|
270
|
+
let (uv, uf) = match union(&kept, !hulled) {
|
|
271
271
|
Ok(u) => u,
|
|
272
272
|
Err(UnionError::Unavailable(why)) => {
|
|
273
273
|
report.skip(
|
|
@@ -631,9 +631,12 @@ enum UnionError {
|
|
|
631
631
|
|
|
632
632
|
/// Boolean union of closed bodies, each converted to a Manifold from f32
|
|
633
633
|
/// positions (as Python's manifold3d `Mesh`), combined left to right. The
|
|
634
|
-
/// result's duplicate positions are merged like trimesh's `process=True
|
|
634
|
+
/// result's duplicate positions are merged like trimesh's `process=True` when
|
|
635
|
+
/// `weld` is set. Without it, Manifold's own indexing is kept: where two
|
|
636
|
+
/// inputs only touch, Manifold leaves separate vertices at one position, and
|
|
637
|
+
/// welding them would make the result non-manifold there.
|
|
635
638
|
#[cfg(feature = "union")]
|
|
636
|
-
fn union(bodies: &[Body]) -> Result<(Vec<DVec3>, Vec<[u32; 3]>), UnionError> {
|
|
639
|
+
fn union(bodies: &[Body], weld: bool) -> Result<(Vec<DVec3>, Vec<[u32; 3]>), UnionError> {
|
|
637
640
|
use manifold_rust::manifold::Manifold;
|
|
638
641
|
use manifold_rust::types::{Error as MfError, MeshGL};
|
|
639
642
|
|
|
@@ -675,11 +678,11 @@ fn union(bodies: &[Body]) -> Result<(Vec<DVec3>, Vec<[u32; 3]>), UnionError> {
|
|
|
675
678
|
.map(|p| DVec3::new(p[0] as f64, p[1] as f64, p[2] as f64))
|
|
676
679
|
.collect();
|
|
677
680
|
let faces: Vec<[u32; 3]> = out.tri_verts.chunks_exact(3).map(|t| [t[0], t[1], t[2]]).collect();
|
|
678
|
-
Ok(merge_positions(&vertices, &faces))
|
|
681
|
+
Ok(if weld { merge_positions(&vertices, &faces) } else { (vertices, faces) })
|
|
679
682
|
}
|
|
680
683
|
|
|
681
684
|
#[cfg(not(feature = "union"))]
|
|
682
|
-
fn union(_bodies: &[Body]) -> Result<(Vec<DVec3>, Vec<[u32; 3]>), UnionError> {
|
|
685
|
+
fn union(_bodies: &[Body], _weld: bool) -> Result<(Vec<DVec3>, Vec<[u32; 3]>), UnionError> {
|
|
683
686
|
Err(UnionError::Unavailable("mesh union support is not built in (feature `union`)".into()))
|
|
684
687
|
}
|
|
685
688
|
|
|
@@ -70,10 +70,48 @@ impl Spheres {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/// Spheres lying entirely inside another one (`|c_i - c_j| + r_j <= r_i`;
|
|
74
|
+
/// touching from inside counts). Of spheres that contain each other
|
|
75
|
+
/// (identical ones) the first is kept, so the largest always survives and the
|
|
76
|
+
/// union of the kept spheres equals the union of all.
|
|
77
|
+
pub fn contained_in_another(centers: &[DVec3], radii: &[f64]) -> Vec<bool> {
|
|
78
|
+
assert_eq!(centers.len(), radii.len());
|
|
79
|
+
let n = centers.len();
|
|
80
|
+
(0..n)
|
|
81
|
+
.map(|j| {
|
|
82
|
+
(0..n).any(|i| {
|
|
83
|
+
if i == j {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
// Same argument order for (i,j) and (j,i), as in `distances::pairwise`.
|
|
87
|
+
let d = crate::distances::dist(centers[i.min(j)], centers[i.max(j)]);
|
|
88
|
+
d + radii[j] <= radii[i] && (i < j || d + radii[i] > radii[j])
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
.collect()
|
|
92
|
+
}
|
|
93
|
+
|
|
73
94
|
#[cfg(test)]
|
|
74
95
|
mod tests {
|
|
75
96
|
use super::*;
|
|
76
97
|
|
|
98
|
+
#[test]
|
|
99
|
+
fn contained_spheres_are_found() {
|
|
100
|
+
let x = |v: f64| DVec3::new(v, 0.0, 0.0);
|
|
101
|
+
// Nested chain: all but the outermost go.
|
|
102
|
+
assert_eq!(contained_in_another(&[x(0.0), x(0.1), x(0.15)], &[1.0, 0.5, 0.2]), [false, true, true]);
|
|
103
|
+
// Partial overlap: both stay.
|
|
104
|
+
assert_eq!(contained_in_another(&[x(0.0), x(0.8)], &[0.5, 0.5]), [false, false]);
|
|
105
|
+
// Touching from inside counts as contained.
|
|
106
|
+
assert_eq!(contained_in_another(&[x(0.0), x(0.5)], &[1.0, 0.5]), [false, true]);
|
|
107
|
+
// Identical spheres: the first is kept.
|
|
108
|
+
assert_eq!(contained_in_another(&[x(1.0); 3], &[0.3; 3]), [false, true, true]);
|
|
109
|
+
// Order does not matter: the largest is kept.
|
|
110
|
+
assert_eq!(contained_in_another(&[x(0.15), x(0.1), x(0.0)], &[0.2, 0.5, 1.0]), [true, true, false]);
|
|
111
|
+
assert!(contained_in_another(&[], &[]).is_empty());
|
|
112
|
+
assert_eq!(contained_in_another(&[x(0.0)], &[0.1]), [false]);
|
|
113
|
+
}
|
|
114
|
+
|
|
77
115
|
#[test]
|
|
78
116
|
fn real_round_trip() {
|
|
79
117
|
let s = Spheres::from_real(vec![DVec3::ZERO, DVec3::ONE], &[0.1, 0.25], Some(&[2.0, 3.0]));
|
|
@@ -21,7 +21,7 @@ use crate::metrics::{DensityEvent, History, IterationRecord};
|
|
|
21
21
|
use crate::optim::{Optimizer, clip_grad_norm, clip_grad_norm_vec3};
|
|
22
22
|
use crate::result::PackResult;
|
|
23
23
|
use crate::sampling::{MorphRng, lognormal_radii, sample_inside, sample_surface, voxel_grid_centers};
|
|
24
|
-
use crate::state::Spheres;
|
|
24
|
+
use crate::state::{Spheres, contained_in_another};
|
|
25
25
|
|
|
26
26
|
/// Lifecycle of a session.
|
|
27
27
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
@@ -32,7 +32,7 @@ pub enum SessionState {
|
|
|
32
32
|
Converged,
|
|
33
33
|
/// All configured iterations ran.
|
|
34
34
|
Completed,
|
|
35
|
-
/// The final escaped
|
|
35
|
+
/// The final prune (escaped and nested spheres) ran; no more steps.
|
|
36
36
|
Finalized,
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -400,8 +400,10 @@ impl Session {
|
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
/// Remove spheres whose centers ended outside the mesh
|
|
403
|
-
/// (`_finalize_training`)
|
|
404
|
-
///
|
|
403
|
+
/// (`_finalize_training`), then, with `training.prune_contained_spheres`
|
|
404
|
+
/// (not in Python), spheres lying entirely inside another one, and end the
|
|
405
|
+
/// session. Idempotent; returns the number of spheres removed. At least
|
|
406
|
+
/// one sphere is always kept.
|
|
405
407
|
pub fn finalize(&mut self) -> usize {
|
|
406
408
|
if self.state == SessionState::Finalized {
|
|
407
409
|
return self.pruned;
|
|
@@ -412,10 +414,26 @@ impl Session {
|
|
|
412
414
|
tracing::warn!("every sphere center is outside the mesh; keeping the first sphere");
|
|
413
415
|
keep.push(0);
|
|
414
416
|
}
|
|
417
|
+
let escaped = self.spheres.len() - keep.len();
|
|
418
|
+
if escaped > 0 {
|
|
419
|
+
tracing::info!(removed = escaped, "final prune removed spheres whose centers escaped the mesh");
|
|
420
|
+
}
|
|
421
|
+
if self.config.training.prune_contained_spheres {
|
|
422
|
+
// Only among the survivors: an escaped container does not take its contents along.
|
|
423
|
+
let radii = self.spheres.radii();
|
|
424
|
+
let centers: Vec<_> = keep.iter().map(|&i| self.spheres.centers[i]).collect();
|
|
425
|
+
let r: Vec<f64> = keep.iter().map(|&i| radii[i]).collect();
|
|
426
|
+
let nested = contained_in_another(¢ers, &r);
|
|
427
|
+
let before = keep.len();
|
|
428
|
+
keep = keep.into_iter().zip(nested).filter_map(|(i, n)| (!n).then_some(i)).collect();
|
|
429
|
+
let contained = before - keep.len();
|
|
430
|
+
if contained > 0 {
|
|
431
|
+
tracing::info!(removed = contained, "final prune removed spheres inside another sphere");
|
|
432
|
+
}
|
|
433
|
+
}
|
|
415
434
|
let removed = self.spheres.len() - keep.len();
|
|
416
435
|
if removed > 0 {
|
|
417
436
|
self.spheres = self.spheres.select(&keep);
|
|
418
|
-
tracing::info!(removed, "final prune removed spheres whose centers escaped the mesh");
|
|
419
437
|
}
|
|
420
438
|
self.pruned = removed;
|
|
421
439
|
self.state = SessionState::Finalized;
|
|
@@ -745,6 +763,46 @@ mod tests {
|
|
|
745
763
|
assert_eq!(s.spheres().len(), 1);
|
|
746
764
|
}
|
|
747
765
|
|
|
766
|
+
/// A session on `cube()` whose spheres are replaced by `spheres`.
|
|
767
|
+
fn with_spheres(prune_contained: bool, spheres: &[([f64; 3], f64)]) -> Session {
|
|
768
|
+
let mut cfg = small_config(Preset::B, 5, spheres.len());
|
|
769
|
+
cfg.training.prune_contained_spheres = prune_contained;
|
|
770
|
+
let mut s = Session::new(cfg, cube()).unwrap();
|
|
771
|
+
let centers = spheres.iter().map(|(c, _)| DVec3::from_array(*c)).collect();
|
|
772
|
+
let radii: Vec<f64> = spheres.iter().map(|(_, r)| *r).collect();
|
|
773
|
+
s.spheres = Spheres::from_real(centers, &radii, None);
|
|
774
|
+
s
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
#[test]
|
|
778
|
+
fn finalize_prunes_spheres_inside_another() {
|
|
779
|
+
let spheres = [
|
|
780
|
+
([0.3, 0.4, 0.3], 0.25), // big
|
|
781
|
+
([0.35, 0.4, 0.3], 0.1), // inside the big one
|
|
782
|
+
([0.7, 0.4, 0.3], 0.2), // overlaps the big one partly
|
|
783
|
+
([0.3, 0.4, 0.3], 0.25), // duplicate of the big one
|
|
784
|
+
];
|
|
785
|
+
let mut s = with_spheres(true, &spheres);
|
|
786
|
+
let radii = s.spheres.radii();
|
|
787
|
+
assert_eq!(s.finalize(), 2);
|
|
788
|
+
assert_eq!(s.spheres().centers, vec![DVec3::new(0.3, 0.4, 0.3), DVec3::new(0.7, 0.4, 0.3)]);
|
|
789
|
+
assert_eq!(s.spheres().radii(), vec![radii[0], radii[2]]);
|
|
790
|
+
assert_eq!(s.finalize(), 2);
|
|
791
|
+
assert_eq!(s.result().num_spheres, 2);
|
|
792
|
+
|
|
793
|
+
let mut s = with_spheres(false, &spheres);
|
|
794
|
+
assert_eq!(s.finalize(), 0);
|
|
795
|
+
assert_eq!(s.spheres().len(), 4);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
#[test]
|
|
799
|
+
fn escaped_container_keeps_its_contents() {
|
|
800
|
+
// The container's center is outside the box; the nested sphere's is inside.
|
|
801
|
+
let mut s = with_spheres(true, &[([1.1, 0.4, 0.3], 0.5), ([0.9, 0.4, 0.3], 0.1)]);
|
|
802
|
+
assert_eq!(s.finalize(), 1);
|
|
803
|
+
assert_eq!(s.spheres().centers, vec![DVec3::new(0.9, 0.4, 0.3)]);
|
|
804
|
+
}
|
|
805
|
+
|
|
748
806
|
#[test]
|
|
749
807
|
fn projection_moves_escaped_centers_inside() {
|
|
750
808
|
let mut s = Session::new(small_config(Preset::B, 5, 4), cube()).unwrap();
|
|
@@ -351,8 +351,9 @@ impl PySession {
|
|
|
351
351
|
self.core.cancel.store(true, Ordering::Release);
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
/// Remove spheres whose centers ended outside the mesh and
|
|
355
|
-
///
|
|
354
|
+
/// Remove spheres whose centers ended outside the mesh and spheres inside
|
|
355
|
+
/// another sphere (unless `training.prune_contained_spheres` is false),
|
|
356
|
+
/// and end the session; returns how many were removed. Idempotent.
|
|
356
357
|
fn finalize(&self, py: Python<'_>) -> PyResult<usize> {
|
|
357
358
|
let _running = self.core.claim()?;
|
|
358
359
|
let core = Arc::clone(&self.core);
|
|
@@ -217,7 +217,7 @@ result.save("bunny.json")?;
|
|
|
217
217
|
([details](#using-the-release-package-from-cmake)):
|
|
218
218
|
|
|
219
219
|
```cmake
|
|
220
|
-
find_package(morphit 0.
|
|
220
|
+
find_package(morphit 0.2 CONFIG REQUIRED)
|
|
221
221
|
target_link_libraries(app PRIVATE morphit::morphit_cpp) # C: morphit::morphit
|
|
222
222
|
```
|
|
223
223
|
|
|
@@ -626,7 +626,7 @@ lib/cmake/morphit/morphit-config.cmake (+ version file, system libraries of
|
|
|
626
626
|
Unpack it anywhere and point `CMAKE_PREFIX_PATH` at the folder:
|
|
627
627
|
|
|
628
628
|
```cmake
|
|
629
|
-
find_package(morphit 0.
|
|
629
|
+
find_package(morphit 0.2 CONFIG REQUIRED)
|
|
630
630
|
target_link_libraries(app PRIVATE morphit::morphit) # shared library
|
|
631
631
|
# target_link_libraries(app PRIVATE morphit::morphit_static) # static, with its system libraries
|
|
632
632
|
# target_link_libraries(app PRIVATE morphit::morphit_cpp) # C++17 wrapper (also _cpp_static)
|
|
@@ -638,7 +638,7 @@ cmake -S . -B build -DCMAKE_PREFIX_PATH=/path/to/morphit-capi-0.2.0-x86_64-unkno
|
|
|
638
638
|
|
|
639
639
|
On Windows copy `morphit_capi.dll` next to the executable
|
|
640
640
|
(`$<TARGET_RUNTIME_DLLS:app>` lists it); the static library there is built
|
|
641
|
-
against the DLL runtime (`/MD`). A `0.
|
|
641
|
+
against the DLL runtime (`/MD`). A `0.2` request accepts any `0.2.x`. The
|
|
642
642
|
examples in `crates/morphit-capi/examples/c` use the package when
|
|
643
643
|
`CMAKE_PREFIX_PATH` points at one (`-DMORPHIT_STATIC=ON` for the static
|
|
644
644
|
library), and `tools/ci/test_capi_package.sh <archive>` builds and runs them
|
|
@@ -831,6 +831,9 @@ exits 0 when it answers, which is what the Docker health check runs.
|
|
|
831
831
|
packs the mesh exactly as loaded (the `X-Morphit-Mesh-Prep` report then says
|
|
832
832
|
`disabled`), and `convex_hull=true` packs the convex hull of each body. The
|
|
833
833
|
web UI has both under Advanced settings, Mesh preparation.
|
|
834
|
+
`advanced` also accepts `prune_contained_spheres` (default `true`; see
|
|
835
|
+
[Differences from the Python code](#differences-from-the-python-code)), under
|
|
836
|
+
Advanced settings, Final prune.
|
|
834
837
|
|
|
835
838
|
Robot sessions live in the server's memory and in the session directory, and
|
|
836
839
|
expire after the TTL. Run a single instance. Uploads are capped at 100 MB per
|
|
@@ -1133,6 +1136,12 @@ one-time device setup per process; ¹ optimizer loop only.
|
|
|
1133
1136
|
- Mesh preparation can be switched off (`model.union_overlapping_bodies`) and
|
|
1134
1137
|
extended with convex hulls (`model.convex_hull`); Python always merges and
|
|
1135
1138
|
never builds hulls. Both keys are stored in the result's config.
|
|
1139
|
+
- The final prune also removes spheres that lie entirely inside another
|
|
1140
|
+
sphere (`training.prune_contained_spheres`, on by default; `false` keeps
|
|
1141
|
+
them as Python does). They add nothing to the covered volume, so coverage
|
|
1142
|
+
and surface distance are unchanged, but they no longer count twice in the
|
|
1143
|
+
per-sphere masses. The key is stored in the result's config, and the HTTP
|
|
1144
|
+
API accepts it in `advanced`.
|
|
1136
1145
|
- Float64 throughout. GPU support is a search accelerator with CPU-identical
|
|
1137
1146
|
results rather than a float32 port; `model.device` therefore never changes
|
|
1138
1147
|
the output.
|
|
@@ -1206,7 +1215,7 @@ token. For PyPI, add a *pending trusted publisher* on pypi.org (project
|
|
|
1206
1215
|
and create an environment named `pypi`; no token is needed. npm publishes
|
|
1207
1216
|
only through trusted publishing (2FA-bypass tokens are being deprecated), and
|
|
1208
1217
|
a trusted publisher can only be added to an existing package: publish the
|
|
1209
|
-
first version by hand (`npm publish morphit-rs-<version>.tgz --access public`
|
|
1218
|
+
first version by hand (`npm publish ./morphit-rs-<version>.tgz --access public`
|
|
1210
1219
|
with the tarball from the GitHub Release; the release's `npm-publish` job
|
|
1211
1220
|
fails until then and can be re-run afterwards), then add a trusted publisher
|
|
1212
1221
|
for `morphit-rs` on npmjs.com (workflow `release.yml`, environment `npm`,
|
|
@@ -10,8 +10,9 @@ use crate::{Error, Result};
|
|
|
10
10
|
pub const ALLOWED_VARIANTS: [&str; 3] = ["MorphIt-V", "MorphIt-S", "MorphIt-B"];
|
|
11
11
|
|
|
12
12
|
/// Flat `advanced` keys the UI sends, and the dotted config keys they set.
|
|
13
|
-
/// Anything else is dropped silently, as in the Python API
|
|
14
|
-
|
|
13
|
+
/// Anything else is dropped silently, as in the Python API
|
|
14
|
+
/// (`prune_contained_spheres` is not in Python).
|
|
15
|
+
pub const ADVANCED_OVERRIDE_MAP: [(&str, &str); 14] = [
|
|
15
16
|
("num_inside_samples", "model.num_inside_samples"),
|
|
16
17
|
("num_surface_samples", "model.num_surface_samples"),
|
|
17
18
|
("density_control_enabled", "training.density_control_enabled"),
|
|
@@ -25,6 +26,7 @@ pub const ADVANCED_OVERRIDE_MAP: [(&str, &str); 13] = [
|
|
|
25
26
|
("sqem_weight", "training.sqem_weight"),
|
|
26
27
|
("hausdorff_weight", "training.hausdorff_weight"),
|
|
27
28
|
("mesh_containment_weight", "training.mesh_containment_weight"),
|
|
29
|
+
("prune_contained_spheres", "training.prune_contained_spheres"),
|
|
28
30
|
];
|
|
29
31
|
|
|
30
32
|
/// `variant must be one of ('MorphIt-V', 'MorphIt-S', 'MorphIt-B')`.
|
|
@@ -135,7 +137,7 @@ mod tests {
|
|
|
135
137
|
#[test]
|
|
136
138
|
fn advanced_is_filtered_and_mapped() {
|
|
137
139
|
let a = parse_advanced(
|
|
138
|
-
r#"{"coverage_weight": 2.5, "bogus": 1, "sqem_weight": null, "num_inside_samples": 100}"#,
|
|
140
|
+
r#"{"coverage_weight": 2.5, "bogus": 1, "sqem_weight": null, "num_inside_samples": 100, "prune_contained_spheres": false}"#,
|
|
139
141
|
)
|
|
140
142
|
.unwrap();
|
|
141
143
|
assert_eq!(
|
|
@@ -143,6 +145,7 @@ mod tests {
|
|
|
143
145
|
vec![
|
|
144
146
|
("training.coverage_weight".to_string(), Value::from(2.5)),
|
|
145
147
|
("model.num_inside_samples".to_string(), Value::from(100)),
|
|
148
|
+
("training.prune_contained_spheres".to_string(), Value::from(false)),
|
|
146
149
|
]
|
|
147
150
|
);
|
|
148
151
|
assert!(parse_advanced("").unwrap().is_empty());
|
|
@@ -183,7 +186,10 @@ mod tests {
|
|
|
183
186
|
fn config_applies_everything() {
|
|
184
187
|
let mut p = params();
|
|
185
188
|
p.seed = Some(7);
|
|
186
|
-
p.advanced = parse_advanced(
|
|
189
|
+
p.advanced = parse_advanced(
|
|
190
|
+
r#"{"coverage_weight": 3, "density_control_enabled": false, "prune_contained_spheres": false}"#,
|
|
191
|
+
)
|
|
192
|
+
.unwrap();
|
|
187
193
|
let c = p.config("cpu", "/tmp/x", "a.json").unwrap();
|
|
188
194
|
assert_eq!(c.model.num_spheres, 20);
|
|
189
195
|
assert_eq!(c.training.iterations, 200);
|
|
@@ -191,6 +197,7 @@ mod tests {
|
|
|
191
197
|
assert_eq!(c.model.device, "cpu");
|
|
192
198
|
assert_eq!(c.training.coverage_weight, 3.0);
|
|
193
199
|
assert!(!c.training.density_control_enabled);
|
|
200
|
+
assert!(!c.training.prune_contained_spheres);
|
|
194
201
|
assert_eq!(c.output_filename, "a.json");
|
|
195
202
|
p.advanced = vec![("model.num_inside_samples".into(), Value::from("many"))];
|
|
196
203
|
assert!(matches!(p.config("cpu", "", ""), Err(Error::Invalid(_))));
|
|
@@ -435,7 +435,8 @@ def train_morphit(
|
|
|
435
435
|
print(f"Stopping at iteration {step.iteration}")
|
|
436
436
|
removed = session.finalize()
|
|
437
437
|
if removed:
|
|
438
|
-
print(f"\n[final prune] removed {removed} sphere(s) whose centers escaped the mesh"
|
|
438
|
+
print(f"\n[final prune] removed {removed} sphere(s) whose centers escaped the mesh "
|
|
439
|
+
f"or that lay inside another sphere")
|
|
439
440
|
model.num_spheres = session.num_spheres
|
|
440
441
|
print("\n=== Training Complete ===")
|
|
441
442
|
print(f"Density control operations: {len(tracker.metrics['density_control_events'])}")
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/mesh_prep/groups_box.obj
RENAMED
|
File without changes
|
{morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/mesh_prep/inward_inner_shell.obj
RENAMED
|
File without changes
|
{morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/mesh_prep/open_overlap.obj
RENAMED
|
File without changes
|
|
File without changes
|
{morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/mesh_prep/three_bodies.obj
RENAMED
|
File without changes
|
{morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/mesh_prep/two_overlapping.obj
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit/tests/fixtures/py_link0_objmass_n16.json
RENAMED
|
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
|
|
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
|
{morphit_rs-0.2.0 → morphit_rs-0.2.1}/crates/morphit-robot/tests/fixtures/py_object_model.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|