diff --git a/bruhanimate/__init__.py b/bruhanimate/__init__.py index 17e83e6..d10cbb0 100644 --- a/bruhanimate/__init__.py +++ b/bruhanimate/__init__.py @@ -32,7 +32,7 @@ from .bruhutil import ( Screen, Buffer, - images, + bruhimage, get_image, text_to_image, LIFE_COLORS, @@ -45,15 +45,11 @@ GREY_SCALES, WIND_DIRECTIONS, NOISE, - FLAKES, - FLAKE_COLORS, - FLAKE_JUMPS, - NEXT_FLAKE_MOVE, + SNOWFLAKE_COLORS, FLAKE_WEIGHT_CHARS, - FLAKE_FLIPS, TWINKLE_COLORS, - FLAKES, VALID_INTERFACES, + SNOWFLAKE_TYPES ) from .bruhrenderer import ( @@ -82,7 +78,7 @@ ) -__version__ = "0.2.65" +__version__ = "0.2.69" __valid_demos__ = [ "static_demo", "offset_demo", @@ -144,7 +140,7 @@ "PanRenderer", "FocusRenderer", "BackgroundColorRenderer", - "images", + "bruhimage", "__version__", "__valid_demos__", "LIFE_COLORS", @@ -157,12 +153,9 @@ "GREY_SCALES", "WIND_DIRECTIONS", "NOISE", - "FLAKES", - "FLAKE_COLORS", - "FLAKE_JUMPS", - "NEXT_FLAKE_MOVE", + "SNOWFLAKE_COLORS", + "SNOWFLAKE_TYPES", "FLAKE_WEIGHT_CHARS", - "FLAKE_FLIPS", "TWINKLE_COLORS", "VALID_INTERFACES", "get_image", diff --git a/bruhanimate/bruheffect/__init__.py b/bruhanimate/bruheffect/__init__.py index 6dd93eb..8a6876a 100644 --- a/bruhanimate/bruheffect/__init__.py +++ b/bruhanimate/bruheffect/__init__.py @@ -10,32 +10,53 @@ from .snow_effect import SnowEffect from .twinkle_effect import TwinkleEffect, TWINKLE_SPEC from .offset_effect import OffsetEffect -from .rain_effect import RainEffect +from .rain_effect import RainEffect from .noise_effect import NoiseEffect -__all__ = [ - "BaseEffect", - "StaticEffect", - "StarEffect", - "ChatbotEffect", - "DrawLinesEffect", - "GameOfLifeEffect", - "MatrixEffect", - "PlasmaEffect", - "SnowEffect", - "TwinkleEffect", - "OffsetEffect", - "RainEffect", - "NoiseEffect", - "GradientNoise", - "Loading", - "StringStreamer", - "Key", - "Line", - "TWINKLE_SPEC" -] - -if sys.platform == 'win32': +if sys.platform == "win32": from .audio_effect import AudioEffect - __all__.append("AudioEffect") \ No newline at end of file + __all__ = [ + "BaseEffect", + "AudioEffect", + "StaticEffect", + "StarEffect", + "ChatbotEffect", + "DrawLinesEffect", + "GameOfLifeEffect", + "MatrixEffect", + "PlasmaEffect", + "SnowEffect", + "TwinkleEffect", + "OffsetEffect", + "RainEffect", + "NoiseEffect", + "GradientNoise", + "Loading", + "StringStreamer", + "Key", + "Line", + "TWINKLE_SPEC", + ] +else: + __all__ = [ + "BaseEffect", + "StaticEffect", + "StarEffect", + "ChatbotEffect", + "DrawLinesEffect", + "GameOfLifeEffect", + "MatrixEffect", + "PlasmaEffect", + "SnowEffect", + "TwinkleEffect", + "OffsetEffect", + "RainEffect", + "NoiseEffect", + "GradientNoise", + "Loading", + "StringStreamer", + "Key", + "Line", + "TWINKLE_SPEC", + ] diff --git a/bruhanimate/bruheffect/snow_effect.py b/bruhanimate/bruheffect/snow_effect.py index 5c43fad..259ba1a 100644 --- a/bruhanimate/bruheffect/snow_effect.py +++ b/bruhanimate/bruheffect/snow_effect.py @@ -17,119 +17,14 @@ import random from bruhcolor import bruhcolored -from ..bruhutil import FLAKE_COLORS, FLAKE_FLIPS, FLAKE_JUMPS, FLAKE_WEIGHT_CHARS, FLAKES, NEXT_FLAKE_MOVE +from ..bruhutil import FLAKE_WEIGHT_CHARS, SNOWFLAKE_TYPES, SNOWFLAKE_COLORS, Buffer from .base_effect import BaseEffect -class _FLAKE: - def __init__(self, index, x, y): - self.index = index - self.x = x - self.y = y - self.prev_x = x - self.prev_y = y - self.weight = 1 - self.color = FLAKE_COLORS[index] - self.char = bruhcolored(FLAKES[index], color=self.color).colored - self.current_position = "center" - self.on_ground = False - self.full = False - - def flip_flake(self): - if self.char == FLAKE_FLIPS[self.index][0]: - self.char = FLAKE_FLIPS[self.index][1] - else: - self.char = FLAKE_FLIPS[self.index][0] - - def next_position(self, frame_number): - if self.on_ground: - return - - if frame_number % self.index != 0: - return - - if random.random() < 0.10: - return - - self.prev_x = self.x - self.prev_y = self.y - - self.y = self.y + random.choice(FLAKE_JUMPS[self.index]) - - next_position = random.choice(["left", "center", "right"]) - - next_flake_move = ( - NEXT_FLAKE_MOVE[(self.current_position, next_position)] - if self.current_position != next_position - else None - ) - - if next_flake_move: - self.x = self.x + next_flake_move - self.current_position = next_position - - def update_position(self, x, y): - self.prev_x = self.x - self.prev_y = self.y - self.x = x - self.y = y - - def set_to_on_ground(self): - self.weight = 1 - self.on_ground = True - self.color = 190 if random.random() < 0.01 else 255 - self.char = bruhcolored( - FLAKE_WEIGHT_CHARS[self.weight], color=self.color - ).colored - - def increment_flake_weight(self): - if self.weight < 18: - self.weight += 1 - - self.update_ground_flake() - - if self.weight == 18: - self.full = True - - def update_ground_flake(self): - if not self.full: - if self.char != list(FLAKE_WEIGHT_CHARS.values())[-1]: - if self.weight in FLAKE_WEIGHT_CHARS.keys(): - self.char = bruhcolored( - FLAKE_WEIGHT_CHARS[self.weight], color=self.color - ).colored - - def __str__(self): - return self.char - - def __repr__(self): - return self.char - - def __len__(self): - return 1 - - def __eq__(self, other): - return self.char == other - - def copy(self): - new_flake = _FLAKE(index=self.index, x=self.x, y=self.y) - new_flake.weight = self.weight - new_flake.char = self.char - new_flake.current_position = self.current_position - new_flake.color = self.color - new_flake.on_ground = self.on_ground - new_flake.x = self.x - new_flake.y = self.y - new_flake.prev_x = self.prev_x - new_flake.prev_y = self.prev_y - new_flake.full = self.full - return new_flake - - class SnowEffect(BaseEffect): def __init__( self, - buffer, + buffer: Buffer, background, img_start_x=None, img_start_y=None, @@ -139,18 +34,13 @@ def __init__( show_info=False, ): super(SnowEffect, self).__init__(buffer, background) - self.image_present = ( - True if img_start_x and img_start_y and img_width and img_height else False - ) + self.image_present = (True if img_start_x and img_start_y and img_width and img_height else False) self.collision = collision self.total_ground_flakes = 0 - self._show_info = show_info - self._flakes = [] - self._ground_flakes = [ - [None for _ in range(self.buffer.width())] - for __ in range(self.buffer.height()) - ] - self._image_collide_flakes = [None for _ in range(self.buffer.width())] + self.show_info = show_info + self.flakes = [] + self.ground_flakes = [[0 for _ in range(buffer.width())] for _ in range(buffer.height())] + self.image_flakes = [None for _ in range(self.buffer.width())] self.smart_transparent = False def update_collision( @@ -160,6 +50,7 @@ def update_collision( img_width, img_height, collision, + smart_transparent, image_buffer=None, ): """ @@ -187,182 +78,86 @@ def update_collision( else: self.image_buffer = None - def show_info(self, show_info: bool): - self._show_info = show_info + def set_show_info(self, show_info: bool): + self.show_info = show_info - def render_frame(self, frame_number): - # calc each flakes next position - for flake in self._flakes: - flake.next_position(frame_number) + def generate_snowflake(self, x: int): + snowflake_type = random.choice(list(SNOWFLAKE_TYPES.keys())) + snowflake_char = bruhcolored(snowflake_type, SNOWFLAKE_COLORS[snowflake_type]) + self.flakes.append({"x": x, "y": 0, "type": snowflake_type, "colored": snowflake_char, "fall_delay": 0}) - # generate the next set of flakes - for x in range(self.buffer.width()): - if random.random() < 0.01: - flake = _FLAKE(index=random.choice([1, 3, 7]), x=x, y=0) - self._flakes.append(flake) - - if self.smart_transparent and frame_number == 0 and self.image_present: - self.smart_boundLine = {} - for x in range(self.img_width): - tmp_flag = False - for y in range(self.img_height): - if self.image_buffer.buffer[y + self.img_start_y][ - x + self.img_start_x - ] not in [" ", None]: - self.smart_boundLine[x + self.img_start_x] = ( - y + self.img_start_y - 1 - ) - tmp_flag = True - break - if not tmp_flag: - self.smart_boundLine[x + self.img_start_x] = None - - # determine what flakes are hitting the ground or need to be deleted - for idx, flake in enumerate(self._flakes): - # ground flake - if ( - flake.x >= 0 - and flake.x < self.buffer.width() - and flake.y >= self.buffer.height() - 1 - ): - # true_y = flake.y + def can_stack(self, x, y): + if y >= self.buffer.height(): + return False + return self.ground_flakes[y][x] >= 18 - # need to set the y value to be the actual net available y val - # what isn't a valid y value? - # a -> value that exceeds the buffer height - # b -> value that intercepts a full flake in the column - true_y = None - for y in range(self.buffer.height() - 1, -1, -1): - if ( - self._ground_flakes[y][flake.x] is None - or not self._ground_flakes[y][flake.x].full - ): - true_y = y - break + def is_colliding(self, x: int, y: int): + if self.image_present: + if self.image_x_boundaries[0] < x < self.image_x_boundaries[1] and self.image_y_boundaries[0] < y < self.image_y_boundaries[1]: + return True + return False + + def handle_snowflake_landing(self, x: int, y: int): + self.ground_flakes[y][x] += 1 + weight = self.ground_flakes[y][x] + for w, char in sorted(FLAKE_WEIGHT_CHARS.items(), reverse=True): + if weight >= w: + self.buffer.put_char(x, y, char) + break + if weight > 18 and y > 0: + self.ground_flakes[y - 1][x] += 1 + + def add_info(self): + self.buffer.put_at(0, 0, f"Total Snow Flakes: {len(self.flakes)}") + self.buffer.put_at(0, 1, f"Total Flakes on Ground: {sum([sum([1 for v in row if v > 0]) for row in self.ground_flakes])}") - if true_y is None: - break + def render_frame(self, frame_number): + """ + Function to render the next frame of the snow effect into it's buffer. + :param frame_number: The current frame number (used to determine the animation state). + """ - if ( - isinstance(self._ground_flakes[true_y][flake.x], _FLAKE) - and not self._ground_flakes[true_y][flake.x].full - ): - ground_flake: _FLAKE = self._ground_flakes[true_y][flake.x] - ground_flake.increment_flake_weight() - self._ground_flakes[true_y][flake.x] = ground_flake.copy() - del ground_flake - elif isinstance(self._ground_flakes[true_y][flake.x], _FLAKE): - tmp_flake = flake.copy() - tmp_flake.set_to_on_ground() - tmp_flake.y = true_y - 1 - self._ground_flakes[true_y - 1][flake.x] = tmp_flake - else: - tmp_flake = flake.copy() - tmp_flake.set_to_on_ground() - tmp_flake.y = true_y - self._ground_flakes[true_y][flake.x] = tmp_flake - self._flakes[idx] = None - self.buffer.put_char(flake.prev_x, flake.prev_y, " ") - elif flake.x < 0 or flake.x >= self.buffer.width(): - self._flakes[idx] = None - self.buffer.put_char(flake.prev_x, flake.prev_y, " ") + # generate a new row of snowflakes + for idx in range(self.buffer.width()): + if random.random() < 0.01: + self.generate_snowflake(idx) + + # update the positions of all flakes + new_flakes = [] + for snowflake in self.flakes: + x, y, flake_type = snowflake["x"], snowflake["y"], snowflake["type"] + speed = SNOWFLAKE_TYPES[flake_type]["speed"] + + # clear out the old position of the snowflake + self.buffer.put_char(x, y, " ") + + if snowflake["fall_delay"] < speed: + snowflake["fall_delay"] += 1 + new_flakes.append(snowflake) + continue else: - # image collision flake - if not self.smart_transparent: - if ( - self.image_present - and flake.x >= self.image_x_boundaries[0] - and flake.x <= self.image_x_boundaries[1] - and flake.y >= self.image_y_boundaries[0] - and flake.y <= self.image_y_boundaries[1] - ): - # colliding with image - if isinstance(self._image_collide_flakes[flake.x], _FLAKE): - ground_flake: _FLAKE = self._image_collide_flakes[ - flake.x - ].copy() - ground_flake.increment_flake_weight() - self._image_collide_flakes[flake.x] = ground_flake - del ground_flake - else: - tmp_flake = flake.copy() - tmp_flake.set_to_on_ground() - tmp_flake.y = self.image_y_boundaries[0] - 1 - self._image_collide_flakes[flake.x] = tmp_flake - self._flakes[idx] = None - self.buffer.put_char(flake.prev_x, flake.prev_y, " ") - elif frame_number != 0: - if self.image_present and flake.x in self.smart_boundLine.keys(): - if start_bound := self.smart_boundLine[flake.x]: - if flake.y >= start_bound and flake.y <= self.img_end_y: - # colliding with image - self._flakes[idx] = None - self.buffer.put_char(flake.prev_x, flake.prev_y, " ") - - if isinstance( - self.buffer.get_char(flake.x, start_bound), _FLAKE - ): - ground_flake: _FLAKE = self._image_collide_flakes[ - flake.x - ].copy() - ground_flake.increment_flake_weight() - self._image_collide_flakes[flake.x] = ground_flake - del ground_flake - else: - tmp_flake = flake.copy() - tmp_flake.set_to_on_ground() - tmp_flake.y = start_bound - self._image_collide_flakes[flake.x] = tmp_flake + snowflake["fall_delay"] = 0 - self._flakes = [flake for flake in self._flakes if flake] - - # place the flakes into the buffer - for flake in self._flakes: - self.buffer.put_char(flake.x, flake.y, flake) - self.buffer.put_char(flake.prev_x, flake.prev_y, " ") - - # place the ground flakes - if self.collision: - for y in range(self.buffer.height()): - for x in range(self.buffer.width()): - flake = self._ground_flakes[y][x] - if flake: - self.buffer.put_char(flake.x, flake.y, flake) - for flake in self._image_collide_flakes: - if flake: - self.buffer.put_char(flake.x, flake.y, flake) - - if self._show_info: - self.buffer.put_at(0, 1, f"Width: {self.buffer.width()}") - self.buffer.put_at(0, 2, f"Height: {self.buffer.height()}") - self.buffer.put_at(0, 3, f"Collision Enabled: {self.collision}") - self.buffer.put_at(0, 4, f"Total flakes: {len(self._flakes):3d}") - self.buffer.put_at( - 0, - 5, - f"Ground flakes: {sum([sum([1 for x in range(len(self._ground_flakes[0])) if self._ground_flakes[y][x]]) for y in range(len(self._ground_flakes))]):3d}", - ) - self.buffer.put_at( - 0, - 6, - f"Full flakes: {sum([1 for flake in [j for sub in self._ground_flakes for j in sub] if flake and flake.full]):3d}", - ) - self.buffer.put_at(0, 7, f"Image present: {self.image_present}") - if self.image_present: - self.buffer.put_at( - 0, - 8, - f"Total flakes on image: {len([0 for _ in self._image_collide_flakes if _]):3d}", - ) - self.buffer.put_at( - 0, 9, f"Image x boundaries: {self.image_x_boundaries}" - ) - self.buffer.put_at( - 0, 10, f"Image y boundaries: {self.image_y_boundaries}" - ) - self.buffer.put_at(0, 11, f"Image y bottom: {self.img_end_y}") + if y + 1 >= self.buffer.height() or self.can_stack(x, y + 1): + self.handle_snowflake_landing(x, y) + else: + dx = random.choice([-1, 0, 1]) + dy = 1 + + new_x = max(0, min(self.buffer.width() - 1, x + dx)) + new_y = y + dy + if new_y < self.buffer.height() and not self.is_colliding(new_x, new_y) and 0 <= new_x < self.buffer.width(): + snowflake["x"], snowflake["y"] = new_x, new_y + new_flakes.append(snowflake) + else: + self.handle_snowflake_landing(x, y) - # for flake in [j for sub in self._ground_flakes for j in sub]: - # if flake: - # print(f"{flake.weight} - {flake.char} - {flake.full}") + self.flakes = new_flakes + # place the updates into the buffer + for snowflake in self.flakes: + x, y, flake_type, colored = snowflake["x"], snowflake["y"], snowflake["type"], snowflake["colored"] + self.buffer.put_char(x, y, colored.colored) + + if self.show_info: + self.add_info() diff --git a/bruhanimate/bruhrenderer/background_color_renderer.py b/bruhanimate/bruhrenderer/background_color_renderer.py index 39b7a10..235478b 100644 --- a/bruhanimate/bruhrenderer/background_color_renderer.py +++ b/bruhanimate/bruhrenderer/background_color_renderer.py @@ -14,23 +14,27 @@ limitations under the License. """ -from bruhcolor import bruhcolored +from typing import List from .base_renderer import BaseRenderer +from bruhcolor import bruhcolored +from ..bruhutil.bruhtypes import EffectType + class BackgroundColorRenderer(BaseRenderer): def __init__( self, screen, - frames, - time, - img, - on_color_code, - effect_type="static", - background=" ", - transparent=False, + img: List[str], + frames: int = 100, + frame_time: float = 0.1, + effect_type: EffectType = "static", + background: str = " ", + transparent: bool = False, + collision: bool = False, + on_color_code: int = 27, ): super(BackgroundColorRenderer, self).__init__( - screen, frames, time, effect_type, background, transparent + screen, frames, frame_time, effect_type, background, transparent, collision ) self.img = img diff --git a/bruhanimate/bruhrenderer/base_renderer.py b/bruhanimate/bruhrenderer/base_renderer.py index 131aa05..7b5eefb 100644 --- a/bruhanimate/bruhrenderer/base_renderer.py +++ b/bruhanimate/bruhrenderer/base_renderer.py @@ -15,43 +15,41 @@ """ import sys -import time -import random -from ..bruhutil import Buffer, Screen -from ..bruheffect import * +from typing import Literal, TypeVar, Dict, Generic from abc import abstractmethod +from ..bruheffect import * +from ..bruhutil.utils import sleep +from ..bruhutil.bruhffer import Buffer +from ..bruhutil.bruhscreen import Screen +from ..bruhutil.bruherrors import InvalidEffectTypeError +from ..bruhutil.bruhtypes import EffectType, valid_effect_types + -_VALID_EFFECTS = ["static", "offset", "noise", "stars", "plasma", "gol", "rain", "matrix", "drawlines", "snow", "twinkle", "audio", "chat"] HORIZONTAL = "h" VERTICAL = "v" INF = float("inf") - -def sleep(s): - sys.stdout.flush() - time.sleep(s) - - class BaseRenderer: """ - Defines the base methods, abstract methods, and base attributes - for the render class, is an Effect Only Renderer + Base class for rendering effects on the screen. """ def __init__( self, screen: Screen, frames: int = 100, - time: float = 0.1, - effect_type: str = "static", + frame_time: float = 0.1, + effect_type: EffectType = "static", background: str = " ", transparent: bool = False, collision: bool = False, ): + self.validate_effect_type(effect_type) + self.screen = screen self.frames = frames - self.time = time - self.effect_type = effect_type if effect_type in _VALID_EFFECTS else "static" + self.frame_time = frame_time + self.effect_type = effect_type self.transparent = transparent self.background = background self.height = screen.height @@ -59,182 +57,149 @@ def __init__( self.smart_transparent = False self.collision = collision - # effect - if self.effect_type == "static": - self.effect = StaticEffect(Buffer(self.height, self.width), self.background) - elif self.effect_type == "offset": - self.effect = OffsetEffect(Buffer(self.height, self.width), self.background) - elif self.effect_type == "noise": - self.effect = NoiseEffect(Buffer(self.height, self.width), self.background) - self.background = self.effect.background - elif self.effect_type == "stars": - self.effect = StarEffect(Buffer(self.height, self.width), self.background) - elif self.effect_type == "plasma": - self.effect = PlasmaEffect(Buffer(self.height, self.width), self.background) - elif self.effect_type == "gol": - self.effect = GameOfLifeEffect(Buffer(self.height, self.width), self.background) - elif self.effect_type == "rain": - self.effect = RainEffect(Buffer(self.height, self.width), self.background) - elif self.effect_type == "matrix": - self.effect = MatrixEffect(Buffer(self.height, self.width), self.background) - elif self.effect_type == "drawlines": - self.effect = DrawLinesEffect(Buffer(self.height, self.width), self.background) - elif self.effect_type == "snow": - self.effect = SnowEffect(Buffer(self.height, self.width), self.background) - elif self.effect_type == "twinkle": - self.effect = TwinkleEffect(Buffer(self.height, self.width), self.background) - elif self.effect_type == "audio": - self.effect = AudioEffect(Buffer(self.height, self.width), self.background) - elif self.effect_type == "chat": - self.effect = ChatbotEffect(screen, Buffer(self.height, self.width), Buffer(self.height, self.width), self.background) - - self.effect.smart_transparent = False - - # buffers - self.image_buffer = Buffer(self.height, self.width).clear_buffer(val=None) - self.back_buffer = Buffer(self.height, self.width) - self.front_buffer = Buffer(self.height, self.width) - - # exit message stats - self.msg1 = " Frames Are Done " - self.msg2 = " Press Enter " - self.centered = True - self.wipe = False - self.x_loc = 0 - self.y_loc = 1 + self.effect = self.create_effect(effect_type) + self.image_buffer = self.create_buffer().clear_buffer(val=None) + self.back_buffer = self.create_buffer() + self.front_buffer = self.create_buffer() + + self.exit_messages = { + "msg1": " Frames Are Done ", + "msg2": " Press Enter ", + "centered": True, + "wipe": False, + "x_loc": 0, + "y_loc": 1, + } + + def validate_effect_type(self, effect_type: EffectType): + """Validate the effect type against available effects.""" + if effect_type not in valid_effect_types: + raise InvalidEffectTypeError(f"'{effect_type}' is not a valid effect. Please choose from {valid_effect_types}") + + def create_effect(self, effect_type: EffectType): + """Create an effect instance based on the specified type.""" + if effect_type == "static": + return StaticEffect(self.create_buffer(), self.background) + elif effect_type == "offset": + return OffsetEffect(self.create_buffer(), self.background) + elif effect_type == "noise": + return NoiseEffect(self.create_buffer(), self.background) + elif effect_type == "stars": + return StarEffect(self.create_buffer(), self.background) + elif effect_type == "plasma": + return PlasmaEffect(self.create_buffer(), self.background) + elif effect_type == "gol": + return GameOfLifeEffect(self.create_buffer(), self.background) + elif effect_type == "rain": + return RainEffect(self.create_buffer(), self.background) + elif effect_type == "matrix": + return MatrixEffect(self.create_buffer(), self.background) + elif effect_type == "drawlines": + return DrawLinesEffect(self.create_buffer(), self.background) + elif effect_type == "snow": + return SnowEffect(self.create_buffer(), self.background) + elif effect_type == "twinkle": + return TwinkleEffect(self.create_buffer(), self.background) + elif effect_type == "audio": + return AudioEffect(self.create_buffer(), self.background) + elif effect_type == "chat": + return ChatbotEffect(self.screen, self.create_buffer(), self.create_buffer(), self.background) + + def create_buffer(self) -> Buffer: + return Buffer(height=self.screen.height, width=self.screen.width) def update_collision(self, collision): - """ - Method for updating the collision for the rain effect - """ - if self.effect_type == "rain": - try: - self.collision = collision - self.effect.update_collision( - self.current_img_x, - self.current_img_y, - self.img_width, - self.img_height, - collision, - self.smart_transparent, - self.image_buffer, - ) - except Exception as e: - self.effect.update_collision(None, None, None, None, collision, None, None) - elif self.effect_type == "snow": - try: - self.collision = collision - self.effect.update_collision( - self.current_img_x, - self.current_img_y, - self.img_width, - self.img_height, - collision, - self.image_buffer, - ) - except Exception as e: - self.effect.update_collision(None, None, None, None, collision, None) - - def update_smart_transparent(self, smart_transparent): - """ - Enable / Disable the smart transparency effect - :param smart_transparent: True / False - """ + """Update collision for specific effects.""" + try: + self.collision = collision + self.effect.update_collision( + self.current_img_x, + self.current_img_y, + self.img_width, + self.img_height, + collision, + self.smart_transparent, + self.image_buffer, + ) + except Exception as e: + print(f"base_renderer: update_collision: warning: {e}") + self.effect.update_collision(None, None, None, None, collision, None) + + def update_smart_transparent(self, smart_transparent: bool): + """Enable/Disable the smart transparency effect.""" self.smart_transparent = smart_transparent self.effect.smart_transparent = smart_transparent def push_front_to_screen(self): - """ - Pushes changes between the back_buffer and front_buffer and applies them to the screen. - - :param None: This method does not take any parameters. - :return None: This method does not return anything. - """ + """Pushes changes from front_buffer to the screen.""" for y, x, val in self.front_buffer.get_buffer_changes(self.back_buffer): self.screen.print_at(val, x, y, 1) def render_exit(self): - """ - Renders out the exit prompt to the screen. - """ - if self.wipe: + """Renders the exit prompt to the screen.""" + if self.exit_messages['wipe']: self.back_buffer.clear_buffer() - if self.centered: - self.back_buffer.put_at_center(self.height // 2 - 1, self.msg1) - self.back_buffer.put_at_center(self.height // 2, self.msg2) + if self.exit_messages['centered']: + self.back_buffer.put_at_center(self.height // 2 - 1, self.exit_messages['msg1']) + self.back_buffer.put_at_center(self.height // 2, self.exit_messages['msg2']) else: - self.back_buffer.put_at( - self.x_loc, self.y_loc - 1, self.msg1, transparent=False - ) - self.back_buffer.put_at( - self.x_loc, self.y_loc, self.msg2, transparent=False - ) + self.back_buffer.put_at(self.exit_messages['x_loc'], self.exit_messages['y_loc'] - 1, self.exit_messages['msg1'], transparent=False) + self.back_buffer.put_at(self.exit_messages['x_loc'], self.exit_messages['y_loc'], self.exit_messages['msg2'], transparent=False) def run(self, end_message=True): - """ - Updates the image_buffer and effect_buffer. Then the image_buffer is applied over top the effect_buffer - and stored into the back_buffer. After the front_buffer is rendered to the screen, the front_buffer is synced - with the back_buffer. Why? So the effect and image, and there associated calculations can be done independently. - """ - + """Main loop for rendering frames.""" try: - if self.frames == INF: - frame = 0 - while True: - if self.screen.has_resized(): raise Exception("An error was encounter. The Screen was resized.") - sleep(self.time) + if self.frames != INF: + for frame in range(self.frames): + if self.screen.has_resized(): + raise Exception("The screen was resized.") + sleep(self.frame_time) self.render_img_frame(frame) self.effect.render_frame(frame) self.back_buffer.sync_with(self.effect.buffer) self.back_buffer.sync_over_top(self.image_buffer) self.push_front_to_screen() self.front_buffer.sync_with(self.back_buffer) - frame += 1 else: - for frame in range(self.frames): - if self.screen.has_resized(): raise Exception("An error was encounter. The Screen was resized.") - sleep(self.time) + frame = 0 + while True: + if self.screen.has_resized(): + raise Exception("The screen was resized.") + sleep(self.frame_time) self.render_img_frame(frame) self.effect.render_frame(frame) self.back_buffer.sync_with(self.effect.buffer) self.back_buffer.sync_over_top(self.image_buffer) self.push_front_to_screen() self.front_buffer.sync_with(self.back_buffer) - + frame += 1 + if end_message: self.render_exit() self.push_front_to_screen() - if sys.platform == 'win32': input() + if sys.platform == 'win32': + input() except KeyboardInterrupt: if end_message: self.render_exit() self.push_front_to_screen() - if sys.platform == 'win32': input() + if sys.platform == 'win32': + input() def update_exit_stats( self, msg1=None, msg2=None, wipe=None, x_loc=None, y_loc=None, centered=False ): - """ - Set the exit messages for when the animation finishes - :param msg1: primary message - :param msg2: secondary message - :param wipe: whether to clear the buffer - :param x_loc: where to put the message along the xaxis - :param y_loc: where to put the message along the yaxis - :param centered: whether or not the message should be centered - """ + """Set the exit messages for when the animation finishes.""" if msg1: - self.msg1 = msg1.replace("\n", "") + self.exit_messages['msg1'] = msg1.replace("\n", "") if msg2: - self.msg2 = msg2.replace("\n", "") - if wipe: - self.wipe = wipe - if x_loc and y_loc: - self.x_loc, self.y_loc = x_loc, y_loc - self.centered = centered + self.exit_messages['msg2'] = msg2.replace("\n", "") + if wipe is not None: + self.exit_messages['wipe'] = wipe + if x_loc is not None and y_loc is not None: + self.exit_messages['x_loc'], self.exit_messages['y_loc'] = x_loc, y_loc + self.exit_messages['centered'] = centered @abstractmethod def render_frame(self): - """ - To be defined by each renderer - """ \ No newline at end of file + """To be defined by each renderer.""" diff --git a/bruhanimate/bruhrenderer/center_renderer.py b/bruhanimate/bruhrenderer/center_renderer.py index e406dc2..a425655 100644 --- a/bruhanimate/bruhrenderer/center_renderer.py +++ b/bruhanimate/bruhrenderer/center_renderer.py @@ -14,8 +14,9 @@ limitations under the License. """ -from ..bruhutil import Screen from .base_renderer import BaseRenderer +from ..bruhutil import Screen +from ..bruhutil.bruhtypes import EffectType class CenterRenderer(BaseRenderer): @@ -29,13 +30,14 @@ def __init__( screen: Screen, img: list[str], frames: int = 100, - time: float = 0.1, - effect_type: str = "static", + frame_time: float = 0.1, + effect_type: EffectType = "static", background: str = " ", transparent: bool = False, + collision: bool = False ): super(CenterRenderer, self).__init__( - screen, frames, time, effect_type, background, transparent + screen, frames, frame_time, effect_type, background, transparent, collision ) self.background = background self.transparent = transparent diff --git a/bruhanimate/bruhrenderer/effect_renderer.py b/bruhanimate/bruhrenderer/effect_renderer.py index ccd2b9e..184b692 100644 --- a/bruhanimate/bruhrenderer/effect_renderer.py +++ b/bruhanimate/bruhrenderer/effect_renderer.py @@ -15,9 +15,10 @@ """ import sys - -from ..bruhutil import Screen, INF, sleep from .base_renderer import BaseRenderer +from ..bruhutil import Screen, INF, sleep +from ..bruhutil.bruherrors import ScreenResizedError +from ..bruhutil.bruhtypes import EffectType class EffectRenderer(BaseRenderer): @@ -26,16 +27,17 @@ class EffectRenderer(BaseRenderer): """ def __init__( + self, screen: Screen, frames: int = 100, - time: float = 0.1, - effect_type: str = "static", + frame_time: float = 0.1, + effect_type: EffectType = "static", background: str = " ", transparent: bool = False, ): super(EffectRenderer, self).__init__( - screen, frames, time, effect_type, background, transparent + screen, frames, frame_time, effect_type, background, transparent ) self.background = self.effect.background @@ -55,8 +57,8 @@ def run(self, end_message: bool = True): if self.frames == INF: frame = 0 while True: - if self.screen.has_resized(): raise Exception("An error was encounter. The Screen was resized.") - sleep(self.time) + if self.screen.has_resized(): raise ScreenResizedError("An error was encounter. The Screen was resized.") + sleep(self.frame_time) self.render_effect_frame(frame) self.back_buffer.sync_with(self.effect.buffer) self.push_front_to_screen() @@ -64,8 +66,8 @@ def run(self, end_message: bool = True): frame += 1 else: for frame in range(self.frames): - if self.screen.has_resized(): raise Exception("An error was encounter. The Screen was resized.") - sleep(self.time) + if self.screen.has_resized(): raise ScreenResizedError("An error was encounter. The Screen was resized.") + sleep(self.frame_time) self.render_effect_frame(frame) self.back_buffer.sync_with(self.effect.buffer) self.push_front_to_screen() @@ -82,4 +84,4 @@ def run(self, end_message: bool = True): if end_message: self.render_exit() - self.push_front_to_screen() \ No newline at end of file + self.push_front_to_screen() diff --git a/bruhanimate/bruhrenderer/focus_renderer.py b/bruhanimate/bruhrenderer/focus_renderer.py index 049dec2..2aa9d40 100644 --- a/bruhanimate/bruhrenderer/focus_renderer.py +++ b/bruhanimate/bruhrenderer/focus_renderer.py @@ -15,8 +15,9 @@ """ import random - +from typing import List from .base_renderer import BaseRenderer +from ..bruhutil.bruhtypes import EffectType class FocusRenderer(BaseRenderer): @@ -28,19 +29,20 @@ class FocusRenderer(BaseRenderer): def __init__( self, screen, - frames, - time, - img, - effect_type="static", - background=" ", - transparent=False, - start_frame=0, - reverse=False, - start_reverse=None, - loop=True + img: List[str], + frames: int = 100, + time: float = 0.1, + effect_type: EffectType = "static", + background: str = " ", + transparent: bool = False, + collision: bool = False, + start_frame: int = 0, + reverse: bool = False, + start_reverse: int = None, + loop: bool = True ): super(FocusRenderer, self).__init__( - screen, frames, time, effect_type, background, transparent + screen, frames, time, effect_type, background, transparent, collision ) self.background = background if background else " " self.transparent = transparent if transparent else False diff --git a/bruhanimate/bruhrenderer/pan_renderer.py b/bruhanimate/bruhrenderer/pan_renderer.py index 91ef4ea..eea7fdd 100644 --- a/bruhanimate/bruhrenderer/pan_renderer.py +++ b/bruhanimate/bruhrenderer/pan_renderer.py @@ -14,43 +14,50 @@ limitations under the License. """ -from ..bruhutil import Screen, VERTICAL, HORIZONTAL +from typing import List, Tuple, Optional from .base_renderer import BaseRenderer +from ..bruhutil import Screen +from ..bruhutil.bruhtypes import EffectType, PanRendererDirection, valid_pan_renderer_directions +from ..bruhutil.bruherrors import InvalidPanRendererDirectionError + class PanRenderer(BaseRenderer): """ A renderer to pan an image across the screen. - Update the image_buffer only. + Updates the image_buffer only. """ def __init__( self, screen: Screen, - img: list[str], - frames: int, - time: float, - effect_type: str = "static", + img: List[str], + frames: int = 100, + frame_time: float = 0.1, + effect_type: EffectType = "static", background: str = " ", transparent: bool = False, - direction: str = "h", + collision: bool = False, + direction: PanRendererDirection = "horizontal", shift_rate: int = 1, loop: bool = False, - ): - super(PanRenderer, self).__init__( - screen, frames, time, effect_type, background, transparent - ) - - self.direction = direction if direction and direction in ["h", "v"] else "h" + ) -> None: + super().__init__(screen, frames, frame_time, effect_type, background, transparent, collision) + self.direction = self.validate_direction(direction) self.img = img - self.shift_rate = int(shift_rate) + self.shift_rate = max(1, int(shift_rate)) self.loop = loop if self.img: self._set_img_attributes() - def _set_img_attributes(self): - """ - Sets the attributes for the image given it exists - """ + def validate_direction(self, direction: PanRendererDirection) -> PanRendererDirection: + if direction not in valid_pan_renderer_directions: + raise InvalidPanRendererDirectionError( + f"Invalid direction for PanRenderer. Please choose from {valid_pan_renderer_directions}" + ) + return direction + + def _set_img_attributes(self) -> None: + """Sets the attributes for the image given it exists.""" self.render_image = True self.img_height = len(self.img) self.img_width = len(self.img[0]) @@ -61,47 +68,34 @@ def _set_img_attributes(self): self.current_img_x = self.img_back self.current_img_y = self.img_top - def _set_padding(self, padding_vals): - """ - Set the padding for the image [DEPRECATED FOR NOW] - :param padding_vals: vals for padding [left-right, top-bottom] - """ - if not self.img: + @property + def img_size(self) -> Tuple[int, int]: + """Return the current image dimensions.""" + return len(self.img), len(self.img[0]) if self.img else (0, 0) + + def render_img_frame(self, frame_number: int) -> None: + """Renders out the next frame of the pan animation.""" + if not self.loop and self.img_back > self.width + 1: return - if len(padding_vals) == 2: - self.padding = padding_vals + if self.direction == "horizontal": + self.render_horizontal_frame(frame_number=frame_number) + elif self.direction == "vertical": + self.render_vertical_frame(frame_number=frame_number) - left_right = self.padding[0] - top_bottom = self.padding[1] - if left_right > 0 or top_bottom > 0: - tmp = [] - for _ in range(top_bottom): - tmp.append(" " * self.img_width) - for line in self.img: - tmp.append(line) - for _ in range(top_bottom): - tmp.append(" " * self.img_width) + def _set_padding(self, padding_vals: Tuple[int, int]) -> None: + """Set the padding for the image [DEPRECATED FOR NOW].""" + if not self.img or len(padding_vals) != 2: + return - for i in range(len(tmp)): - tmp[i] = (" " * left_right) + tmp[i] + (" " * left_right) + left_right, top_bottom = padding_vals + self.padding = (left_right, top_bottom) - self.img = [line for line in tmp] - self._set_img_attributes() + self.img = [" " * self.img_width for _ in range(top_bottom)] + + [(" " * left_right) + line + (" " * left_right) for line in self.img] + + [" " * self.img_width for _ in range(top_bottom)] - def render_img_frame(self, frame_number): - """ - Renders out the next frame of the pan animation, - if there is no image passed into the renderer then - the background is rendered on it's own - """ - if not self.loop: - if self.img_back > self.width + 1: - return - if self.direction == HORIZONTAL: - self.render_horizontal_frame(frame_number) - elif self.direction == VERTICAL: - self.render_veritcal_frame() + self._set_img_attributes() def render_horizontal_frame(self, frame_number): """ @@ -164,3 +158,5 @@ def render_horizontal_frame(self, frame_number): else: pass + def render_vertical_frame(self, frame_number): + pass diff --git a/bruhanimate/bruhutil/__init__.py b/bruhanimate/bruhutil/__init__.py index 42e7bdb..bde09bc 100644 --- a/bruhanimate/bruhutil/__init__.py +++ b/bruhanimate/bruhutil/__init__.py @@ -1,21 +1,19 @@ from .bruhscreen import Screen from .bruhffer import Buffer -from .images import get_image, text_to_image +from .bruhimage import get_image, text_to_image +from .bruhtypes import EffectType, Font, Image, valid_effect_types +from .bruherrors import ScreenResizedError, InvalidEffectTypeError, InvalidImageError from .utils import ( INF, VERTICAL, HORIZONTAL, VALID_EFFECTS, - FLAKE_COLORS, - FLAKE_FLIPS, - FLAKE_JUMPS, + SNOWFLAKE_COLORS, FLAKE_WEIGHT_CHARS, - FLAKES, GRADIENTS, GREY_SCALES, LIFE_COLORS, LIFE_SCALES, - NEXT_FLAKE_MOVE, NOISE, OLD_GREY_SCALES, PLASMA_COLORS, @@ -24,6 +22,7 @@ VALID_DIRECTIONS, WIND_DIRECTIONS, VALID_INTERFACES, + SNOWFLAKE_TYPES, sleep, ) @@ -36,16 +35,13 @@ "VERTICAL", "HORIZONTAL", "VALID_EFFECTS", - "FLAKE_COLORS", - "FLAKE_FLIPS", - "FLAKE_JUMPS", + "SNOWFLAKE_COLORS", "FLAKE_WEIGHT_CHARS", - "FLAKES", + "SNOWFLAKE_TYPES", "GRADIENTS", "GREY_SCALES", "LIFE_COLORS", "LIFE_SCALES", - "NEXT_FLAKE_MOVE", "NOISE", "OLD_GREY_SCALES", "PLASMA_COLORS", @@ -55,5 +51,12 @@ "WIND_DIRECTIONS", "VALID_INTERFACES", "sleep", + "Effect", + "Font", + "Image", + "valid_effects", + "ScreenResizedError", + "InvalidEffectTypeError", + "InvalidImageError" ] diff --git a/bruhanimate/bruhutil/bruherrors.py b/bruhanimate/bruhutil/bruherrors.py new file mode 100644 index 0000000..4f5ba13 --- /dev/null +++ b/bruhanimate/bruhutil/bruherrors.py @@ -0,0 +1,27 @@ +class ScreenResizedError(Exception): + """Exception raised when the screen is resized.""" + + def __init__(self, message="The screen has been resized"): + self.message = message + super().__init__(self.message) + +class InvalidEffectTypeError(Exception): + """Exception raised when an invalid effect type is used.""" + + def ____init__(self, message="Invalid effect type"): + self.message = message + super().__init__(self.message) + +class InvalidImageError(Exception): + """Exception raised when an invalid effect type is used.""" + + def ____init__(self, message="Invalid image"): + self.message = message + super().__init__(self.message) + +class InvalidPanRendererDirectionError(Exception): + """Exception raised when an invalid effect type is used.""" + + def ____init__(self, message="Invalid direction"): + self.message = message + super().__init__(self.message) diff --git a/bruhanimate/bruhutil/images.py b/bruhanimate/bruhutil/bruhimage.py similarity index 82% rename from bruhanimate/bruhutil/images.py rename to bruhanimate/bruhutil/bruhimage.py index ce0f9f8..4fd0b23 100644 --- a/bruhanimate/bruhutil/images.py +++ b/bruhanimate/bruhutil/bruhimage.py @@ -14,12 +14,13 @@ limitations under the License. """ -import re -import random import pyfiglet -import bruhcolor +from typing import List +from .bruhtypes import Image, Font +from .bruherrors import InvalidImageError -BRUH = [ + +bruh = [ r"loading BRUH SHELL 2.0 loading BRUH SHELL 2.0 loading BRUH ", r"loading BRUH SHELL 2.0 loading BRUH SHELL 2.0 loading BRUH ", r"loading BRUH SHELL 2.0 loading BRUH SHELL 2.0 loading BRUH ", @@ -40,7 +41,7 @@ r"loading BRUH SHELL 2.0 loading BRUH SHELL 2.0 loading BRUH ", ] -BRUH_EMPTY = [ +bruh_empty = [ r" _ _ ", r" /\ \ / /\ ", r" / \ \ / / \ ", @@ -54,7 +55,7 @@ r"\________/ \/_/ \_________\/ ", ] -BRUH_COMPUTER = [ +bruh_computer = [ f" .,,uod8B8bou,,. ", f" ..,uod8BBBBBBBBBBBBBBBBRPFT?l!i:. ", f" ,=m8BBBBBBBBBBBBBBBRPFT?!|||||||||||||| ", @@ -87,7 +88,7 @@ f" `!^\"' ", ] -COMPUTER = [ +computer = [ f" .,,uod8B8bou,,. ", f" ..,uod8BBBBBBBBBBBBBBBBRPFT?l!i:. ", f" ,=m8BBBBBBBBBBBBBBBRPFT?!|||||||||||||| ", @@ -120,7 +121,7 @@ f" `!^\"' ", ] -HEY = [ +hey = [ f" __ __ ", f" / / / /__ __ __", f" / /_/ / _ \/ / / /", @@ -129,7 +130,7 @@ f" /____/ ", ] -TWOPOINT = [ +twopoint = [ r" ", r" ____________ _ _ _ _ _____ _ _ _____ _ _ _____ _____ ", r" | ___ \ ___ \ | | | | | |/ ___| | | || ___| | | | / __ \| _ | ", @@ -140,7 +141,7 @@ r" ", ] -CHRISTMAS_1 = [ +christmas = [ r" ", r" _\/_ ", r" /\ ", @@ -158,61 +159,57 @@ r" ", ] - -_REGISTERED_IMAGES = { - "BRUH": BRUH, - "BRUH_EMPTY": BRUH_EMPTY, - "COMPUTER": COMPUTER, - "HEY": HEY, - "TWOPOINT": TWOPOINT, - "CHRISTMAS_1": CHRISTMAS_1, +image_mappings = { + "bruh": bruh, + "bruh_empty": bruh_empty, + "bruh_computer": bruh_computer, + "computer": computer, + "hey": hey, + "twopoint": twopoint, + "christmas": christmas, } -def get_image(name): +def get_image(name: Image = "hey") -> List[str]: """ - Function to return one of the premade images - :param name: name of the image to get + Retrieves one of the pre-defined ASCII art images based on the given name. + + :param name: The name of the image to retrieve. + :return: A list of strings representing the ASCII art image. """ - if name in _REGISTERED_IMAGES: - return _REGISTERED_IMAGES[name] - else: - return _REGISTERED_IMAGES["HEY"] + if name not in image_mappings: + raise InvalidImageError(f"Unknown / non-registered image: {name}") + return image_mappings.get(name) def text_to_image( - text, font=pyfiglet.DEFAULT_FONT, padding_top_bottom=0, padding_left_right=0 -): + text: str, + font: Font = pyfiglet.DEFAULT_FONT, + padding_top_bottom: int = 0, + padding_left_right: int = 0, +) -> List[str]: """ - Function to take a piece of text and turn it into - an image that can be used. - :param text: text to turn into an image - :param font: pyfiglet font to use - :param padding_top_bottom: padding to apply to the generated image - :param padding_left_right: padding to apply to the generate image + Converts text into ASCII art using the specified font and padding. + + :param text: The text to convert into ASCII art. + :param font: The font to use for the ASCII art. + :param padding_top_bottom: Number of empty lines to add above and below the ASCII art. + :param padding_left_right: Number of spaces to add on the left and right of each line. + :return: A list of strings representing the ASCII art with padding. """ - - img_flat = None try: img_flat = pyfiglet.Figlet(font).renderText(text) - except: - img_flat = pyfiglet.Figlet().renderText("Invalid Font") - img = [] - row = "" - for val in img_flat: - if val == "\n": - img.append(row) - row = "" - else: - row += val + except pyfiglet.FontError: + img_flat = pyfiglet.Figlet("standard").renderText(text) + + img = img_flat.splitlines() if padding_top_bottom > 0: - img = ( - [" " * len(img[0]) for __ in range(padding_top_bottom)] - + img - + [" " * len(img[0]) for __ in range(padding_top_bottom)] - ) + empty_line = " " * len(img[0]) + img = ([empty_line] * padding_top_bottom + img + [empty_line] * padding_top_bottom) + if padding_left_right > 0: - for i in range(len(img)): - img[i] = (" " * padding_left_right) + img[i] + (" " * padding_left_right) + padding = " " * padding_left_right + img = [f"{padding}{line}{padding}" for line in img] + return img diff --git a/bruhanimate/bruhutil/bruhtypes.py b/bruhanimate/bruhutil/bruhtypes.py new file mode 100644 index 0000000..d79fdcf --- /dev/null +++ b/bruhanimate/bruhutil/bruhtypes.py @@ -0,0 +1,25 @@ +""" +Copyright 2023 Ethan Christensen + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import sys +from typing import Literal, Union + +Font = Literal["1943____","1row","3-d","3d-ascii","3d_diagonal","3x5","4max","4x4_offr","5lineoblique","5x7","5x8","64f1____","6x10","6x9","acrobatic","advenger","alligator","alligator2","alpha","alphabet","amc_3_line","amc_3_liv1","amc_aaa01","amc_neko","amc_razor","amc_razor2","amc_slash","amc_slider","amc_thin","amc_tubes","amc_untitled","ansi_regular","ansi_shadow","aquaplan","arrows","ascii_new_roman","ascii___","asc_____","assalt_m","asslt__m","atc_gran","atc_____","avatar","a_zooloo","b1ff","banner","banner3-D","banner3","banner4","barbwire","basic","battlesh","battle_s","baz__bil","bear","beer_pub","bell","benjamin","big","bigchief","bigfig","big_money-ne","big_money-nw","big_money-se","big_money-sw","binary","block","blocks","blocky","bloody","bolger","braced","bright","brite","briteb","britebi","britei","broadway","broadway_kb","bubble","bubble_b","bubble__","bulbhead","b_m__200","c1______","c2______","calgphy2","caligraphy","calvin_s","cards","catwalk","caus_in_","char1___","char2___","char3___","char4___","charact1","charact2","charact3","charact4","charact5","charact6","characte","charset_","chartr","chartri","chiseled","chunky","clb6x10","clb8x10","clb8x8","cli8x8","clr4x6","clr5x10","clr5x6","clr5x8","clr6x10","clr6x6","clr6x8","clr7x10","clr7x8","clr8x10","clr8x8","coil_cop","coinstak","cola","colossal","computer","com_sen_","contessa","contrast","convoy__","cosmic","cosmike","cour","courb","courbi","couri","crawford","crawford2","crazy","cricket","cursive","cyberlarge","cybermedium","cybersmall","cygnet","c_ascii_","c_consen","danc4","dancing_font","dcs_bfmo","decimal","deep_str","defleppard","def_leppard","delta_corps_priest_1","demo_1__","demo_2__","demo_m__","devilish","diamond","diet_cola","digital","doh","doom","dos_rebel","dotmatrix","double","double_shorts","drpepper","druid___","dwhistled","d_dragon","ebbs_1__","ebbs_2__","eca_____","eftichess","eftifont","eftipiti","eftirobot","eftitalic","eftiwall","eftiwater","efti_robot","electronic","elite","epic","etcrvs__","e__fist_","f15_____","faces_of","fairligh","fair_mea","fantasy_","fbr12___","fbr1____","fbr2____","fbr_stri","fbr_tilt","fender","filter","finalass","fireing_","fire_font-k","fire_font-s","flipped","flower_power","flyn_sh","fourtops","fp1_____","fp2_____","fraktur","funky_dr","fun_face","fun_faces","future_1","future_2","future_3","future_4","future_5","future_6","future_7","future_8","fuzzy","gauntlet","georgi16","georgia11","ghost","ghost_bo","ghoulish","glenyn","goofy","gothic","gothic__","graceful","gradient","graffiti","grand_pr","greek","green_be","hades___","heart_left","heart_right","heavy_me","helv","helvb","helvbi","helvi","henry_3d","heroboti","hex","hieroglyphs","high_noo","hills___","hollywood","home_pak","horizontal_left","horizontal_right","house_of","hypa_bal","hyper___","icl-1900","impossible","inc_raw_","invita","isometric1","isometric2","isometric3","isometric4","italic","italics_","ivrit","jacky","jazmine","jerusalem","joust___","js_block_letters","js_bracket_letters","js_capital_curves","js_cursive","js_stick_letters","katakana","kban","keyboard","kgames_i","kik_star","knob","konto","konto_slant","krak_out","larry3d","lazy_jon","lcd","lean","letters","letterw3","letter_w","lexible_","lil_devil","line_blocks","linux","lockergnome","madrid","mad_nurs","magic_ma","marquee","master_o","maxfour","mayhem_d","mcg_____","merlin1","merlin2","mig_ally","mike","mini","mirror","mnemonic","modern__","modular","morse","morse2","moscow","mshebrew210","muzzle","nancyj-fancy","nancyj-improved","nancyj-underlined","nancyj","new_asci","nfi1____","nipples","notie_ca","npn_____","nscript","ntgreek","nvscript","o8","octal","odel_lak","ogre","ok_beer_","old_banner","os2","outrun__","pacos_pe","panther_","patorjk's_cheese","patorjk-hex","pawn_ins","pawp","peaks","pebbles","pepper","phonix__","platoon2","platoon_","pod_____","poison","puffy","puzzle","pyramid","p_skateb","p_s_h_m_","r2-d2___","radical_","rad_phan","rad_____","rainbow_","rally_s2","rally_sp","rammstein","rampage_","rastan__","raw_recu","rci_____","rectangles","red_phoenix","relief","relief2","rev","ripper!_","road_rai","rockbox_","rok_____","roman","roman___","rot13","rotated","rounded","rowancap","rozzo","runic","runyc","sans","sansb","sansbi","sansi","santa_clara","sblood","sbook","sbookb","sbookbi","sbooki","script","script__","serifcap","shadow","shimrod","short","skateord","skateroc","skate_ro","sketch_s","slant","slant_relief","slide","slscript","sl_script","small","small_caps","small_poison","small_shadow","small_slant","smisome1","smkeyboard","smscript","smshadow","smslant","smtengwar","sm______","soft","space_op","spc_demo","speed","spliff","stacey","stampate","stampatello","standard","starwars","star_strips","star_war","stealth_","stellar","stencil1","stencil2","stforek","stick_letters","stop","straight","street_s","stronger_than_all","sub-zero","subteran","super_te","swamp_land","swan","sweet","tanja","tav1____","taxi____","tec1____","tecrvs__","tec_7000","tengwar","term","test1","the_edge","thick","thin","this","thorned","threepoint","ticks","ticksslant","tiles","times","timesofl","tinker-toy","ti_pan__","tomahawk","tombstone","top_duck","train","trashman","trek","triad_st","ts1_____","tsalagi","tsm_____","tsn_base","tty","ttyb","tubular","twin_cob","twisted","twopoint","type_set","t__of_ap","ucf_fan_","ugalympi","unarmed_","univers","usaflag","usa_pq__","usa_____","utopia","utopiab","utopiabi","utopiai","varsity","vortron_","war_of_w","wavy","weird","wet_letter","whimsy","wow","xbrite","xbriteb","xbritebi","xbritei","xchartr","xchartri","xcour","xcourb","xcourbi","xcouri","xhelv","xhelvb","xhelvbi","xhelvi","xsans","xsansb","xsansbi","xsansi","xsbook","xsbookb","xsbookbi","xsbooki","xtimes","xtty","xttyb","yie-ar__","yie_ar_k","z-pilot_","zig_zag_","zone7___"] +Image = Literal["bruh","bruh_empty","bruh_computer","computer", "hey","twopoint","christmas"] +EffectType = Literal["static","offset","noise","stars","plasma","gol","rain","matrix","drawlines","snow","twinkle","audio","chat"] +valid_effect_types = {"static","offset","noise","stars","plasma","gol","rain","matrix","drawlines","snow","twinkle","audio","chat"} +PanRendererDirection = Literal["horizontal", "vertical"] +valid_pan_renderer_directions = {"horizontal", "vertical"} diff --git a/bruhanimate/bruhutil/utils.py b/bruhanimate/bruhutil/utils.py index d6b3e42..8c183a3 100644 --- a/bruhanimate/bruhutil/utils.py +++ b/bruhanimate/bruhutil/utils.py @@ -25,9 +25,9 @@ def sleep(s): VALID_EFFECTS = ["static", "offset", "noise", "stars", "plasma", "gol", "rain", "matrix", "drawlines", "snow", "twinkle", "audio", "chat"] -HORIZONTAL = "h" +HORIZONTAL = "horizontal" -VERTICAL = "v" +VERTICAL = "vertical" INF = float("inf") @@ -69,33 +69,24 @@ def sleep(s): NOISE = "!@#$%^&*()_+1234567890-=~`qazwsxedcrfvtgbyhnujmik,ol.p;/[']\QAZXSWEDCVFRTGBNHYUJM?:P{\"}|" -FLAKES = {1: "*", 3: "+", 7: "."} - -FLAKE_COLORS = {1: 253, 3: 69, 7: 31} - -FLAKE_JUMPS = { - 1: [1, 2, 3], - 3: [1, 2, 3], - 7: [1, 2], +SNOWFLAKE_TYPES = { + ".": {"speed": 5}, + ".": {"speed": 4}, + "+": {"speed": 3}, + "+": {"speed": 2}, + "*": {"speed": 1}, } -NEXT_FLAKE_MOVE = { - ("center", "right"): 1, - ("center", "left"): -1, - ("left", "center"): 1, - ("right", "center"): -1, - ("right", "left"): None, # not valid - ("left", "right"): None, # not valid +SNOWFLAKE_COLORS = { + ".": 31, + ".": 31, + "+": 69, + "+": 69, + "*": 255, } FLAKE_WEIGHT_CHARS = {1: ",", 4: ";", 7: "*", 12: "@", 18: "#"} -FLAKE_FLIPS = { - 1: ["*", "1"], - 3: ["+", "3"], - 7: [".", "7"], -} - TWINKLE_COLORS = {idx: val for idx, val in enumerate(range(232, 256))} VALID_INTERFACES = ["ollama", "openai"] diff --git a/bruhanimate/demos/audio_demo.py b/bruhanimate/demos/audio_demo.py index de75d48..6981e8f 100644 --- a/bruhanimate/demos/audio_demo.py +++ b/bruhanimate/demos/audio_demo.py @@ -17,15 +17,15 @@ import os os.system(" ") -from ..bruhutil import Screen, images, GRADIENTS +from ..bruhutil import Screen, bruhimage, GRADIENTS from ..bruhrenderer import CenterRenderer def audio(screen): renderer = CenterRenderer( screen=screen, frames=float("inf"), - img=images.text_to_image("AUDIO!"), - time=0.01, + img=bruhimage.text_to_image("AUDIO!"), + frame_time=0.01, effect_type="audio", background=" ", transparent=False, diff --git a/bruhanimate/demos/chatbot_demo.py b/bruhanimate/demos/chatbot_demo.py index 90844f5..9c7bac3 100644 --- a/bruhanimate/demos/chatbot_demo.py +++ b/bruhanimate/demos/chatbot_demo.py @@ -27,7 +27,7 @@ def chatbot(screen: Screen, openai_api_key: str, name: str): renderer = EffectRenderer( screen=screen, frames=float("inf"), - time=0.03, + frame_time=0.03, effect_type="chat", background=" ", transparent=False, diff --git a/bruhanimate/demos/gol_demo.py b/bruhanimate/demos/gol_demo.py index 7848e30..e7cc03f 100644 --- a/bruhanimate/demos/gol_demo.py +++ b/bruhanimate/demos/gol_demo.py @@ -17,7 +17,7 @@ import os os.system(" ") -from ..bruhutil import Screen, images +from ..bruhutil import Screen, bruhimage from ..bruhrenderer import CenterRenderer @@ -25,8 +25,8 @@ def gol(screen): renderer = CenterRenderer( screen=screen, frames=float("inf"), - img=images.text_to_image("GOL!"), - time=0.0, + img=bruhimage.text_to_image("GOL!"), + frame_time=0.0, effect_type="gol", background=" ", transparent=False, diff --git a/bruhanimate/demos/holiday.py b/bruhanimate/demos/holiday.py index 497e9d7..9143240 100644 --- a/bruhanimate/demos/holiday.py +++ b/bruhanimate/demos/holiday.py @@ -25,8 +25,8 @@ def holiday(screen): renderer = CenterRenderer( screen=screen, frames=2000, - time=0.075, - img=get_image("CHRISTMAS_1"), + frame_time=0.075, + img=get_image("christmas"), effect_type="snow", background=" ", transparent=True, diff --git a/bruhanimate/demos/line_demo.py b/bruhanimate/demos/line_demo.py index a1b2c62..e8fbb1c 100644 --- a/bruhanimate/demos/line_demo.py +++ b/bruhanimate/demos/line_demo.py @@ -17,7 +17,7 @@ import os os.system("") -from ..bruhutil import Screen, images +from ..bruhutil import Screen, bruhimage from ..bruhrenderer import PanRenderer @@ -26,7 +26,7 @@ def demo(screen, img, frames, time, effect, background, transparent): renderer = PanRenderer( screen=screen, frames=frames, - time=time, + frame_time=time, img=img, effect_type=effect, background=background, @@ -48,7 +48,7 @@ def demo(screen, img, frames, time, effect, background, transparent): def run(): - image = images.text_to_image( + image = bruhimage.text_to_image( "HELLO WORLD!", padding_top_bottom=1, padding_left_right=3 ) Screen.show(demo, args=(image, 500, 0.05, "drawlines", " ", True)) diff --git a/bruhanimate/demos/matrix_demo.py b/bruhanimate/demos/matrix_demo.py index db59de6..3acadac 100644 --- a/bruhanimate/demos/matrix_demo.py +++ b/bruhanimate/demos/matrix_demo.py @@ -17,16 +17,16 @@ import os os.system(" ") -from ..bruhutil import Screen, images +from ..bruhutil import Screen, bruhimage from ..bruhrenderer import CenterRenderer def matrix(screen): renderer = CenterRenderer( screen=screen, - img=images.text_to_image("MATRIX!"), + img=bruhimage.text_to_image("MATRIX!"), frames=float("inf"), - time=0, + frame_time=0, effect_type="matrix", background=" ", transparent=False, diff --git a/bruhanimate/demos/noise_demo.py b/bruhanimate/demos/noise_demo.py index 3e7fb6c..66d37fd 100644 --- a/bruhanimate/demos/noise_demo.py +++ b/bruhanimate/demos/noise_demo.py @@ -17,7 +17,7 @@ import os os.system(" ") -from ..bruhutil import Screen, images +from ..bruhutil import Screen, bruhimage from ..bruhrenderer import CenterRenderer @@ -25,8 +25,8 @@ def noise(screen): renderer = CenterRenderer( screen=screen, frames=float("inf"), - img=images.text_to_image("NOISE!"), - time=0.0, + img=bruhimage.text_to_image("NOISE!"), + frame_time=0.0, effect_type="noise", background=" ", transparent=False, diff --git a/bruhanimate/demos/offset_demo.py b/bruhanimate/demos/offset_demo.py index 8ae59a8..957e76c 100644 --- a/bruhanimate/demos/offset_demo.py +++ b/bruhanimate/demos/offset_demo.py @@ -17,7 +17,7 @@ import os os.system(" ") -from ..bruhutil import Screen, images +from ..bruhutil import Screen, bruhimage from ..bruhrenderer import CenterRenderer @@ -25,8 +25,8 @@ def offset(screen): renderer = CenterRenderer( screen=screen, frames=float("inf"), - img=images.text_to_image("OFFSET!"), - time=0.0, + img=bruhimage.text_to_image("OFFSET!"), + frame_time=0.0, effect_type="offset", background="!!@@##$$%%^^&&**(())__++", transparent=False, diff --git a/bruhanimate/demos/plasma_demo.py b/bruhanimate/demos/plasma_demo.py index f79194f..976935c 100644 --- a/bruhanimate/demos/plasma_demo.py +++ b/bruhanimate/demos/plasma_demo.py @@ -17,18 +17,18 @@ import os os.system(" ") -from ..bruhutil import Screen, images +from ..bruhutil import Screen, bruhimage from ..bruhrenderer import FocusRenderer def show(screen): - image = images.text_to_image("PLASMA!", padding_top_bottom=1, padding_left_right=3) + image = bruhimage.text_to_image("PLASMA!", padding_top_bottom=1, padding_left_right=3) # Create the renderer renderer = FocusRenderer( screen=screen, frames=500, - time=0, + frame_time=0, img=image, effect_type="plasma", background=" ", diff --git a/bruhanimate/demos/rain_demo.py b/bruhanimate/demos/rain_demo.py index df9e600..f0f7d23 100644 --- a/bruhanimate/demos/rain_demo.py +++ b/bruhanimate/demos/rain_demo.py @@ -17,7 +17,7 @@ import os os.system(" ") -from ..bruhutil import Screen, images +from ..bruhutil import Screen, bruhimage from ..bruhrenderer import CenterRenderer @@ -25,8 +25,8 @@ def rain(screen): renderer = CenterRenderer( screen=screen, frames=float("inf"), - img=images.text_to_image("RAIN!"), - time=0.01, + img=bruhimage.text_to_image("RAIN!"), + frame_time=0.01, effect_type="rain", background=" ", transparent=False, diff --git a/bruhanimate/demos/snow_demo.py b/bruhanimate/demos/snow_demo.py index e0cc1be..9886946 100644 --- a/bruhanimate/demos/snow_demo.py +++ b/bruhanimate/demos/snow_demo.py @@ -17,16 +17,16 @@ import os os.system(" ") -from ..bruhutil import Screen, images +from ..bruhutil import Screen, bruhimage from ..bruhrenderer import CenterRenderer def snow(screen): renderer = CenterRenderer( screen=screen, - img=images.text_to_image("SNOW!"), + img=bruhimage.text_to_image("SNOW!"), frames=float("inf"), - time=0.075, + frame_time=0.075, effect_type="snow", background=" ", transparent=True, diff --git a/bruhanimate/demos/stars_demo.py b/bruhanimate/demos/stars_demo.py index 9eea7cf..d70427a 100644 --- a/bruhanimate/demos/stars_demo.py +++ b/bruhanimate/demos/stars_demo.py @@ -25,7 +25,7 @@ def stars(screen): renderer = EffectRenderer( screen=screen, frames=float("inf"), - time=0.05, + frame_time=0.05, effect_type="stars", background=" ", transparent=False, diff --git a/bruhanimate/demos/static_demo.py b/bruhanimate/demos/static_demo.py index c80ed99..d83d289 100644 --- a/bruhanimate/demos/static_demo.py +++ b/bruhanimate/demos/static_demo.py @@ -17,7 +17,7 @@ import os os.system(" ") -from ..bruhutil import Screen, images +from ..bruhutil import Screen, bruhimage from ..bruhrenderer import CenterRenderer @@ -25,8 +25,8 @@ def static(screen): renderer = CenterRenderer( screen=screen, frames=float("inf"), - img=images.text_to_image("STATIC!"), - time=0.0, + img=bruhimage.text_to_image("STATIC!"), + frame_time=1.0, effect_type="static", background="This is a static background! ", transparent=False, diff --git a/bruhanimate/demos/twinkle_demo.py b/bruhanimate/demos/twinkle_demo.py index 1344f42..3db801c 100644 --- a/bruhanimate/demos/twinkle_demo.py +++ b/bruhanimate/demos/twinkle_demo.py @@ -17,16 +17,16 @@ import os os.system(" ") -from ..bruhutil import Screen, images +from ..bruhutil import Screen, bruhimage from ..bruhrenderer import CenterRenderer def twinkle(screen): renderer = CenterRenderer( screen=screen, - img=images.text_to_image("TWINKLE!"), + img=bruhimage.text_to_image("TWINKLE!"), frames=float("inf"), - time=0.05, + frame_time=0.05, effect_type="twinkle", background=" ", transparent=False, diff --git a/setup.py b/setup.py index 75eb555..6e429c1 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ with codecs.open(os.path.join(here, "README.md"), encoding="utf-8") as fh: long_description = "\n" + fh.read() -VERSION = "0.2.65" +VERSION = "0.2.69" DESCRIPTION = 'ASCII Terminal Animation Package' LONG_DESCRIPTION = 'A package that allows for various animations in the terminal'