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.
Files changed (23) hide show
  1. diffusion_cartogram-0.2.2/CHANGELOG.md +80 -0
  2. diffusion_cartogram-0.2.2/MANIFEST.in +23 -0
  3. {diffusion_cartogram-0.2.1/diffusion_cartogram.egg-info → diffusion_cartogram-0.2.2}/PKG-INFO +3 -2
  4. {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram/__init__.py +3 -1
  5. {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram/core.py +228 -53
  6. {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram/visualization.py +8 -5
  7. {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2/diffusion_cartogram.egg-info}/PKG-INFO +3 -2
  8. {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram.egg-info/SOURCES.txt +3 -6
  9. {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/pyproject.toml +1 -1
  10. diffusion_cartogram-0.2.1/tests/test_core.py +0 -374
  11. diffusion_cartogram-0.2.1/tests/test_core_2d.py +0 -372
  12. diffusion_cartogram-0.2.1/tests/test_interpolation.py +0 -70
  13. diffusion_cartogram-0.2.1/tests/test_io.py +0 -160
  14. diffusion_cartogram-0.2.1/tests/test_vizualization.py +0 -214
  15. {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/LICENSE +0 -0
  16. {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/README.md +0 -0
  17. {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/README_pypi.md +0 -0
  18. {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram/core_2d.py +0 -0
  19. {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram/visualization_2d.py +0 -0
  20. {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram.egg-info/dependency_links.txt +0 -0
  21. {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram.egg-info/requires.txt +0 -0
  22. {diffusion_cartogram-0.2.1 → diffusion_cartogram-0.2.2}/diffusion_cartogram.egg-info/top_level.txt +0 -0
  23. {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
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: diffusion-cartogram
3
- Version: 0.2.1
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
  [![PyPI version](https://badge.fury.io/py/diffusion-cartogram.svg)](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.1'
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, depth=8, fulldepth=5, scale=1.1):
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
- Creates and exports a Poisson mesh from a deformed point cloud.
1335
-
1336
- Normals are automatically estimated from the deformed point cloud geometry
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 cloud to remesh
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 reconstructed mesh object
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 neighbors with 2 smoothing iterations.
1364
- Adjust these in the code if needed for your specific geometry.
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
- >>> # Basic usage
1374
- >>> mesh = export_mesh_file('output.stl', deformed_points)
1375
-
1376
- >>> # Higher quality reconstruction
1377
- >>> mesh = export_mesh_file('output.ply', deformed_points, depth=10)
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('create_pcd')
1380
-
1381
- # Create MeshSet and add point cloud
1382
- ms = ml.MeshSet()
1383
- point_cloud_mesh = ml.Mesh(vertex_matrix=deformed_pcd)
1384
- ms.add_mesh(point_cloud_mesh)
1385
-
1386
- # Estimate normals from local geometry of deformed point cloud
1387
- ms.compute_normal_for_point_clouds(k=20, smoothiter=2)
1388
-
1389
- # Perform Poisson surface reconstruction using estimated normals
1390
- ms.generate_surface_reconstruction_screened_poisson(
1391
- depth=depth,
1392
- fulldepth=fulldepth,
1393
- scale=scale
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.
@@ -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=5000,
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=5000
260
- Subsample to this many points for faster rendering
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
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: diffusion-cartogram
3
- Version: 0.2.1
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
  [![PyPI version](https://badge.fury.io/py/diffusion-cartogram.svg)](https://badge.fury.io/py/diffusion-cartogram)
@@ -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.1"
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"