{"id":16696015,"url":"https://github.com/hexdecimal/python-tcod-camera","last_synced_at":"2025-04-15T12:28:56.337Z","repository":{"id":64525764,"uuid":"576460217","full_name":"HexDecimal/python-tcod-camera","owner":"HexDecimal","description":"Camera helper utilities for grid-based games.","archived":false,"fork":false,"pushed_at":"2025-04-07T21:22:35.000Z","size":70,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-07T21:42:41.262Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","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/HexDecimal.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":"HexDecimal"}},"created_at":"2022-12-09T23:23:11.000Z","updated_at":"2025-04-07T21:22:14.000Z","dependencies_parsed_at":"2023-02-10T13:02:51.954Z","dependency_job_id":"ac256d77-c825-487d-9d21-a5d1bf231a80","html_url":"https://github.com/HexDecimal/python-tcod-camera","commit_stats":{"total_commits":18,"total_committers":1,"mean_commits":18.0,"dds":0.0,"last_synced_commit":"65b7f13e014f8db4c549f5b27d922d507e7f2fe1"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HexDecimal%2Fpython-tcod-camera","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HexDecimal%2Fpython-tcod-camera/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HexDecimal%2Fpython-tcod-camera/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HexDecimal%2Fpython-tcod-camera/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HexDecimal","download_url":"https://codeload.github.com/HexDecimal/python-tcod-camera/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249071505,"owners_count":21207995,"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":[],"created_at":"2024-10-12T17:25:24.600Z","updated_at":"2025-04-15T12:28:56.320Z","avatar_url":"https://github.com/HexDecimal.png","language":"Python","funding_links":["https://github.com/sponsors/HexDecimal"],"categories":[],"sub_categories":[],"readme":"# About\n\n[![PyPI](https://img.shields.io/pypi/v/tcod-camera)](https://pypi.org/project/tcod-camera/)\n[![PyPI - License](https://img.shields.io/pypi/l/tcod-camera)](https://github.com/HexDecimal/python-tcod-camera/blob/main/LICENSE)\n[![Documentation Status](https://readthedocs.org/projects/python-tcod-camera/badge/?version=latest)](https://python-tcod-camera.readthedocs.io)\n[![codecov](https://codecov.io/gh/HexDecimal/python-tcod-camera/branch/main/graph/badge.svg?token=UP161WEo0s)](https://codecov.io/gh/HexDecimal/python-tcod-camera)\n\nThis packages contains a set of tools for working with cameras which translate between world and screen coordinates.\n\nIt is intended to be used with [Python-tcod](https://github.com/libtcod/python-tcod) and [NumPy](https://numpy.org/) but requires neither.\n\n[Additional examples can be found here.](https://github.com/HexDecimal/python-tcod-camera/tree/main/examples)\n\n```py\n# This library works with the idea that you have a world array you want projected onto a screen array.\n\u003e\u003e\u003e import numpy as np\n\u003e\u003e\u003e import tcod.camera\n\u003e\u003e\u003e screen = np.arange(3 * 3, dtype=int).reshape(3, 3)\n\u003e\u003e\u003e world = np.arange(9 * 10, dtype=int).reshape(9, 10)\n\u003e\u003e\u003e screen\narray([[0, 1, 2],\n       [3, 4, 5],\n       [6, 7, 8]])\n\u003e\u003e\u003e world\narray([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],\n       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],\n       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],\n       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],\n       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],\n       [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],\n       [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],\n       [70, 71, 72, 73, 74, 75, 76, 77, 78, 79],\n       [80, 81, 82, 83, 84, 85, 86, 87, 88, 89]])\n\n# This example uses `ij` coordinates, but `xy` coordinates are also an option.\n# The most basic example is to get the camera and use it to project the world and screen shapes.\n\u003e\u003e\u003e camera_ij = tcod.camera.get_camera(screen.shape, center=(2, 2))  # Get the camera position centered on (2, 2).\n\u003e\u003e\u003e camera_ij  # The actual camera position is always which world position to project onto screen[0, 0].\n(1, 1)\n\u003e\u003e\u003e screen_slice, world_slice = tcod.camera.get_slices(screen.shape, world.shape, camera_ij)\n\u003e\u003e\u003e screen_slice\n(slice(0, 3, None), slice(0, 3, None))\n\u003e\u003e\u003e world_slice\n(slice(1, 4, None), slice(1, 4, None))\n\u003e\u003e\u003e screen[screen_slice] = world[world_slice]  # Project the values of screen onto the world.\n\u003e\u003e\u003e screen\narray([[11, 12, 13],\n       [21, 22, 23],\n       [31, 32, 33]])\n\n# Out-of-bounds camera coordinates result in partial views.\n# Fully out-of-bounds cameras will result in zero element views.\n\u003e\u003e\u003e camera_ij = tcod.camera.get_camera(screen.shape, (4, 10))  # A camera position beyond the right side of the world.\n\u003e\u003e\u003e screen_slice, world_slice = tcod.camera.get_slices(screen.shape, world.shape, camera_ij)\n\u003e\u003e\u003e screen[screen_slice].shape  # Because this is partially out-of-bounds not all of the screen is in view.\n(3, 1)\n\u003e\u003e\u003e screen_slice\n(slice(0, 3, None), slice(0, 1, None))\n\u003e\u003e\u003e world_slice\n(slice(3, 6, None), slice(9, 10, None))\n\u003e\u003e\u003e screen[:] = -1  # The screen will be cleared with -1, this value now means out-of-bounds.\n\u003e\u003e\u003e screen[screen_slice] = world[world_slice]  # The parts which do overlap will be projected.\n\u003e\u003e\u003e screen\narray([[39, -1, -1],\n       [49, -1, -1],\n       [59, -1, -1]])\n\n# By adding the shape of the world to camera functions the camera can be clamped to the bounds of the world.\n# All screen indexes will be in-view as long as the screen is never larger than the world.\n\u003e\u003e\u003e camera_ij = tcod.camera.clamp_camera(screen.shape, world.shape, camera_ij)\n\u003e\u003e\u003e screen_slice, world_slice = tcod.camera.get_slices(screen.shape, world.shape, camera_ij)\n\u003e\u003e\u003e screen[screen_slice] = world[world_slice]\n\u003e\u003e\u003e screen  # The camera was moved left to fit the screen to the world.\narray([[37, 38, 39],\n       [47, 48, 49],\n       [57, 58, 59]])\n\n# If the world is divided into chunks then this library can be used to project each chunk onto a single screen.\n# You'll have to manage your own chunks.  Possibly in a `dict[tuple[int, int], NDArray[Any]]`-like container.\n\u003e\u003e\u003e screen = np.zeros((10, 10), dtype=int)\n\u003e\u003e\u003e CHUNK_SIZE = (4, 4)\n\u003e\u003e\u003e for screen_slice, chunk_ij, chunk_slice in tcod.camera.get_chunked_slices(screen.shape, CHUNK_SIZE, camera=(0, 0)):\n...     screen[screen_slice] = chunk_ij[0] + chunk_ij[1] * 10\n...     print(f\"{screen_slice=}, {chunk_ij=}, {chunk_slice=}\")\nscreen_slice=(slice(0, 4, None), slice(0, 4, None)), chunk_ij=(0, 0), chunk_slice=(slice(0, 4, None), slice(0, 4, None))\nscreen_slice=(slice(0, 4, None), slice(4, 8, None)), chunk_ij=(0, 1), chunk_slice=(slice(0, 4, None), slice(0, 4, None))\nscreen_slice=(slice(0, 4, None), slice(8, 10, None)), chunk_ij=(0, 2), chunk_slice=(slice(0, 4, None), slice(0, 2, None))\nscreen_slice=(slice(4, 8, None), slice(0, 4, None)), chunk_ij=(1, 0), chunk_slice=(slice(0, 4, None), slice(0, 4, None))\nscreen_slice=(slice(4, 8, None), slice(4, 8, None)), chunk_ij=(1, 1), chunk_slice=(slice(0, 4, None), slice(0, 4, None))\nscreen_slice=(slice(4, 8, None), slice(8, 10, None)), chunk_ij=(1, 2), chunk_slice=(slice(0, 4, None), slice(0, 2, None))\nscreen_slice=(slice(8, 10, None), slice(0, 4, None)), chunk_ij=(2, 0), chunk_slice=(slice(0, 2, None), slice(0, 4, None))\nscreen_slice=(slice(8, 10, None), slice(4, 8, None)), chunk_ij=(2, 1), chunk_slice=(slice(0, 2, None), slice(0, 4, None))\nscreen_slice=(slice(8, 10, None), slice(8, 10, None)), chunk_ij=(2, 2), chunk_slice=(slice(0, 2, None), slice(0, 2, None))\n\u003e\u003e\u003e screen\narray([[ 0,  0,  0,  0, 10, 10, 10, 10, 20, 20],\n       [ 0,  0,  0,  0, 10, 10, 10, 10, 20, 20],\n       [ 0,  0,  0,  0, 10, 10, 10, 10, 20, 20],\n       [ 0,  0,  0,  0, 10, 10, 10, 10, 20, 20],\n       [ 1,  1,  1,  1, 11, 11, 11, 11, 21, 21],\n       [ 1,  1,  1,  1, 11, 11, 11, 11, 21, 21],\n       [ 1,  1,  1,  1, 11, 11, 11, 11, 21, 21],\n       [ 1,  1,  1,  1, 11, 11, 11, 11, 21, 21],\n       [ 2,  2,  2,  2, 12, 12, 12, 12, 22, 22],\n       [ 2,  2,  2,  2, 12, 12, 12, 12, 22, 22]])\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhexdecimal%2Fpython-tcod-camera","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhexdecimal%2Fpython-tcod-camera","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhexdecimal%2Fpython-tcod-camera/lists"}