{"id":18174810,"url":"https://github.com/kylebarron/pydelatin","last_synced_at":"2025-04-30T11:22:58.508Z","repository":{"id":45608052,"uuid":"282366277","full_name":"kylebarron/pydelatin","owner":"kylebarron","description":"Python bindings to `hmm` for fast terrain mesh generation","archived":false,"fork":false,"pushed_at":"2024-09-03T22:06:46.000Z","size":1704,"stargazers_count":70,"open_issues_count":6,"forks_count":8,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-25T12:55:20.216Z","etag":null,"topics":["elevation","heightmap","mesh","mesh-generation","numpy","terrain"],"latest_commit_sha":null,"homepage":"https://kylebarron.dev/quantized-mesh-encoder","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kylebarron.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-25T03:55:53.000Z","updated_at":"2025-04-16T10:32:14.000Z","dependencies_parsed_at":"2025-01-04T08:27:44.575Z","dependency_job_id":"2c100826-1f9f-465a-8146-e3b401340e93","html_url":"https://github.com/kylebarron/pydelatin","commit_stats":{"total_commits":99,"total_committers":3,"mean_commits":33.0,"dds":0.04040404040404044,"last_synced_commit":"49ffad710548f1bd2839bf49b97a44431d88a382"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylebarron%2Fpydelatin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylebarron%2Fpydelatin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylebarron%2Fpydelatin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylebarron%2Fpydelatin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kylebarron","download_url":"https://codeload.github.com/kylebarron/pydelatin/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251689109,"owners_count":21627843,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elevation","heightmap","mesh","mesh-generation","numpy","terrain"],"created_at":"2024-11-02T16:07:49.729Z","updated_at":"2025-04-30T11:22:58.485Z","avatar_url":"https://github.com/kylebarron.png","language":"C","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# pydelatin\n\nA Python wrapper of [`hmm`][hmm] (of which [Delatin][delatin] is a port) for fast terrain mesh generation.\n\n[![][image_url]][example]\n\n[image_url]: https://raw.githubusercontent.com/kylebarron/pydelatin/master/assets/glac.jpg\n[example]: https://kylebarron.dev/quantized-mesh-encoder\n\n[hmm]: https://github.com/fogleman/hmm\n[delatin]: https://github.com/mapbox/delatin\n\nA screenshot of Glacier National Park taken from [the demo][example]. The mesh\nis created using `pydelatin`, encoded using\n[`quantized-mesh-encoder`][quantized-mesh-encoder], served on-demand using\n[`dem-tiler`][dem-tiler], and rendered with [deck.gl](https://deck.gl).\n\n[quantized-mesh-encoder]: https://github.com/kylebarron/quantized-mesh-encoder\n[dem-tiler]: https://github.com/kylebarron/dem-tiler\n\n## Install\n\nWith pip:\n\n```\npip install pydelatin\n```\n\nor with Conda:\n\n```\nconda install -c conda-forge pydelatin\n```\n\nOn Windows, installing via Conda is strongly recommended.\n\nIf installing with pip on Windows, [`glm`][glm] is a prerequisite for building\nfrom source. Open an issue if you'd like to help package binary wheels for\nWindows.\n\n[glm]: https://glm.g-truc.net/\n\n## Using\n\n### Example\n\n```py\nfrom pydelatin import Delatin\n\ntin = Delatin(terrain, width, height)\n# Mesh vertices\ntin.vertices\n# Mesh triangles\ntin.triangles\n```\n\n### API\n\nThe API is similar to that of [`hmm`][hmm].\n\nAdditionally I include a helper function: `decode_ele`, to decode a Mapbox\nTerrain RGB or Terrarium PNG array to elevations.\n\n#### `Delatin`\n\n##### Arguments\n\n- `arr` (numpy `ndarray`): data array. If a 2D array, dimensions are expected to be (height, width). If a 1D array, height and width parameters must be passed, and the array is assumed to be in C order.\n- `height` (`int`, default: `None`): height of array; required when arr is not 2D\n- `width` (`int`, default: `None`): width of array; required when arr is not 2D\n- `z_scale` (`float`, default: `1`): z scale relative to x \u0026 y\n- `z_exag` (`float`, default: `1`): z exaggeration\n- `max_error` (`float`, default: `0.001`): maximum triangulation error\n- `max_triangles` (`int`, default: `None`): maximum number of triangles\n- `max_points` (`int`, default: `None`): maximum number of vertices\n- `base_height` (`float`, default: `0`): solid base height\n- `level` (`bool`, default: `False`): auto level input to full grayscale range\n- `invert` (`bool`, default: `False`): invert heightmap\n- `blur` (`int`, default: `0`): gaussian blur sigma\n- `gamma` (`float`, default: `0`): gamma curve exponent\n- `border_size` (`int`, default: `0`): border size in pixels\n- `border_height` (`float`, default: `1`): border z height\n\n##### Attributes\n\n- `vertices` (`ndarray` of shape `(-1, 3)`): the interleaved 3D coordinates of each vertex, e.g. `[[x0, y0, z0], [x1, y1, z1], ...]`.\n- `triangles` (`ndarray` of shape `(-1, 3)`): represents _indices_ within the `vertices` array. So `[0, 1, 3, ...]` would use the first, second, and fourth vertices within the `vertices` array as a single triangle.\n- `error` (`float`): the maximum error of the mesh.\n\n#### `util.rescale_positions`\n\nA helper function to rescale the `vertices` output to a new bounding box.\nReturns an `ndarray` of shape `(-1, 3)` with positions rescaled. Each row\nrepresents a single 3D point.\n\n##### Arguments\n\n- `vertices`: (`np.ndarray`) vertices output from Delatin\n- `bounds`: (`Tuple[float]`) linearly rescale position values to this extent.\n  Expected to be `[minx, miny, maxx, maxy]`.\n- `flip_y`: (`bool`, default `False`) Flip y coordinates. Can be useful since\n  images' coordinate origin is in the top left.\n\n### Saving to mesh formats\n\n#### Quantized Mesh\n\nA common mesh format for the web is the [Quantized Mesh][quantized-mesh-spec]\nformat, which is supported in Cesium and deck.gl (via\n[loaders.gl][loaders.gl-quantized-mesh]). You can use\n[`quantized-mesh-encoder`][quantized-mesh-encoder] to save in this format:\n\n```py\nimport quantized_mesh_encoder\nfrom pydelatin import Delatin\nfrom pydelatin.util import rescale_positions\n\ntin = Delatin(terrain, max_error=30)\nvertices, triangles = tin.vertices, tin.triangles\n\n# Rescale vertices linearly from pixel units to world coordinates\nrescaled_vertices = rescale_positions(vertices, bounds)\n\nwith open('output.terrain', 'wb') as f:\n    quantized_mesh_encoder.encode(f, rescaled_vertices, triangles)\n```\n\n[quantized-mesh-spec]: https://github.com/CesiumGS/quantized-mesh\n[quantized-mesh-encoder]: https://github.com/kylebarron/quantized-mesh-encoder\n[loaders.gl-quantized-mesh]: https://loaders.gl/modules/terrain/docs/api-reference/quantized-mesh-loader\n\n#### Meshio\n\nAlternatively, you can save to a variety of mesh formats using\n[`meshio`][meshio]:\n\n```py\nfrom pydelatin import Delatin\nimport meshio\n\ntin = Delatin(terrain, max_error=30)\nvertices, triangles = tin.vertices, tin.triangles\n\ncells = [(\"triangle\", triangles)]\nmesh = meshio.Mesh(vertices, cells)\n# Example output format\n# Refer to meshio documentation\nmesh.write('foo.vtk')\n```\n\n[meshio]: https://github.com/nschloe/meshio\n\n## `Martini` or `Delatin`?\n\nTwo popular algorithms for terrain mesh generation are the **\"Martini\"**\nalgorithm, found in the JavaScript [`martini`][martini] library and the Python\n[`pymartini`][pymartini] library, and the **\"Delatin\"** algorithm, found in the\nC++ [`hmm`][hmm] library, this Python `pydelatin` library, and the JavaScript\n[`delatin`][delatin] library.\n\nWhich to use?\n\nFor most purposes, use `pydelatin` over `pymartini`. A good breakdown from [a\nMartini issue][martini_desc_issue]:\n\n\u003e Martini:\n\u003e\n\u003e - Only works on square 2^n+1 x 2^n+1 grids.\n\u003e - Generates a hierarchy of meshes (pick arbitrary detail after a single run)\n\u003e - Optimized for meshing speed rather than quality.\n\u003e\n\u003e Delatin:\n\u003e\n\u003e - Works on arbitrary raster grids.\n\u003e - Generates a single mesh for a particular detail.\n\u003e - Optimized for quality (as few triangles as possible for a given error).\n\n[martini]: https://github.com/mapbox/martini\n[pymartini]: https://github.com/kylebarron/pymartini\n[martini_desc_issue]: https://github.com/mapbox/martini/issues/15#issuecomment-700475731\n\n## Benchmark\n\nThe following uses the same dataset as the [`pymartini`\nbenchmarks][pymartini_bench], a 512x512 pixel heightmap of Mt. Fuji.\n\n[pymartini_bench]: https://github.com/kylebarron/pymartini#benchmark\n\nFor the 30-meter mesh, `pydelatin` is 25% slower than `pymartini`, but the mesh\nis much more efficient: it has 40% fewer vertices and triangles.\n\n`pydelatin` is 4-5x faster than the JavaScript `delatin` package.\n\n### Python\n\n```bash\ngit clone https://github.com/kylebarron/pydelatin\ncd pydelatin\npip install '.[test]'\npython bench.py\n```\n\n```\nmesh (max_error=30m): 27.322ms\nvertices: 5668, triangles: 11140\n\nmesh (max_error=1m): 282.946ms\nmesh (max_error=2m): 215.839ms\nmesh (max_error=3m): 163.424ms\nmesh (max_error=4m): 127.203ms\nmesh (max_error=5m): 106.596ms\nmesh (max_error=6m): 91.868ms\nmesh (max_error=7m): 82.572ms\nmesh (max_error=8m): 74.335ms\nmesh (max_error=9m): 65.893ms\nmesh (max_error=10m): 60.999ms\nmesh (max_error=11m): 55.213ms\nmesh (max_error=12m): 54.475ms\nmesh (max_error=13m): 48.662ms\nmesh (max_error=14m): 47.029ms\nmesh (max_error=15m): 44.517ms\nmesh (max_error=16m): 42.059ms\nmesh (max_error=17m): 39.699ms\nmesh (max_error=18m): 37.657ms\nmesh (max_error=19m): 36.333ms\nmesh (max_error=20m): 34.131ms\n```\n\n### JS (Node)\n\nThis benchmarks against the [`delatin`][delatin] JavaScript module.\n\n```bash\ngit clone https://github.com/kylebarron/pydelatin\ncd test/bench_js/\nyarn\nwget https://raw.githubusercontent.com/mapbox/delatin/master/index.js\nnode -r esm bench.js\n```\n\n```\nmesh (max_error=30m): 143.038ms\nvertices: 5668\ntriangles: 11140\n\nmesh (max_error=0m): 1169.226ms\nmesh (max_error=1m): 917.290ms\nmesh (max_error=2m): 629.776ms\nmesh (max_error=3m): 476.958ms\nmesh (max_error=4m): 352.907ms\nmesh (max_error=5m): 290.946ms\nmesh (max_error=6m): 240.556ms\nmesh (max_error=7m): 234.181ms\nmesh (max_error=8m): 188.273ms\nmesh (max_error=9m): 162.743ms\nmesh (max_error=10m): 145.734ms\nmesh (max_error=11m): 130.119ms\nmesh (max_error=12m): 119.865ms\nmesh (max_error=13m): 114.645ms\nmesh (max_error=14m): 101.390ms\nmesh (max_error=15m): 100.065ms\nmesh (max_error=16m): 96.247ms\nmesh (max_error=17m): 89.508ms\nmesh (max_error=18m): 85.754ms\nmesh (max_error=19m): 79.838ms\nmesh (max_error=20m): 75.607ms\n```\n\n## License\n\nThis package wraps \\@fogleman's [`hmm`][hmm], a C++ library that is also\nMIT-licensed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkylebarron%2Fpydelatin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkylebarron%2Fpydelatin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkylebarron%2Fpydelatin/lists"}