Metadata-Version: 2.4
Name: animpy
Version: 3.0.0
Summary: A simple terminal animation library
Author: Samin Riyaz
License: MIT License
        
        Copyright (c) 2026 13DoesPython
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Documentation, https://13DoesPython.github.io/animpy/
Project-URL: Homepage, https://github.com/13DoesPython/animpy
Project-URL: Issues, https://github.com/13DoesPython/animpy/issues
Project-URL: Repository, https://github.com/13DoesPython/animpy.git
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# Animpy

![PyPI - Version](https://img.shields.io/pypi/v/animpy?color=orange)
[![Downloads](https://static.pepy.tech/personalized-badge/animpy?period=total&units=INTERNATIONAL_SYSTEM&left_color=black&right_color=green&left_text=downloads)](https://pepy.tech/projects/animpy)
![GitHub License](https://img.shields.io/github/license/13DoesPython/animpy)

Animpy is a Python library for building lightweight terminal-based animations, visual effects, mini-games, and interactive apps. It gives you a simple API for placing text objects on a scene, moving them, coloring them, applying physics, reacting to input, playing sound, and even following a moving target with a camera-style offset.

It is built for developers who want to create animated terminal experiences without needing a full game engine or a graphical UI framework.

## What Animpy can do

- Create and animate text objects in the terminal
- Draw shapes such as rectangles, circles, polygons, lines, hearts, triangles, ellipses, squares, and donuts
- Create particle bursts, trails, and explosions
- Apply built-in visual effects such as shaking, gravity, decay, fading, interpolation, pulsing, and typing effects
- Build scenes with layered objects, background colors, offsets, and camera-style views
- Add physics behaviors such as gravity, friction, bounce, angular motion, and force pushes
- Handle keyboard and mouse input for interactive demos and games
- Play audio clips with basic playback controls
- Format terminal output with color, panels, centering helpers, and ANSI utilities

## Installation

```bash
pip install animpy
```

Animpy depends on the following packages:

- `pygame-ce`
- `keyboard`
- `mouse`
- `rich`

## Quick start

```python
import animpy

scene = animpy.Scene()
text = animpy.Text("Hello!", 10, 5, r=255, g=100, b=50)
scene.add(text)
scene.render()
```

A more animated example:

```python
import animpy
import time

scene = animpy.Scene()
text = animpy.Text("Hello", 0, 0, r=0, g=255, b=255)
scene.add(text)

for _ in range(20):
    text.moveX(1)
    scene.render()
    time.sleep(0.05)
```

## Core concepts

### 1. Scenes

A `Scene` is the container that holds objects to be rendered to the terminal. It manages the draw order, background color, offsets, and optional camera logic.

```python
scene = animpy.Scene()
scene.set_bg_rgb(0, 0, 30)
```

### 2. Text objects

`Text` is the basic object that can be placed on the screen and moved around. Text can also be created from a list of frames, which allows simple frame-based animation.

```python
text = animpy.Text("Hello", 5, 3)
text.moveX(4)
text.moveY(2)
text.set_color(255, 0, 0)
```

### 3. Effects and motion

`EffectText` adds common visual effects and movement helpers on top of `Text`.

```python
fx = animpy.EffectText("Bounce", 10, 2)
fx.shaking_text(intensity=2)
fx.gravity_text(floor=10, gravity=0.5)
```

### 4. Groups

A `Group` lets you manage many objects as one unit. Position and color changes can be applied to the entire group.

```python
group = animpy.Group()
group.add(animpy.Text("A", 1, 1), animpy.Text("B", 4, 1))
group.position(2, 0)
```

### 5. Camera

A `Camera` provides a simple view transform for a scene. You can point it at a target object or keep a custom offset.

```python
scene = animpy.Scene()
player = animpy.Text("P", 10, 5)
scene.add(player)

camera = animpy.Camera((0, 0), None)
camera.follow(player)
scene.camera = camera
scene.render()
```

## Feature reference

### Text

`Text` is the base class for terminal objects.

```python
text = animpy.Text("Hello", 2, 2, r=255, g=255, b=255)
```

Common methods:

- `change_frame()` — advance to the next frame if the text was created from a list
- `set_frame(index)` — switch to a specific frame
- `set_text(text)` — replace the text content or frame list
- `set_position(x, y)` — directly set the position
- `move_to(x, y)` — alias for `set_position`
- `set_color(r, g, b)` — update the color using RGB values
- `change_rgb_values(r, g, b)` — same as `set_color`
- `moveX(dx)` — move horizontally
- `moveY(dy)` — move vertically
- `slide_to_pos(new_pos, speed=1)` — move smoothly toward a coordinate-like object
- `centerX()` — center the text horizontally
- `centerY()` — center the text vertically
- `collides_with(other)` — test for bounding-box overlap
- `on_collide_callback(other, callback)` — run a callback when collision occurs
- `type_out(text, speed=0.05, scene=None)` — print text gradually, optionally rendering after each character
- `fall(velocity, floor)` — move the object downward until a floor is reached

Useful properties:

- `width` — number of characters in the current frame
- `height` — number of lines in the current frame
- `current_frame` — current frame index

### EffectText

`EffectText` extends `Text` with reusable animation effects and velocity support.

```python
fx = animpy.EffectText("Shaky", 5, 5)
fx.shaking_text(intensity=2)
fx.gravity_text(floor=10, gravity=0.5)
fx.pulse_text(time=1.0, pulse_rate=0.5)
```

Available methods:

- `gravity_text(floor=20, gravity=0.5)` — apply gravity and clamp to a floor
- `shaking_text(intensity=1)` — jitter position randomly
- `decaying_text(time, decay_rate=0.1)` — shorten the text over time
- `fade_out_text(time, fade_rate=0.1)` — fade the object toward black
- `lerp_text(target_x, target_y, t)` — interpolate smoothly to a target position
- `pulse_text(time, pulse_rate=0.5)` — change brightness over time
- `set_velocity(vx, vy)` — set motion in X/Y
- `reset_velocity()` — clear velocity values
- `apply_force(fx, fy)` — add to the current velocity
- `fade_in_text(time, fade_rate=0.1)` — fade in over time

### Shapes

Shapes return multi-line strings that can be used as text content.

```python
rect = animpy.Shapes.rectangle(8, 4, "#")
circle = animpy.Shapes.circle(4, "*")
line = animpy.Shapes.line(0, 0, 10, 4, "-")
heart = animpy.Shapes.heart(5, "♥")
triangle = animpy.Shapes.triangle(0, 0, 6, 0, 3, 5, "^")
ellipse = animpy.Shapes.ellipse(6, 3, 4, 2, "o")
square = animpy.Shapes.square(5, "#")
donut = animpy.Shapes.donut(5, 2, "#")
```

Supported builders:

- `rectangle(width, height, char)`
- `circle(radius, char)`
- `polygon(points, char)`
- `line(x1, y1, x2, y2, char)`
- `triangle(x1, y1, x2, y2, x3, y3, char)`
- `ellipse(center_x, center_y, radius_x, radius_y, char)`
- `heart(size, char)`
- `square(size, char)`
- `donut(outer_radius, inner_radius, char)`

### Group

A `Group` collects multiple objects so they can be moved or recolored together.

```python
group = animpy.Group()
text1 = animpy.Text("A", 0, 0)
text2 = animpy.Text("B", 2, 0)
group.add(text1, text2)

group.position(3, 1)
group.change_rgb_values(0, 255, 0)
```

Available methods:

- `add(*items)` — add one or more objects
- `remove(*items)` — remove one or more objects
- `clear()` — remove all items
- `contains(item)` — check if an item exists in the group
- `find_by_color(r, g, b)` — return matching items by color
- `position(newx, newy)` — shift every item by an offset
- `change_rgb_values(r, g, b)` — recolor every item
- `change_rgb_values_one(item, r, g, b)` — recolor a single item

### Coords, Keyframe, and KeyChains

These helpers support path-based movement.

```python
from animpy import Coords, Keyframe, KeyChains

start = Coords(0, 0)
mid = Coords(10, 3)
end = Coords(15, 8)

path = KeyChains(Keyframe(start), Keyframe(mid), Keyframe(end))
path.follow_path(text, speed=1)
```

- `Coords(x, y)` — simple coordinate container
- `distance_to(other)` — calculate Euclidean distance
- `offset(dx, dy)` — return a new offset coordinate
- `Keyframe(pos)` — stores a target position
- `set_pos(pos)` — update keyframe position
- `distance_to(other)` — measure the distance to another keyframe or coordinate
- `KeyChains(*keyframes)` — store a list of keyframes
- `append(keyframe)` — add a keyframe
- `clear()` — remove all keyframes
- `reverse_path()` — reverse the path order
- `is_complete` — becomes `True` once the path has no more keyframes
- `follow_path(obj, speed=1)` — moves an object toward the current keyframe

### Scene

A `Scene` handles rendering and object management.

```python
scene = animpy.Scene()
scene.add(text1, text2)
scene.remove(text2)
scene.render()
```

Useful methods:

- `add(*items)` — add objects to the scene
- `remove(*items)` — remove objects from the scene
- `render()` — draw the current frame to the terminal
- `update(delta_time)` — update particles and remove expired items
- `set_bg_rgb(r, g, b)` — set a background color
- `shake(intensity=1)` — apply a random shaking offset
- `clear()` — clear the terminal display
- `clear_items()` — remove every object from the scene
- `count_items()` — return the number of scene items
- `find_items_at(x, y)` — return the objects sitting at a coordinate

Scene properties:

- `dt` — elapsed time since the last frame
- `offset_x`, `offset_y` — manual rendering offsets
- `camera` — optional `Camera` attached to the scene

### Camera

The `Camera` helper lets a scene render from a different view.

```python
camera = animpy.Camera((0, 0), None)
camera.follow(player)
scene.camera = camera
scene.render()
```

Available methods:

- `move(new_position)` — set the camera position directly
- `look_at(new_target)` — point the camera at a new target
- `follow(target_object)` — track an object by reference
- `get_view_matrix()` — return the current view position as a tuple

### PhysicsScene

`PhysicsScene` adds simple physical motion helpers to a scene.

```python
scene = animpy.PhysicsScene()
ball = animpy.EffectText("O", 2, 2)
scene.add(ball)

scene.apply_gravity(ball)
scene.apply_friction(ball, friction=0.1)
scene.bounce(ball, bounce_factor=0.7)
scene.apply_physics(ball)
scene.angular_motion(ball, angle=45, speed=2.0)
scene.push(ball, force_x=0.5, force_y=-0.2)
```

Available methods:

- `apply_gravity(obj)` — move an object downward toward the floor
- `apply_friction(obj, friction=0.1)` — reduce velocity over time
- `bounce(obj, bounce_factor=0.5)` — reverse vertical motion when hitting the floor
- `apply_physics(obj)` — combine gravity, friction, and bounce
- `angular_motion(obj, angle, speed)` — set an angled velocity
- `push(obj, force_x, force_y)` — add force in X/Y directions

### InteractiveScene

`InteractiveScene` makes it possible to react to keyboard and mouse input.

```python
scene = animpy.InteractiveScene()
player = animpy.Text("P", 10, 10)
scene.add(player)

while True:
    if scene.key_pressed("w"):
        player.moveY(-1)
    if scene.key_pressed("s"):
        player.moveY(1)
    if scene.key_pressed("esc"):
        break
    scene.render()
```

Available methods:

- `key_pressed(key)` — check whether a key is held down
- `key_released(key)` — check whether a key has been released
- `on_key_press_callback(key, callback)` — run a callback when a key is pressed
- `on_key_release_callback(key, callback)` — run a callback when a key is released
- `mouse_pressed(button="left")` — check whether a mouse button is held down
- `mouse_position()` — return the current mouse position
- `on_mouse_press_callback(button, callback)` — run a callback when a mouse button is pressed
- `mouse_release(button, callback)` — run a callback when a button is released
- `mouse_release_callback(button, callback)` — alias for the release callback helper
- `quick_exit(key="esc")` — exit immediately when a key is pressed
- `quick_exit_callback(key, callback)` — run a callback when a key is pressed
- `limit_to_bounds(obj)` — keep an object inside the terminal area
- `limit_group_to_bounds(group)` — keep every object in a group inside the terminal area

### Particles

Particles are useful for trails, explosions, smoke, sparks, and other visual effects.

```python
particle = animpy.Particle("*", 10, 5, r=255, g=150, b=50, lifetime=2.0)
particle.burst(scene, count=20, speed=1.5)
```

Available methods:

- `update(delta_time)` — advance the particle in time
- `emit(scene)` — add a single particle to the scene
- `burst(scene, count=10, speed=1.0)` — create many particles with random velocities
- `change_rgb_values(r, g, b)` — change the particle color
- `set_velocity(vx, vy)` — set particle velocity
- `apply_force(fx, fy)` — add velocity changes
- `set_color(r, g, b)` — alias for setting color
- `reset(x, y, lifetime=None)` — reset particle position and age
- `is_alive()` — check whether the particle is still alive
- `update_all(delta_time)` — update all particle children created by the parent
- `is_dead()` — check whether all tracked particles are dead

There are also built-in particle presets:

- `Presets.create_explosion(x, y, count=20, speed=1.0, lifetime=1.0)`
- `Presets.create_smoke(x, y, count=10, speed=0.5, lifetime=2.0)`
- `Presets.create_firework(x, y, count=30, speed=2.0, lifetime=1.5)`

### Audio

Audio support is built on `pygame` and can be used to add music or effects.

```python
audio = animpy.Audio()
audio.load("bg", "music.mp3")
audio.play("bg", loop=-1)
audio.set_volume("bg", 0.5)
```

Available methods:

- `load(name, file_path)` — load an audio file
- `play(name, loop=0)` — start playback
- `stop(name)` — stop a specific sound
- `stop_all()` — stop everything
- `is_playing(name=None)` — check whether a sound is active
- `set_volume(name, volume)` — update a volume level
- `pause(name)` — pause playback
- `resume(name)` — resume playback
- `fade_out(name, duration)` — fade the sound out
- `fade_in(name, duration, loop=0)` — fade in and optionally loop
- `play_for_time(name, duration)` — play for a set length and stop automatically

### Terminal utilities

Animpy also includes helpers for terminal output.

```python
animpy.hide_cursor()
animpy.show_cursor()
animpy.clear_screen()
animpy.print_centered("Centered")
animpy.print_with_color("Colored", r=255, g=0, b=0)
animpy.print_panel("Hello", title="Info")
```

Available helpers:

- `lerp(start, end, t)` — linear interpolation
- `hide_cursor()` / `show_cursor()`
- `clear_screen()`
- `print_centered(text)`
- `print_with_color(text, r, g, b)`
- `print_panel(text, title="Panel", style="bold white on blue")`
- ANSI color constants such as `animpy.ANSI["red"]` and `animpy.ANSI["bg_blue"]`
- terminal size helpers such as `get_terminal_size()`, `set_terminal_size(columns, lines)`, `reset_terminal_size()`, `get_terminal_width()`, and `get_terminal_height()`

## Example projects

The repository contains examples for:

- basic movement and interpolation
- loading screens
- visual effects
- physics demos
- player controls
- collision and tag-style games
- audio playback

You can browse the examples folder in the project repository for complete scripts.

## Version history

### v2.6.0

Added a new `Camera` class for scene view transformation and object tracking. All existing particle and utility methods now work seamlessly with camera-enabled scenes.

**New Camera class:**

- `Camera(position, target=None)` — Constructor that creates a camera at a specific position with an optional target
  - `position` — tuple of (x, y) representing the camera's current location
  - `target` — optional object or position to point the camera at; defaults to `None`

**Camera methods:**

- `move(new_position)` — Directly set the camera position to a new tuple (x, y)
- `look_at(new_target)` — Point the camera at a new target position or object
- `follow(target_object)` — Continuously track a Text or object with `x` and `y` properties
- `get_view_matrix()` — Return the current camera view as a tuple (x, y)

**Scene camera support:**

- `Scene.camera` — New optional property to attach a Camera instance
  - Assign a Camera to enable camera-based rendering offsets
  - When set, all objects render relative to the camera's viewpoint
  - Works with all scene objects: Text, Particles, Shapes, Groups

**Particle class methods (now camera-compatible):**

- `update(delta_time)` — Advance particle age and position
- `emit(scene)` — Add a single particle instance to the scene
- `burst(scene, count=10, speed=1.0)` — Create multiple particles with random velocities in all directions
- `change_rgb_values(r, g, b)` — Update particle color
- `set_velocity(vx, vy)` — Set particle velocity directly
- `apply_force(fx, fy)` — Add velocity changes via force
- `set_color(r, g, b)` — Alias for changing color
- `reset(x, y, lifetime=None)` — Reset particle position and age
- `is_alive()` — Check if particle is still active
- `update_all(delta_time)` — Update all child particles created by burst
- `is_dead()` — Check if all tracked particles have expired

**Particle Presets class (now camera-compatible):**

- `Presets.create_explosion(x, y, count=20, speed=1.0, lifetime=1.0)` — Create an explosion effect
- `Presets.create_smoke(x, y, count=10, speed=0.5, lifetime=2.0)` — Create a smoke effect
- `Presets.create_firework(x, y, count=30, speed=2.0, lifetime=1.5)` — Create a firework effect

**Utility functions (now camera-compatible):**

- `lerp(start, end, t)` — Linear interpolation between start and end values
- `hide_cursor()` — Hide the terminal cursor
- `show_cursor()` — Show the terminal cursor
- `clear_screen()` — Clear terminal display
- `print_panel(text, title="Panel", style="bold white on blue")` — Print text in a styled panel
- `print_centered(text)` — Print text centered in the terminal
- `print_with_color(text, r, g, b)` — Print colored text using RGB values
- `get_terminal_size()` — Return current terminal dimensions
- `set_terminal_size(columns, lines)` — Set terminal size
- `reset_terminal_size()` — Reset terminal size to defaults
- `get_terminal_width()` — Get terminal column count
- `get_terminal_height()` — Get terminal line count
- `ANSI` — Dictionary of ANSI color and style codes for advanced terminal formatting

### v2.5.0
- Added coordinate helpers such as `Coords.distance_to()` and `Coords.offset()`
- Added `Keyframe` and `KeyChains` helpers for path-based motion
- Added `Audio.pause()`, `Audio.resume()`, `Audio.fade_in()`, and `Audio.fade_out()`
- Added `Group.clear()`, `Group.contains()`, and `Group.find_by_color()`
- Added particle helpers such as `set_velocity()`, `apply_force()`, `set_color()`, `reset()`, and `is_alive()`
- Added scene helpers such as `clear_items()`, `count_items()`, and `find_items_at()`
- Added shape builders `square()` and `donut()`
- Added text convenience setters such as `set_frame()`, `set_text()`, `set_position()`, `move_to()`, and `set_color()`

### v2.1.0
- Added `Shapes.line()`, `Shapes.ellipse()`, `Shapes.heart()`, and `Shapes.triangle()`
- Added `clear_screen()`, `print_centered()`, and `print_with_color()`

### v2.0.0
- Added `PhysicsScene` for gravity, friction, bounce, and force-driven motion
- Added `EffectText` animation helpers such as `fade_out_text()`, `lerp_text()`, and `pulse_text()`
- Added interactive scene helpers such as `limit_to_bounds()`, `quick_exit()`, and `limit_group_to_bounds()`

## Support

If you enjoy Animpy, please consider starring the project on GitHub:

https://github.com/13DoesPython/animpy
