{"id":15287198,"url":"https://github.com/gmac/constellation-js","last_synced_at":"2025-04-13T05:08:08.258Z","repository":{"id":2662109,"uuid":"3653271","full_name":"gmac/constellation-js","owner":"gmac","description":"A grid geometry toolkit for A* pathfinding and 2D sprite motion.","archived":false,"fork":false,"pushed_at":"2023-01-07T05:50:52.000Z","size":1343,"stargazers_count":22,"open_issues_count":11,"forks_count":5,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-13T05:08:02.844Z","etag":null,"topics":["2d","a-star","a-star-algorithm","a-star-path-finding","a-star-search","geometry","pathfinding","sprite"],"latest_commit_sha":null,"homepage":"http://gmac.github.io/constellation-js","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gmac.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-03-07T20:33:02.000Z","updated_at":"2024-11-03T09:49:37.000Z","dependencies_parsed_at":"2023-01-13T12:00:31.748Z","dependency_job_id":null,"html_url":"https://github.com/gmac/constellation-js","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gmac%2Fconstellation-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gmac%2Fconstellation-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gmac%2Fconstellation-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gmac%2Fconstellation-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gmac","download_url":"https://codeload.github.com/gmac/constellation-js/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248665747,"owners_count":21142123,"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":["2d","a-star","a-star-algorithm","a-star-path-finding","a-star-search","geometry","pathfinding","sprite"],"created_at":"2024-09-30T15:26:31.606Z","updated_at":"2025-04-13T05:08:08.238Z","avatar_url":"https://github.com/gmac.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Constellation.js\n\nA zero-dependency geometry toolkit for controlling 2D sprite motion.\n\n![sample graph](./website/example.png?v=2)\n\nConstellation manages 2D point grids and pathfinding. The library is designed to control sprite motion within a 2D environment. While not an animation library itself, Constellation may be used to manage and search grid geometry, and then feed the resulting point arrays into your preferred animation library. Constellation expands upon the motion control system used in the [What Makes You Tick?](https://www.youtube.com/watch?v=qxngufqrZBE \"What Makes You Tick?\") adventure game series. Features include:\n\n- Point and cell grid management.\n- Point pathfinding with [A-star](http://en.wikipedia.org/wiki/A*_search_algorithm \"A-star\").\n- Cell hit tests with [winding number](http://en.wikipedia.org/wiki/Point_in_polygon \"Point in polygon\").\n- Snapping points to line segments.\n\nSee the [grid builder](http://gmac.github.io/constellation-js \"constellation-js\") demo for an interactive trial.\n\n## Getting started\n\nInstall via NPM:\n\n```shell\nyarn add constellation\n```\n\nImport or require:\n\n```js\nimport { Grid } from 'constellation';\nconst Constellation = require('constellation');\n```\n\n## API Documentation\n\n### Constellation.Point\n\n```js\nimport { Point } from 'constellation';\nconst pt = new Point(100, 100);\n```\n\n**point = new Point(x, y)**\n\nBuilds a Point primitive with the following properties:\n\n- `x`: horizontal coordinate of the point.\n- `y`: vertical coordinate of the point.\n\n**Point.distance(a, b)**\n\nCalculates the distance between two provided `Point` objects.\n\n### Constellation.Rect\n\n```js\nimport { Rect } from 'constellation';\nconst rect = new Rect(10, 10, 100, 100);\n```\n\n**rect = new Rect(x, y, width, height)**\n\nBuilds a Rect primitive with the following properties:\n\n- `x`: horizontal coordinate of the rectangle origin.\n- `y`: vertical coordinate of the rectangle origin.\n- `width`: rectangle width.\n- `height`: rectangle height.\n\n**rect.hitTest(point)**\n\nReturns `true` if the point falls within the rectangle bounds.\n\n### Constellation.ExtendedGrid\n\n```js\nimport { Grid, ExtendedGrid } from 'constellation';\nconst grid = new Grid(data);\nconst exgrid = new ExtendedGrid(grid);\nconst path = exgrid.route(new Point(10, 10), new Point(100, 100));\n```\n\n**exgrid = new ExtendedGrid(grid)**\n\nBuilds a new `ExtendedGrid` instance that wraps a basic `Grid`. An extended grid allows arbitrary points outside of the base grid to be routed between using the geometry of the base grid.\n\n**exgrid.route(a, b, confineToGrid?)**\n\nReceives basic points A and B, and builds a path of points between them that follows the constraints of the base grid geometry. Start point A is always connected into the grid at the nearest valid position. End point B will be constrained to fall within grid geometry unless `confineToGrid` is specified as false. In either scenario, A and B will connect directly if their constrained positions fall within a common cell or along a common line segment. Otherwise, they will attach to their nearest geometry features and routing follows the grid to navigate between them. Returns an array of Point objects definiting the route.\n\n### Constellation.Grid\n\n```js\nimport { Grid } from 'constellation';\nconst grid = new Grid(data);\n```\n\n**grid = new Grid(data?)**\n\nBuilds a new `Grid` instance. A `Grid` object manages a collection of `Node` and `Cell` geometry objects. Accepts an optional grid data structure to build the grid with; this data structure is generated using `grid.toConfig()`.\n\n**grid.toConfig()**\n\nPrints the grid as a plain data structure that may be serialized as JSON. This grid data may be passed to the `Grid` constructor to reconstitue the grid.\n\n**grid.addNode(x, y, data?)**\n\nAdds a new `Node` object with specified X and Y coordinates, and an optional data object. Returns the new `Node` object. An additional data object may be provided; if the data object contains an `id` property, that id will be assigned as the node id.\n\n**grid.getNode(id)**\n\nGets a node by id reference. Returns a `Node` object, or `null` for missing ids.\n\n**grid.hasNodes([id, ...])**\n\nTests if all of the specified node ids exist in the grid.\n\n**grid.nodeCount**\n\nSpecifies the number of nodes in the grid.\n\n**grid.joinNodes([id1, id2, ...])**\n\nJoins an array of two or more node ids with connections. Returns `true` if changes are made.\n\n**grid.splitNodes([id1, id2, ...])**\n\nBreaks the connections among an array of two or more node ids. Returns `true` if changes are made.\n\n**grid.detachNodes([id, ...])**\n\nSplits an array of node ids from all of their respective connections. Returns `true` if changes are made.\n\n**grid.removeNodes([id, ...])**\n\nDetaches an array of node ids, then removes them each from the grid. Any dependent grid cells are also removed. Returns `true` if changes are made.\n\n**grid.addCell([nodeId, ...], data?)**\n\nCreates a new `Cell` from three or more node ids. Returns the new `Cell` object, or null if no cell was created. An additional data object may be provided; if the data object contains an `id` property, that id will be assigned as the cell id.\n\n**grid.getCell(id)**\n\nGets a cell by id reference. Returns a `Cell` object, or `null` for missing ids.\n\n**grid.nodesForCell(id)**\n\nGets an array of `Node` objects defining the point ring of the given cell id.\n\n**grid.cellCount**\n\nSpecifies the number of cells in the grid.\n\n**grid.removeCells([id, ...])**\n\nRemoves an array of cell ids from the grid. All nodes assocated with the removed cells remain unchanged. Returns `true` if changes are made.\n\n**grid.findPath({ options })**\n\n```ts\ngrid.findPath({\n  start: string,\n  goal: string,\n  costForSegment?: (a: Node, b: Node) =\u003e number,\n  costEstimateToGoal?: (a: Node, b: Node) =\u003e number,\n  bestCandidatePath?: (a: Path, b: Path) =\u003e Path,\n});\n```\n\nTakes `start` and `goal` node ids, then finds the shortest path between them. Routing favors the shortest path based on coordinate geometry by default. You may customize path routing using the optional weight and estimate functions:\n\n- `costForSegment`: used to calculate the weight (or cost) of each new grid segment added to a path. Receives two `Node` objects as arguments: the previous search node, and the current search node. Returns a numeric weight for each path segment. The pathfinder returns a path that accrues the lowest total weight; `Point.distance` is the default measure.\n\n- `costEstimateToGoal`: provides a best-case scenario estimate for each node's cost to reach the goal. Receives two `Node` objects as arguments: the current search node, and the goal node. Returns a numeric estimated cost-to-goal. The pathfinder prioritizes paths that estimate the lowest total weight; `Point.distance` is the default measure.\n\n- `bestCandidatePath`: once a path to goal is reached, subsequent paths discovered with _equal_ cost will use this tiebreaker to select which path to return. Favors the first discovered path by default.\n\n**grid.snapPointToGrid(point)**\n\nSnaps the provided `Point` to the nearest position among all joined line segments within the grid. The snapped point will be plotted along the nearest available line segment. Returns line segment _AB_ (if available) with the point _P_ plotted along it:\n\n- `a`: grid node _A_ of line segment.\n- `b`: grid node _B_ of line segment.\n- `p`: the snapped `Point` object.\n\n**grid.nearestNodeToNode(id)**\n\nFinds and returns the closest other grid `Node` to the specified node id.\n\n**grid.nearestNodeToPoint(point)**\n\nFinds and returns the closest grid `Node` to the specified point.\n\n**grid.cellsContainingPoint(point)**\n\nTests a `Point` object for intersections with all `Polygon` objects in the grid, then returns an array of polygon ids that encompass the point.\n\n**grid.nodesInCell(id)**\n\nReturns an array of grid `Node` objects that compose the shape of the given cell id, or those that fall within its bounds.\n\n**grid.nodesInRect(rect)**\n\nReturns an array of grid `Node` objects that fall within the bounds of the given rectangle.\n\n### Constellation.Node\n\n```js\nimport { Grid } from 'constellation';\nconst grid = new Grid();\nconst node = grid.addNode(100, 100, { myMetadata: 23 });\n```\n\n**Node (constructor)**\n\nUse `grid.addNode();` to create and manage nodes. Nodes are just `Point` objects with additional attributes, therefore they may be used directly with any methods that recieve `Point` arguments. Grid nodes have the following properties:\n\n- `id`: unique identifier for the node.\n- `x`: horizontal coordinate of the node.\n- `y`: vertical coordinate of the node.\n- `to`: table of connections to other nodes.\n- `data?`: a freeform data object with meta attributes for the node.\n\n### Constellation.Cell\n\n```js\nimport { Grid } from 'constellation';\nconst grid = new Grid();\nconst a = grid.addNode(0, 0);\nconst b = grid.addNode(100, 100);\nconst c = grid.addNode(150, 75);\nconst cell = grid.addCell([a.id, b.id, c.id], { myMetadata: 77 });\n```\n\n**Cell (constructor)**\n\nUse `grid.addCell();` to create and manage cells. Grid cells have the following properties:\n\n- `id`: unique identifier for the cell.\n- `rels`: an array of node ids defining the point ring.\n- `data?`: a freeform data object with meta attributes for the cell.\n\n### Constellation.Path\n\n```js\nimport { Grid } from 'constellation';\nconst grid = new Grid();\nconst a = grid.addNode(0, 0);\nconst b = grid.addNode(50, 50);\nconst c = grid.addNode(100, 100);\ngrid.joinNodes([a.id, b.id]);\ngrid.joinNodes([b.id, c.id]);\nconst path = grid.findPath({ start: a.id, goal: c.id });\n```\n\nUse `grid.findPath();` to create paths. Grid paths have the following properties:\n\n- `nodes`: an array of grid `Node` objects followed by this path.\n- `weight`: a numeric weight of the completed path. Uses coordinate geometry distances by default.\n- `estimate`: a numeric estimate of the path's cost to completion. Should match weight in completed paths.\n\n### Utilities\n\n```js\nimport { intersect } from 'constellation';\nconst x = intersect(new Point(0, 0), new Point(100, 100), new Point(100, 0), new Point(0, 100));\n```\n\n**ccw(pointA, pointB, pointC)**\n\nTests for counter-clockwise winding among three `Point` objects. Returns true if the three points trend in a counter-clockwise arc. Useful for testing line intersections.\n\n**intersect(pointA, pointB, pointC, pointD)**\n\nTests for intersection between line segments AB and CD. Returns true if the line segments intersect.\n\n**degreesToRadians(degrees)**\n\nConverts [degrees](http://en.wikipedia.org/wiki/Degree_%28angle%29 \"Degrees\") to [radians](http://en.wikipedia.org/wiki/Radian \"Radians\").\n\n**radiansToDegrees(radians)**\n\nConverts [radians](http://en.wikipedia.org/wiki/Radian \"Radians\") to [degrees](http://en.wikipedia.org/wiki/Degree_%28angle%29 \"Degrees\").\n\n**angleRadians(pointA, pointB)**\n\nCalculates the angle (in radians) between line segment AB and the [positive X-origin axis](http://en.wikipedia.org/wiki/Origin_%28mathematics%29 \"Origin axis\"). Accepts two `Point` objects and returns the angle in [radians](http://en.wikipedia.org/wiki/Radian \"Radians\").\n\n**angleDegrees(pointA, pointB)**\n\nCalculates the angle (in degrees) between line segment AB and the [positive X-origin axis](http://en.wikipedia.org/wiki/Origin_%28mathematics%29 \"Origin axis\"). Accepts two `Point` objects and returns the angle in [degrees](http://en.wikipedia.org/wiki/Degree_%28angle%29 \"Degrees\").\n\n**angleSector(radians, sectors?, offsetRadians?)**\n\nGets the [circular sector](http://en.wikipedia.org/wiki/Circular_sector \"Circular Sector\") index that an angle falls into. You may specify how many sectors to divide the circle into, and then plot an angle among those breaks. This is useful for applying orientation view states to a sprite while moving it around a grid; for example: given a sprite with 4 walk cycles for different orientations (left, front, right, back), use this method to select one of the four views based on the sprite's next angle of motion.\n\nRequires an angle to be provided in radians. You may optionally specify the number of sectors to divide the circle into, the default is 8. Also accepts an optional offset (in radians) used to shift sector divisions off the [positive X-origin axis](http://en.wikipedia.org/wiki/Origin_%28mathematics%29 \"Origin axis\"). By default, offset is configured as one-half of the sector size, which centers the X-origin axis within the first sector. Returns an index between `0` and `N-1`, where N is the number of sectors.\n\n**boundingRectForPoints([point, ...])**\n\nReceives an array of `Point` objects and returns a `Rect` comprising their bounding box.\n\n**hitTestPointRing(pointP, [point, ...])**\n\nReceives a target point P and an array of points defining a ring. Returns true if P falls within the ring of points. Hit test is performed using [ray casting](http://en.wikipedia.org/wiki/Point_in_polygon \"ray casting\").\n\n**snapPointToLineSegment(pointP, pointA, pointB)**\n\nReceives target point P, and snaps it to the nearest point along line segment AB.\n\n**nearestPointToPoint(pointP, [point, ...])**\n\nReceives target point P and an array of points to search. Returns the nearest point to P within the array of points, using a basic [nearest neighbor](http://en.wikipedia.org/wiki/Closest_pair_of_points_problem \"Nearest neighbor\") search.\n\n## Data graphs\n\nWhile Constellation is designed to manage 2D coordinate geometry, it also provides support for managing node-based data graphs that can be searched using a-star. For example, let's set up a simple social graph using Constellation's node data API:\n\n```js\nimport { Grid } from 'constellation';\n\n// Create a social graph with \"mom\", \"sister\", \"brother\", and a \"friend\":\nconst grid = new Grid();\ngrid.addNode(0, 0, { id:'mom', age:50 });\ngrid.addNode(0, 0, { id:'sister', age:16 });\ngrid.addNode(0, 0, { id:'brother', age:14 });\ngrid.addNode(0, 0, { id:'friend', age:14 });\n\n// Create two social rings:\ngrid.joinNodes('mom', 'sister', 'brother');\ngrid.joinNodes('mom', 'brother', 'friend');\n```\n\nThe above defines a graph of data without real coordinates. This graph can be intelligently searched using the *costForSegment* and *costEstimateToGoal* pathfinder functions; for example, the following finds a path with the lowest age:\n\n```js\nvar path = grid.findPath({\n  start: 'sister',\n  goal: 'friend',\n  costForSegment: (last, current) =\u003e last.data.age + current.data.age,\n  costEstimateToGoal: (current, goal) =\u003e goal.data.age,\n});\n// result array: [\"sister\" \u003e \"brother\" \u003e \"friend\"]\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgmac%2Fconstellation-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgmac%2Fconstellation-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgmac%2Fconstellation-js/lists"}