diffusion-cartogram 0.2.1__tar.gz → 0.2.2__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.
- diffusion_cartogram-0.2.2/CHANGELOG.md +80 -0
- diffusion_cartogram-0.2.2/MANIFEST.in +23 -0
- {diffusion_cartogram-0.2.1/diffusion_cartogram.egg-info → diffusion_cartogram-0.2.2}/PKG-INFO +3 -2
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram/__init__.py +3 -1
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram/core.py +228 -53
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram/visualization.py +8 -5
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2/diffusion_cartogram.egg-info}/PKG-INFO +3 -2
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram.egg-info/SOURCES.txt +3 -6
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/pyproject.toml +1 -1
- diffusion_cartogram-0.2.1/tests/test_core.py +0 -374
- diffusion_cartogram-0.2.1/tests/test_core_2d.py +0 -372
- diffusion_cartogram-0.2.1/tests/test_interpolation.py +0 -70
- diffusion_cartogram-0.2.1/tests/test_io.py +0 -160
- diffusion_cartogram-0.2.1/tests/test_vizualization.py +0 -214
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/LICENSE +0 -0
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/README.md +0 -0
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/README_pypi.md +0 -0
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram/core_2d.py +0 -0
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram/visualization_2d.py +0 -0
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram.egg-info/dependency_links.txt +0 -0
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram.egg-info/requires.txt +0 -0
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram.egg-info/top_level.txt +0 -0
- {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/setup.cfg +0 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.2.2] - 2026-08-31
|
|
4
|
+
|
|
5
|
+
### New: Additional reconstruction options
|
|
6
|
+
|
|
7
|
+
- **Added** `load_mesh_topology(mesh_path)`: loads a mesh's own vertices,
|
|
8
|
+
faces, and per-vertex normals with **no resampling**. This is the new
|
|
9
|
+
default entry point for the deformation pipeline (replaces `create_pcd` as
|
|
10
|
+
the "get me surface points" call for most users; `create_pcd` still exists
|
|
11
|
+
unchanged for anyone who wants a lighter/resampled point cloud, or as the
|
|
12
|
+
Poisson fallback's input).
|
|
13
|
+
- **Modified** `export_mesh_file(filename, deformed_pcd, ...)`:
|
|
14
|
+
- New parameters: `method='none'` (new default), `original_faces=None`.
|
|
15
|
+
- `method='none'`: requires `original_faces`; if given, saves
|
|
16
|
+
`(deformed_pcd, original_faces)` directly via
|
|
17
|
+
`pymeshlab.Mesh(vertex_matrix=..., face_matrix=...)` -- no reconstruction.
|
|
18
|
+
- If `method='none'` but `original_faces` is `None`: emits a
|
|
19
|
+
`RuntimeWarning` and **falls back to `method='poisson'`** (the prior,
|
|
20
|
+
only, behavior) automatically.
|
|
21
|
+
- `method='poisson'`: unchanged prior behavior (screened Poisson
|
|
22
|
+
reconstruction with estimated normals), reachable explicitly or via the
|
|
23
|
+
automatic fallback above.
|
|
24
|
+
- Added `method='ball_pivoting'` and `method='alpha_shape'` alongside `'none'`/ `'poisson'`. New parameters, all with defaults matching pymeshlab's own except `alpha_filtering`. `ball_radius=0.0` (0% = auto-estimated), `ball_clustering=20.0`, `ball_creasethr=90.0`, `ball_deletefaces=False`, `alpha=1.0`, `alpha_filtering='Alpha Shape'` (pymeshlab's own filter default is `'Alpha Complex'`, deliberately overridden -- `'Alpha Complex'` retains interior simplicial-complex faces, not just the outer boundary).
|
|
25
|
+
- Backward compatible: existing callers that only ever passed
|
|
26
|
+
`(filename, deformed_pcd)` keep working exactly as before, just now with
|
|
27
|
+
a printed warning nudging them toward the no-reconstruction path.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## [0.2.1] - 2026-07-31
|
|
31
|
+
|
|
32
|
+
### New: Post hoc animation
|
|
33
|
+
- **`animate_surface_posthoc`** - Interpolate the final displacement field and densities to create an animation for display purposes. Faster than running with tracking but not guaranteed to be accurate at intermediate steps
|
|
34
|
+
- **`animate_map_posthoc`** - Same function implemented for 2D maps
|
|
35
|
+
|
|
36
|
+
## [0.2.0] - 2026-04-29
|
|
37
|
+
|
|
38
|
+
### New: 2D VDERM Pipeline
|
|
39
|
+
|
|
40
|
+
- **`VDERMGrid2D`** — 2-D Lagrangian-Eulerian grid; same physics as `VDERMGrid` (diffusion + gradient advection) with one dimension removed. Compatible with the existing `run_VDERM()` function.
|
|
41
|
+
- **`run_VDERM_2d_with_tracking()`** — tracking run with grid and map-point exports (CSV format: `x y v_x v_y rho` for grid, `x y rho` for map points).
|
|
42
|
+
- **`make_initial_grid_2d()`** / **`compute_grid_dimensions_2d()`** / **`print_grid_info_2d()`** — 2-D grid utilities mirroring the 3-D equivalents.
|
|
43
|
+
- **`interpolate_to_map_2d()`** / **`interpolate_densities_2d()`** / **`interpolate_velocities_2d()`** — 2-D interpolation from grid to arbitrary point sets.
|
|
44
|
+
|
|
45
|
+
### New: Geographic I/O (`pip install diffusion-cartogram[2D]`)
|
|
46
|
+
|
|
47
|
+
- **`read_geojson(filepath)`** — extract 2-D point array from GeoJSON (polygon boundaries, point features, line features).
|
|
48
|
+
- **`read_shapefile(filepath)`** — same for Shapefiles.
|
|
49
|
+
- **`read_geotiff(filepath, band=1)`** — read a raster band plus coordinate arrays.
|
|
50
|
+
- **`density_from_geotiff(grid_2d, filepath)`** — sample a GeoTIFF raster onto a `VDERMGrid2D` density field with bilinear interpolation; handles north-up rasters and nodata automatically.
|
|
51
|
+
- **`write_csv_2d()`** / **`read_csv_2d()`** — simple 2-column / 3-column CSV I/O for 2-D point sets.
|
|
52
|
+
|
|
53
|
+
### New: 2D Visualization
|
|
54
|
+
|
|
55
|
+
- **`plot_map_2d()`** — scatter plot of 2-D points with optional density colouring.
|
|
56
|
+
- **`plot_density_field_2d()`** — heatmap of the `VDERMGrid2D` density field.
|
|
57
|
+
- **`plot_map_before_after()`** — side-by-side comparison of original and deformed map.
|
|
58
|
+
- **`animate_map_deformation_2d()`** — GIF / MP4 animation from `vderm_map/` CSV exports.
|
|
59
|
+
- **`animate_grid_deformation_2d()`** — GIF / MP4 animation from `vderm_grid/` CSV exports.
|
|
60
|
+
- **`plot_density_evolution_2d()`** — density statistics over iterations.
|
|
61
|
+
|
|
62
|
+
### Packaging
|
|
63
|
+
|
|
64
|
+
- Version bumped to **0.2.0**.
|
|
65
|
+
- New optional dependency group **`[2D]`**: geopandas, rasterio, shapely.
|
|
66
|
+
- New optional dependency group **`[3D]`**: pymeshlab.
|
|
67
|
+
- New optional dependency group **`[all]`**: `[2D]` + `[3D]`.
|
|
68
|
+
- `pyproject.toml` now points `readme` at `README_pypi.md` (no embedded GIF) for PyPI; `README.md` remains the full GitHub README.
|
|
69
|
+
|
|
70
|
+
## [0.1.0] - 2026-02-09
|
|
71
|
+
|
|
72
|
+
### Initial Release
|
|
73
|
+
|
|
74
|
+
- Core VDERM algorithm implementation
|
|
75
|
+
- Flexible XYZ file I/O
|
|
76
|
+
- Optional mesh support via PyMeshLab
|
|
77
|
+
- Visualization and animation tools
|
|
78
|
+
- Tracking with intermediate exports
|
|
79
|
+
- ParaView export capabilities
|
|
80
|
+
- Comprehensive example notebooks
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Include essential files
|
|
2
|
+
include README.md
|
|
3
|
+
include LICENSE
|
|
4
|
+
include CHANGELOG.md
|
|
5
|
+
|
|
6
|
+
# Include package code
|
|
7
|
+
recursive-include diffusion_cartogram *.py
|
|
8
|
+
|
|
9
|
+
# Exclude examples, tests, and large files
|
|
10
|
+
prune examples
|
|
11
|
+
prune tests
|
|
12
|
+
global-exclude *.stl
|
|
13
|
+
global-exclude *.obj
|
|
14
|
+
global-exclude *.ply
|
|
15
|
+
global-exclude *.xyz
|
|
16
|
+
global-exclude *.gif
|
|
17
|
+
global-exclude *.mp4
|
|
18
|
+
global-exclude *.png
|
|
19
|
+
global-exclude *.jpg
|
|
20
|
+
global-exclude .git*
|
|
21
|
+
global-exclude __pycache__
|
|
22
|
+
global-exclude *.pyc
|
|
23
|
+
global-exclude .DS_Store
|
{diffusion_cartogram-0.2.1/diffusion_cartogram.egg-info → diffusion_cartogram-0.2.2}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: diffusion-cartogram
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Volumetric Density-Equalizing Reference Map — 3D shape deformation and 2D cartogram generation
|
|
5
5
|
Author-email: Jonah Spector <spector.jo@northeastern.edu>
|
|
6
6
|
License: MIT License
|
|
@@ -56,6 +56,7 @@ Requires-Dist: geopandas>=0.12; extra == "all"
|
|
|
56
56
|
Requires-Dist: rasterio>=1.3; extra == "all"
|
|
57
57
|
Requires-Dist: shapely>=2.0; extra == "all"
|
|
58
58
|
Requires-Dist: pymeshlab>=2023.12; extra == "all"
|
|
59
|
+
Dynamic: license-file
|
|
59
60
|
|
|
60
61
|
# diffusion-cartogram
|
|
61
62
|
[](https://badge.fury.io/py/diffusion-cartogram)
|
|
@@ -29,7 +29,7 @@ cartogram deformation from GeoJSON / Shapefile inputs.
|
|
|
29
29
|
>>> vd.plot_map_2d(deformed, title='Population Cartogram')
|
|
30
30
|
"""
|
|
31
31
|
|
|
32
|
-
__version__ = '0.2.
|
|
32
|
+
__version__ = '0.2.2'
|
|
33
33
|
|
|
34
34
|
# Core VDERM classes and algorithms
|
|
35
35
|
from .core import (
|
|
@@ -44,6 +44,7 @@ from .core import (
|
|
|
44
44
|
write_xyz,
|
|
45
45
|
read_xyz,
|
|
46
46
|
create_pcd,
|
|
47
|
+
load_mesh_topology,
|
|
47
48
|
export_mesh_file,
|
|
48
49
|
export_mesh_vtk,
|
|
49
50
|
)
|
|
@@ -92,6 +93,7 @@ __all__ = [
|
|
|
92
93
|
'write_xyz',
|
|
93
94
|
'read_xyz',
|
|
94
95
|
'create_pcd',
|
|
96
|
+
'load_mesh_topology',
|
|
95
97
|
'export_mesh_file',
|
|
96
98
|
'export_mesh_vtk',
|
|
97
99
|
|
|
@@ -2,6 +2,7 @@ import numpy as np
|
|
|
2
2
|
from scipy import interpolate
|
|
3
3
|
from tqdm import tqdm
|
|
4
4
|
import os
|
|
5
|
+
import warnings
|
|
5
6
|
from scipy.interpolate import RegularGridInterpolator, NearestNDInterpolator
|
|
6
7
|
try:
|
|
7
8
|
import pymeshlab as ml
|
|
@@ -51,10 +52,57 @@ def create_pcd(mesh_path, n_pts=25_000, sampling_method='poisson'):
|
|
|
51
52
|
current_mesh = ms.current_mesh()
|
|
52
53
|
out = current_mesh.vertex_matrix()
|
|
53
54
|
norms = current_mesh.vertex_normal_matrix()
|
|
54
|
-
|
|
55
|
+
|
|
55
56
|
return out, norms
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def load_mesh_topology(mesh_path):
|
|
60
|
+
"""
|
|
61
|
+
Load a mesh's own vertices, faces, and per-vertex normals directly, with
|
|
62
|
+
no resampling -- unlike create_pcd, which generates an entirely new point
|
|
63
|
+
cloud unrelated to the mesh's actual vertex/face indices.
|
|
64
|
+
|
|
65
|
+
This is the entry point for the "no reconstruction" deformation pipeline:
|
|
66
|
+
push the returned `vertices` through interpolate_to_surface exactly as you
|
|
67
|
+
would create_pcd's point cloud, then pass the (still index-aligned)
|
|
68
|
+
deformed vertices and this function's `faces` to
|
|
69
|
+
export_mesh_file(..., method='none', original_faces=faces) to export with
|
|
70
|
+
the source mesh's exact topology preserved -- no reconstruction, and
|
|
71
|
+
therefore no risk of the reconstruction artifacts (holes, non-manifold
|
|
72
|
+
surfaces) that Poisson/ball-pivoting/alpha-shape can produce on sparse or
|
|
73
|
+
thin geometry.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
mesh_path : str
|
|
78
|
+
mesh file path
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
vertices : ndarray, shape (n_verts, 3)
|
|
83
|
+
Vertex positions [x, y, z], in the mesh's own vertex order.
|
|
84
|
+
faces : ndarray, shape (n_faces, 3)
|
|
85
|
+
Triangle vertex-index triples, referencing rows of `vertices`.
|
|
86
|
+
normals : ndarray, shape (n_verts, 3)
|
|
87
|
+
Per-vertex normal vectors, in the same order as `vertices`.
|
|
88
|
+
"""
|
|
89
|
+
_require_pymeshlab('load_mesh_topology')
|
|
90
|
+
|
|
91
|
+
ms = ml.MeshSet()
|
|
92
|
+
ms.load_new_mesh(mesh_path)
|
|
93
|
+
ms.compute_normal_per_vertex()
|
|
94
|
+
|
|
95
|
+
current_mesh = ms.current_mesh()
|
|
96
|
+
vertices = current_mesh.vertex_matrix()
|
|
97
|
+
faces = current_mesh.face_matrix()
|
|
98
|
+
normals = current_mesh.vertex_normal_matrix()
|
|
99
|
+
norm_len = np.linalg.norm(normals, axis=1, keepdims=True)
|
|
100
|
+
norm_len[norm_len == 0] = 1
|
|
101
|
+
normals = normals / norm_len
|
|
102
|
+
|
|
103
|
+
return vertices, faces, normals
|
|
104
|
+
|
|
105
|
+
|
|
58
106
|
def write_xyz(filepath, positions, normals=None, densities=None):
|
|
59
107
|
"""
|
|
60
108
|
Write grid positions (and optionally normal vectors and densities) to space-delimited .xyz file.
|
|
@@ -1329,81 +1377,208 @@ def interpolate_to_surface(surface_points, grid_params, displacement_field):
|
|
|
1329
1377
|
|
|
1330
1378
|
return surface_points + interpolated_displacement
|
|
1331
1379
|
|
|
1332
|
-
def export_mesh_file(filename, deformed_pcd,
|
|
1380
|
+
def export_mesh_file(filename, deformed_pcd, method='none', original_faces=None,
|
|
1381
|
+
depth=8, fulldepth=5, scale=1.1,
|
|
1382
|
+
ball_radius=0.0, ball_clustering=20.0, ball_creasethr=90.0,
|
|
1383
|
+
ball_deletefaces=False,
|
|
1384
|
+
alpha=1.0, alpha_filtering='Alpha Shape'):
|
|
1333
1385
|
"""
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
using local neighborhood analysis, which is more accurate for deformed surfaces
|
|
1338
|
-
than using original normals.
|
|
1339
|
-
|
|
1386
|
+
Exports a deformed point cloud/mesh to file, either by reusing known mesh
|
|
1387
|
+
topology directly (default) or by reconstructing a surface from scratch.
|
|
1388
|
+
|
|
1340
1389
|
Parameters
|
|
1341
1390
|
----------
|
|
1342
1391
|
filename : str
|
|
1343
1392
|
Output file path (.ply, .stl, .obj, .off, or .gltf/.glb)
|
|
1344
1393
|
deformed_pcd : ndarray, shape (n_points, 3)
|
|
1345
|
-
Deformed point
|
|
1394
|
+
Deformed point positions to export.
|
|
1395
|
+
method : str, default='none'
|
|
1396
|
+
'none' -- No reconstruction: save `deformed_pcd` directly with
|
|
1397
|
+
`original_faces` as its triangle connectivity. Requires
|
|
1398
|
+
`original_faces` (see below); if omitted, falls back to
|
|
1399
|
+
method='poisson' automatically (with a warning). Guarantees the
|
|
1400
|
+
same topology/watertightness as the source mesh, since nothing is
|
|
1401
|
+
being reconstructed -- just moving known vertices. Use this when
|
|
1402
|
+
`deformed_pcd` is the deformed version of a mesh's own vertices,
|
|
1403
|
+
e.g. from load_mesh_topology() pushed through
|
|
1404
|
+
interpolate_to_surface().
|
|
1405
|
+
|
|
1406
|
+
The remaining methods are all genuine surface-*reconstruction*
|
|
1407
|
+
fallbacks, only meaningful when `original_faces` isn't available
|
|
1408
|
+
(e.g. `deformed_pcd` came from create_pcd's resampling, or some other
|
|
1409
|
+
point source with no known mesh behind it). All three can leave holes
|
|
1410
|
+
or non-manifold regions on sparse or thin (e.g. wireframe) geometry --
|
|
1411
|
+
prefer method='none' whenever `original_faces` is available, and treat
|
|
1412
|
+
these as a last resort, trying more than one if the first doesn't
|
|
1413
|
+
give a usable result on your specific point cloud:
|
|
1414
|
+
|
|
1415
|
+
'poisson' -- Screened Poisson surface reconstruction, with normals
|
|
1416
|
+
estimated from local neighborhood geometry. Tends to over-smooth
|
|
1417
|
+
and can bridge/merge separate thin features that are close
|
|
1418
|
+
together, but usually produces a closed (watertight) surface even
|
|
1419
|
+
from imperfect data.
|
|
1420
|
+
'ball_pivoting' -- Rolls a virtual ball of radius `ball_radius` over
|
|
1421
|
+
the (normal-estimated) point cloud, adding a triangle everywhere
|
|
1422
|
+
it touches 3 points simultaneously. Preserves sharp features
|
|
1423
|
+
better than Poisson, but requires fairly uniform point density --
|
|
1424
|
+
it leaves holes wherever the ball can't reach (e.g. sparse
|
|
1425
|
+
regions, or thin rods meeting at sharp angles), so it is *not*
|
|
1426
|
+
guaranteed watertight.
|
|
1427
|
+
'alpha_shape' -- Filters the point cloud's Delaunay triangulation down
|
|
1428
|
+
to the faces within `alpha` of the input points (see `alpha` and
|
|
1429
|
+
`alpha_filtering` below). Purely geometric (no normal estimation
|
|
1430
|
+
needed), deterministic, but similarly sensitive to uneven point
|
|
1431
|
+
density and also not guaranteed watertight.
|
|
1432
|
+
original_faces : ndarray, shape (n_faces, 3), optional
|
|
1433
|
+
Required when method='none'. Triangle vertex-index triples, referencing
|
|
1434
|
+
rows of `deformed_pcd` by index -- i.e. `deformed_pcd` must still be in
|
|
1435
|
+
the same vertex order these faces were defined against.
|
|
1346
1436
|
depth : int, default=8
|
|
1347
|
-
Poisson reconstruction octree depth (higher = more detail)
|
|
1437
|
+
(method='poisson' only) Poisson reconstruction octree depth (higher = more detail)
|
|
1348
1438
|
fulldepth : int, default=5
|
|
1349
|
-
Depth below which octree will be complete
|
|
1439
|
+
(method='poisson' only) Depth below which octree will be complete
|
|
1350
1440
|
scale : float, default=1.1
|
|
1351
|
-
Ratio between reconstruction cube diameter and samples' bounding cube diameter
|
|
1352
|
-
|
|
1441
|
+
(method='poisson' only) Ratio between reconstruction cube diameter and samples' bounding cube diameter
|
|
1442
|
+
ball_radius : float, default=0.0
|
|
1443
|
+
(method='ball_pivoting' only) Pivoting ball radius, as a percentage of
|
|
1444
|
+
the point cloud's bounding-box diagonal. 0.0 asks pymeshlab to
|
|
1445
|
+
auto-estimate a radius from the point cloud's own density.
|
|
1446
|
+
ball_clustering : float, default=20.0
|
|
1447
|
+
(method='ball_pivoting' only) Percentage of `ball_radius` used as a
|
|
1448
|
+
clustering threshold -- points closer together than this are treated
|
|
1449
|
+
as a single point during reconstruction (reduces redundant triangles).
|
|
1450
|
+
ball_creasethr : float, default=90.0
|
|
1451
|
+
(method='ball_pivoting' only) Angle (degrees) beyond which an edge is
|
|
1452
|
+
treated as a crease and not smoothed over.
|
|
1453
|
+
ball_deletefaces : bool, default=False
|
|
1454
|
+
(method='ball_pivoting' only) If True, only add points to the mesh
|
|
1455
|
+
without generating faces (rarely useful; kept for parity with the
|
|
1456
|
+
underlying pymeshlab filter).
|
|
1457
|
+
alpha : float, default=1.0
|
|
1458
|
+
(method='alpha_shape' only) Alpha value, as a percentage of the point
|
|
1459
|
+
cloud's bounding-box diagonal. Larger values include more/coarser
|
|
1460
|
+
faces; smaller values hew closer to the points themselves (and more
|
|
1461
|
+
readily leave holes).
|
|
1462
|
+
alpha_filtering : str, default='Alpha Shape'
|
|
1463
|
+
(method='alpha_shape' only) 'Alpha Shape' keeps only the outer
|
|
1464
|
+
boundary surface (what you almost always want for a printable mesh);
|
|
1465
|
+
'Alpha Complex' also keeps interior simplicial-complex faces, which
|
|
1466
|
+
tends to produce a much larger, messier face count for the same
|
|
1467
|
+
input. (pymeshlab's own filter default is 'Alpha Complex' --
|
|
1468
|
+
deliberately overridden here.)
|
|
1469
|
+
|
|
1353
1470
|
Returns
|
|
1354
1471
|
-------
|
|
1355
1472
|
result_mesh : pymeshlab Mesh
|
|
1356
|
-
The
|
|
1357
|
-
|
|
1473
|
+
The exported mesh object (topology reused as-is for method='none';
|
|
1474
|
+
reconstructed for the other methods).
|
|
1475
|
+
|
|
1358
1476
|
Notes
|
|
1359
1477
|
-----
|
|
1360
1478
|
Poisson reconstruction default values from:
|
|
1361
1479
|
https://www.cs.jhu.edu/~misha/Code/PoissonRecon/Version8.0/
|
|
1362
|
-
|
|
1363
|
-
Normal estimation uses k=20 nearest
|
|
1364
|
-
Adjust these in the code if needed
|
|
1480
|
+
|
|
1481
|
+
Normal estimation (method='poisson'/'ball_pivoting') uses k=20 nearest
|
|
1482
|
+
neighbors with 2 smoothing iterations. Adjust these in the code if needed
|
|
1483
|
+
for your specific geometry.
|
|
1365
1484
|
|
|
1366
1485
|
macOS + conda users may encounter an OpenMP conflict (OMP: Error #15) when
|
|
1367
1486
|
this function is called, due to pymeshlab's bundled libomp conflicting with
|
|
1368
1487
|
conda-forge's numpy/scipy. See the Known Issues section of the README for
|
|
1369
|
-
the fix.
|
|
1370
|
-
|
|
1488
|
+
the fix.
|
|
1489
|
+
|
|
1371
1490
|
Examples
|
|
1372
1491
|
--------
|
|
1373
|
-
>>> #
|
|
1374
|
-
>>>
|
|
1375
|
-
|
|
1376
|
-
>>>
|
|
1377
|
-
|
|
1492
|
+
>>> # Default: no reconstruction, reuse the source mesh's own topology
|
|
1493
|
+
>>> verts, faces, normals = load_mesh_topology('mesh.stl')
|
|
1494
|
+
>>> deformed = interpolate_to_surface(verts, grid_params, displacement_field)
|
|
1495
|
+
>>> mesh = export_mesh_file('output.stl', deformed, original_faces=faces)
|
|
1496
|
+
|
|
1497
|
+
>>> # Reconstruction fallbacks, for when original_faces isn't available
|
|
1498
|
+
>>> mesh = export_mesh_file('output.stl', deformed_points, method='poisson')
|
|
1499
|
+
>>> mesh = export_mesh_file('output.stl', deformed_points, method='ball_pivoting', ball_radius=2.0)
|
|
1500
|
+
>>> mesh = export_mesh_file('output.stl', deformed_points, method='alpha_shape', alpha=2.0)
|
|
1378
1501
|
"""
|
|
1379
|
-
_require_pymeshlab('
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1502
|
+
_require_pymeshlab('export_mesh_file')
|
|
1503
|
+
|
|
1504
|
+
if method == 'none' and original_faces is None:
|
|
1505
|
+
warnings.warn(
|
|
1506
|
+
"export_mesh_file(method='none') requires original_faces; "
|
|
1507
|
+
"falling back to method='poisson' (surface reconstruction from "
|
|
1508
|
+
"the point cloud, no known topology to reuse).",
|
|
1509
|
+
RuntimeWarning,
|
|
1510
|
+
)
|
|
1511
|
+
method = 'poisson'
|
|
1512
|
+
|
|
1513
|
+
if method == 'none':
|
|
1514
|
+
ms = ml.MeshSet()
|
|
1515
|
+
ms.add_mesh(ml.Mesh(
|
|
1516
|
+
vertex_matrix=np.asarray(deformed_pcd, dtype=float),
|
|
1517
|
+
face_matrix=np.asarray(original_faces, dtype=np.int32),
|
|
1518
|
+
))
|
|
1519
|
+
ms.save_current_mesh(filename)
|
|
1520
|
+
return ms.current_mesh()
|
|
1521
|
+
|
|
1522
|
+
if method == 'poisson':
|
|
1523
|
+
# Create MeshSet and add point cloud
|
|
1524
|
+
ms = ml.MeshSet()
|
|
1525
|
+
point_cloud_mesh = ml.Mesh(vertex_matrix=deformed_pcd)
|
|
1526
|
+
ms.add_mesh(point_cloud_mesh)
|
|
1527
|
+
|
|
1528
|
+
# Estimate normals from local geometry of deformed point cloud
|
|
1529
|
+
ms.compute_normal_for_point_clouds(k=20, smoothiter=2)
|
|
1530
|
+
|
|
1531
|
+
# Perform Poisson surface reconstruction using estimated normals
|
|
1532
|
+
ms.generate_surface_reconstruction_screened_poisson(
|
|
1533
|
+
depth=depth,
|
|
1534
|
+
fulldepth=fulldepth,
|
|
1535
|
+
scale=scale
|
|
1536
|
+
)
|
|
1537
|
+
|
|
1538
|
+
# Compute normals for the reconstructed mesh (for smooth rendering)
|
|
1539
|
+
ms.compute_normal_for_point_clouds()
|
|
1540
|
+
|
|
1541
|
+
# Save the mesh to file
|
|
1542
|
+
ms.save_current_mesh(filename)
|
|
1543
|
+
|
|
1544
|
+
# Return the mesh object
|
|
1545
|
+
return ms.current_mesh()
|
|
1546
|
+
|
|
1547
|
+
if method == 'ball_pivoting':
|
|
1548
|
+
ms = ml.MeshSet()
|
|
1549
|
+
ms.add_mesh(ml.Mesh(vertex_matrix=deformed_pcd))
|
|
1550
|
+
|
|
1551
|
+
# Ball pivoting needs oriented normals, same as Poisson
|
|
1552
|
+
ms.compute_normal_for_point_clouds(k=20, smoothiter=2)
|
|
1553
|
+
|
|
1554
|
+
ms.generate_surface_reconstruction_ball_pivoting(
|
|
1555
|
+
ballradius=ml.PercentageValue(ball_radius),
|
|
1556
|
+
clustering=ball_clustering,
|
|
1557
|
+
creasethr=ball_creasethr,
|
|
1558
|
+
deletefaces=ball_deletefaces,
|
|
1559
|
+
)
|
|
1560
|
+
|
|
1561
|
+
ms.save_current_mesh(filename)
|
|
1562
|
+
return ms.current_mesh()
|
|
1563
|
+
|
|
1564
|
+
if method == 'alpha_shape':
|
|
1565
|
+
ms = ml.MeshSet()
|
|
1566
|
+
ms.add_mesh(ml.Mesh(vertex_matrix=deformed_pcd))
|
|
1567
|
+
|
|
1568
|
+
# Purely geometric (Delaunay + circumradius) -- no normals needed
|
|
1569
|
+
ms.generate_alpha_shape(
|
|
1570
|
+
alpha=ml.PercentageValue(alpha),
|
|
1571
|
+
filtering=alpha_filtering,
|
|
1572
|
+
)
|
|
1573
|
+
|
|
1574
|
+
ms.save_current_mesh(filename)
|
|
1575
|
+
return ms.current_mesh()
|
|
1576
|
+
|
|
1577
|
+
raise ValueError(
|
|
1578
|
+
f"Unknown method {method!r}; expected 'none', 'poisson', "
|
|
1579
|
+
f"'ball_pivoting', or 'alpha_shape'"
|
|
1394
1580
|
)
|
|
1395
1581
|
|
|
1396
|
-
# Compute normals for the reconstructed mesh (for smooth rendering)
|
|
1397
|
-
ms.compute_normal_for_point_clouds()
|
|
1398
|
-
|
|
1399
|
-
# Save the mesh to file
|
|
1400
|
-
ms.save_current_mesh(filename)
|
|
1401
|
-
|
|
1402
|
-
# Return the mesh object
|
|
1403
|
-
result_mesh = ms.current_mesh()
|
|
1404
|
-
|
|
1405
|
-
return result_mesh
|
|
1406
|
-
|
|
1407
1582
|
def export_mesh_vtk(filepath, deformed_pcd, densities, depth=8):
|
|
1408
1583
|
"""
|
|
1409
1584
|
Create and export a Poisson mesh in VTK format with density vertex attribute.
|
{diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram/visualization.py
RENAMED
|
@@ -236,10 +236,10 @@ def animate_grid_deformation(export_folder='vderm_exports',
|
|
|
236
236
|
|
|
237
237
|
|
|
238
238
|
def animate_surface_deformation(export_folder='vderm_exports',
|
|
239
|
-
subfolder='vderm_surface',
|
|
239
|
+
subfolder='vderm_surface',
|
|
240
240
|
output_file='surface_animation.gif',
|
|
241
241
|
fps=5,
|
|
242
|
-
subsample=
|
|
242
|
+
subsample=None,
|
|
243
243
|
show_normals=False,
|
|
244
244
|
alpha=0.6,
|
|
245
245
|
figsize=(10, 8)):
|
|
@@ -256,13 +256,16 @@ def animate_surface_deformation(export_folder='vderm_exports',
|
|
|
256
256
|
Output animation filename (.gif or .mp4)
|
|
257
257
|
fps : int, default=5
|
|
258
258
|
Frames per second
|
|
259
|
-
subsample : int or None, default=
|
|
260
|
-
|
|
259
|
+
subsample : int or None, default=None
|
|
260
|
+
If given, subsample to this many points for faster rendering. Default
|
|
261
|
+
is no subsampling -- use every exported point, for the densest
|
|
262
|
+
(most informative) animation. Set this if rendering is too slow on a
|
|
263
|
+
very large point cloud.
|
|
261
264
|
show_normals : bool, default=False
|
|
262
265
|
If True, draw normal vectors (slower, may clutter visualization)
|
|
263
266
|
figsize : tuple, default=(10, 8)
|
|
264
267
|
Figure size in inches
|
|
265
|
-
|
|
268
|
+
|
|
266
269
|
Returns
|
|
267
270
|
-------
|
|
268
271
|
None
|
{diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2/diffusion_cartogram.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: diffusion-cartogram
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Volumetric Density-Equalizing Reference Map — 3D shape deformation and 2D cartogram generation
|
|
5
5
|
Author-email: Jonah Spector <spector.jo@northeastern.edu>
|
|
6
6
|
License: MIT License
|
|
@@ -56,6 +56,7 @@ Requires-Dist: geopandas>=0.12; extra == "all"
|
|
|
56
56
|
Requires-Dist: rasterio>=1.3; extra == "all"
|
|
57
57
|
Requires-Dist: shapely>=2.0; extra == "all"
|
|
58
58
|
Requires-Dist: pymeshlab>=2023.12; extra == "all"
|
|
59
|
+
Dynamic: license-file
|
|
59
60
|
|
|
60
61
|
# diffusion-cartogram
|
|
61
62
|
[](https://badge.fury.io/py/diffusion-cartogram)
|
{diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram.egg-info/SOURCES.txt
RENAMED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
CHANGELOG.md
|
|
1
2
|
LICENSE
|
|
3
|
+
MANIFEST.in
|
|
2
4
|
README.md
|
|
3
5
|
README_pypi.md
|
|
4
6
|
pyproject.toml
|
|
@@ -11,9 +13,4 @@ diffusion_cartogram.egg-info/PKG-INFO
|
|
|
11
13
|
diffusion_cartogram.egg-info/SOURCES.txt
|
|
12
14
|
diffusion_cartogram.egg-info/dependency_links.txt
|
|
13
15
|
diffusion_cartogram.egg-info/requires.txt
|
|
14
|
-
diffusion_cartogram.egg-info/top_level.txt
|
|
15
|
-
tests/test_core.py
|
|
16
|
-
tests/test_core_2d.py
|
|
17
|
-
tests/test_interpolation.py
|
|
18
|
-
tests/test_io.py
|
|
19
|
-
tests/test_vizualization.py
|
|
16
|
+
diffusion_cartogram.egg-info/top_level.txt
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "diffusion-cartogram"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Volumetric Density-Equalizing Reference Map — 3D shape deformation and 2D cartogram generation"
|
|
9
9
|
readme = "README_pypi.md"
|
|
10
10
|
requires-python = ">=3.8"
|