{"id":15210810,"url":"https://github.com/vleue/polyanya","last_synced_at":"2025-05-14T21:07:07.746Z","repository":{"id":56849105,"uuid":"525140760","full_name":"vleue/polyanya","owner":"vleue","description":"Pathfinding using Polyanya","archived":false,"fork":false,"pushed_at":"2025-05-11T22:16:51.000Z","size":2103,"stargazers_count":373,"open_issues_count":11,"forks_count":25,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-11T22:27:21.207Z","etag":null,"topics":["any-angle","game-development","navmesh","path-planning","pathfinding","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vleue.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE-APACHE","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,"zenodo":null},"funding":{"github":"mockersf","ko_fi":"mockersf"}},"created_at":"2022-08-15T21:13:43.000Z","updated_at":"2025-05-11T22:15:40.000Z","dependencies_parsed_at":"2024-03-09T14:47:06.839Z","dependency_job_id":"8d01c5ab-65db-45df-a607-0f3d26426f49","html_url":"https://github.com/vleue/polyanya","commit_stats":{"total_commits":207,"total_committers":10,"mean_commits":20.7,"dds":0.07729468599033817,"last_synced_commit":"dde70a232c055ea121b93d0033fb5fe6f3fa52d9"},"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vleue%2Fpolyanya","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vleue%2Fpolyanya/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vleue%2Fpolyanya/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vleue%2Fpolyanya/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vleue","download_url":"https://codeload.github.com/vleue/polyanya/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254227612,"owners_count":22035669,"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":["any-angle","game-development","navmesh","path-planning","pathfinding","rust"],"created_at":"2024-09-28T08:01:32.458Z","updated_at":"2025-05-14T21:07:02.648Z","avatar_url":"https://github.com/vleue.png","language":"Rust","funding_links":["https://github.com/sponsors/mockersf","https://ko-fi.com/mockersf"],"categories":["Rust"],"sub_categories":[],"readme":"# Polyanya - Compromise-free Pathfinding on a Navigation Mesh\n\n![MIT/Apache 2.0](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)\n[![Release Doc](https://docs.rs/polyanya/badge.svg)](https://docs.rs/polyanya)\n[![Crate](https://img.shields.io/crates/v/polyanya.svg)](https://crates.io/crates/polyanya)\n\nImplementation of [Polyanya](https://www.ijcai.org/proceedings/2017/0070.pdf) in Rust! Polyanya is a [any-angle path planning](https://en.wikipedia.org/wiki/Any-angle_path_planning) algorithm.\n\nWASM demos made with [Bevy](https://bevyengine.org) are available [here](https://vleue.github.io/vleue_navigator/).\n\n## Features\n\n- **Pathfinding using Polyanya**: Efficient any-angle path planning algorithm for navigation meshes.\n- **Multi-layer Navigation Mesh Support**: \n  - Overlapping navmeshes for 3D navigation (floors, bridges, ...).\n  - One-way layers for directional movement.\n  - Conditional layer traversal with the ability to enable or disable layers.\n  - Layers with different traversal costs for more realistic pathfinding.\n\n## Usage\n\nNavigation meshes can be built by specifying their outer edges and inner obstacles:\n\n```rust\nuse glam::vec2;\nuse polyanya::*;\n\n// Build a mesh from its outer edge\nlet triangulation = Triangulation::from_outer_edges(\u0026[\n    vec2(0., 6.), vec2(2., 5.),\n    vec2(2., 4.), vec2(1., 4.),\n    vec2(1., 3.), vec2(2., 1.),\n    vec2(4., 1.), vec2(4., 2.),\n    vec2(7., 4.), vec2(7., 0.),\n    vec2(12., 0.), vec2(12., 3.),\n    vec2(11., 3.), vec2(11., 5.),\n    vec2(13., 5.), vec2(13., 7.),\n    vec2(10., 7.), vec2(11., 8.),\n    vec2(7., 8.), vec2(7., 7.),\n    vec2(5., 7.), vec2(5., 8.),\n    vec2(0., 8.),\n]);\nlet mesh = triangulation.as_navmesh();\n\n// Get the path between two points\nlet from = vec2(12.0, 0.0);\nlet to = vec2(3.0, 1.0);\nlet path = mesh.path(from, to);\n\nassert_eq!(\n    path.unwrap().path,\n    vec![\n        vec2(7.0, 4.0),\n        vec2(4.0, 2.0),\n        vec2(3.0, 1.0)\n    ]\n);\n```\n\nThey can also be built by manually specifying vertices and polygons for complete control. The following produces the same mesh\n\n```rust\nuse glam::vec2;\nuse polyanya::*;\n\n// Build a mesh from a list of vertices and polygons\nlet mesh = Mesh::new(\n    vec![\n        Vertex::new(vec2(0., 6.), vec![0, u32::MAX]),           // 0\n        Vertex::new(vec2(2., 5.), vec![0, u32::MAX, 2]),        // 1\n        Vertex::new(vec2(5., 7.), vec![0, 2, u32::MAX]),        // 2\n        Vertex::new(vec2(5., 8.), vec![0, u32::MAX]),           // 3\n        Vertex::new(vec2(0., 8.), vec![0, u32::MAX]),           // 4\n        Vertex::new(vec2(1., 4.), vec![1, u32::MAX]),           // 5\n        Vertex::new(vec2(2., 1.), vec![1, u32::MAX]),           // 6\n        Vertex::new(vec2(4., 1.), vec![1, u32::MAX]),           // 7\n        Vertex::new(vec2(4., 2.), vec![1, u32::MAX, 2]),        // 8\n        Vertex::new(vec2(2., 4.), vec![1, 2, u32::MAX]),        // 9\n        Vertex::new(vec2(7., 4.), vec![2, u32::MAX, 4]),        // 10\n        Vertex::new(vec2(10., 7.), vec![2, 4, 6, u32::MAX, 3]), // 11\n        Vertex::new(vec2(7., 7.), vec![2, 3, u32::MAX]),        // 12\n        Vertex::new(vec2(11., 8.), vec![3, u32::MAX]),          // 13\n        Vertex::new(vec2(7., 8.), vec![3, u32::MAX]),           // 14\n        Vertex::new(vec2(7., 0.), vec![5, 4, u32::MAX]),        // 15\n        Vertex::new(vec2(11., 3.), vec![4, 5, u32::MAX]),       // 16\n        Vertex::new(vec2(11., 5.), vec![4, u32::MAX, 6]),       // 17\n        Vertex::new(vec2(12., 0.), vec![5, u32::MAX]),          // 18\n        Vertex::new(vec2(12., 3.), vec![5, u32::MAX]),          // 19\n        Vertex::new(vec2(13., 5.), vec![6, u32::MAX]),          // 20\n        Vertex::new(vec2(13., 7.), vec![6, u32::MAX]),          // 21\n        Vertex::new(vec2(1., 3.), vec![1, u32::MAX]),           // 22\n    ],\n    vec![\n        Polygon::new(vec![0, 1, 2, 3, 4], true),           // 0\n        Polygon::new(vec![5, 22, 6, 7, 8, 9], true),       // 1\n        Polygon::new(vec![1, 9, 8, 10, 11, 12, 2], false), // 2\n        Polygon::new(vec![12, 11, 13, 14], true),          // 3\n        Polygon::new(vec![10, 15, 16, 17, 11], false),     // 4\n        Polygon::new(vec![15, 18, 19, 16], true),          // 5\n        Polygon::new(vec![11, 17, 20, 21], true),          // 6\n    ],\n).unwrap();\n```\n\nThe code above will build the following mesh, with polygons marked in green, and vertices in red:\n\n![example mesh](https://raw.githubusercontent.com/vleue/polyanya/main/example-mesh.png)\n\n## Original Work\n\nCheck the [cpp implementation](https://bitbucket.org/dharabor/pathfinding/src/master/anyangle/polyanya/).\n\n```notrust\nindex;micro;successor_calls;generated;pushed;popped;pruned_post_pop;length;gridcost\n0;4960.92;6974;4368;4313;3823;21;1123.222637572437;1199.73\n```\n\nThis crate seems to generate a few more nodes, but tends to be faster than the cpp implementation. There are a few known cases to still improve it:\n\n* collinear optimisation, when a search node root and interval are all on a same line\n* triangle optimisation, when searching in a triangle polygon\n* when an intersection is very close to a vertex, it sometimes generates an extra slim search node\n* searching start and end nodes is costlier\n\nCompiling this crate with feature `stats` will output almost the same level of information as the default cpp implementation output.\n\n```notrust\nindex;micros;successor_calls;generated;pushed;popped;pruned_post_pop;length\n0;2990.083;6983;7748;4314;3828;21;1123.2228\n```\n\nThe `verbose` feature will give the same output as [setting `verbose` to `1`](https://bitbucket.org/dharabor/pathfinding/src/ce5b02e9d051d5f17addb359429104c0293decaf/anyangle/polyanya/scenariorunner.cpp#lines-20).\n\n```notrust\n        pushing: root=(993, 290); left=(989, 303); right=(1001, 288); f=1020.21, g=0.00\n        pushing: root=(993, 290); left=(984, 301); right=(988, 303); f=1016.98, g=0.00\n        pushing: root=(993, 290); left=(982, 300); right=(984, 301); f=1016.06, g=0.00\n        pushing: root=(993, 290); left=(994, 285); right=(981, 299); f=1014.84, g=0.00\npopped off: root=(993, 290); left=(994, 285); right=(981, 299); f=1014.84, g=0.00\n        intermediate: root=(993, 290); left=(988, 282); right=(981, 299); f=1014.84, g=0.00\n        pushing: root=(993, 290); left=(977, 299); right=(980, 299); f=1015.14, g=0.00\n        pushing: root=(993, 290); left=(984, 280); right=(976, 297); f=1014.84, g=0.00\npopped off: root=(993, 290); left=(984, 280); right=(976, 297); f=1014.84, g=0.00\n        pushing: root=(993, 290); left=(973, 296); right=(976, 297); f=1014.84, g=0.00\n        pushing: root=(993, 290); left=(970, 295); right=(973, 296); f=1014.86, g=0.00\n        pushing: root=(993, 290); left=(967, 294); right=(970, 295); f=1015.01, g=0.00\n        pushing: root=(993, 290); left=(965, 293); right=(967, 294); f=1015.28, g=0.00\n        pushing: root=(993, 290); left=(977, 276); right=(965, 293); f=1015.58, g=0.00\n        pushing: root=(993, 290); left=(983, 279); right=(979, 277); f=1023.95, g=0.00\npopped off: root=(993, 290); left=(973, 296); right=(976, 297); f=1014.84, g=0.00\npopped off: root=(993, 290); left=(970, 295); right=(973, 296); f=1014.86, g=0.00\npopped off: root=(993, 290); left=(967, 294); right=(970, 295); f=1015.01, g=0.00\npopped off: root=(993, 290); left=(977, 299); right=(980, 299); f=1015.14, g=0.00\npopped off: root=(993, 290); left=(965, 293); right=(967, 294); f=1015.28, g=0.00\npopped off: root=(993, 290); left=(977, 276); right=(965, 293); f=1015.58, g=0.00\n        pushing: root=(993, 290); left=(963, 292); right=(965, 293); f=1015.58, g=0.00\n        pushing: root=(993, 290); left=(961, 291); right=(963, 292); f=1015.94, g=0.00\n        pushing: root=(993, 290); left=(971, 273); right=(959, 289); f=1017.13, g=0.00\n...\n```\n\nThe mesh files used in tests are coming from the cpp implementation and are available under MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvleue%2Fpolyanya","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvleue%2Fpolyanya","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvleue%2Fpolyanya/lists"}