diff --git a/resources/mock_data.txt b/resources/mock_data.txt index 69e0dd7..f5e1986 100644 --- a/resources/mock_data.txt +++ b/resources/mock_data.txt @@ -17,7 +17,7 @@ state_3=14 seed_4=524 state_4=13 seed_5=251 -state_5=10 +state_5=15 seed_6=808 state_6=4 active_0=0 diff --git a/src/forge_invar/constants.py b/src/forge_invar/constants.py index c552bec..9999ac2 100644 --- a/src/forge_invar/constants.py +++ b/src/forge_invar/constants.py @@ -17,21 +17,38 @@ BACKGROUND_IMG_PATH = RESOURCES_PATH / "background.png" ESTIMATED_BOOT_TIME_SECONDS = 18.0 SELECTED_RECT_BOUNDS = [ - (10, 14, 28, 32), - (10, 38, 28, 56), - (39, 14, 57, 32), - (39, 38, 57, 56), (0, 0, 0, 0), - (97, 26, 115, 44), + (10, 14, 28, 32), # SRC A + (10, 38, 28, 56), # SRC B + (39, 14, 57, 32), # FX A + (39, 38, 57, 56), # FX B + (0, 0, 0, 0), + (97, 26, 115, 44), # MFX ] ACTIVE_SRC_RECT_BOUNDS = [ - (10, 32, 28, 32), - (10, 56, 28, 56), + (0, 0, 0, 0), + (10, 32, 28, 32), # SRC A + (10, 56, 28, 56), # SRC B ] ACTIVE_FX_RECT_BOUNDS = [ - (39, 32, 57, 32), - (39, 56, 57, 56), - (97, 44, 115, 44), + (0, 0, 0, 0), + (39, 32, 57, 32), # FX A + (39, 56, 57, 56), # FX B + (97, 44, 115, 44), # MFX ] + +SRCA_FRAG = 1 +SRCB_FRAG = 2 +FXA_FRAG = 3 +FXB_FRAG = 4 +MIX_FRAG = 5 +MFX_FRAG = 6 + +SRC_GROUP = 1 +FX_GROUP = 2 +EXTRA_GROUP = 3 + +GROUPS_DELTA = [0, 12, 33] +GROUPS_SIZE = [6, 7, 2] diff --git a/src/forge_invar/display.py b/src/forge_invar/display.py index 9032b5a..1ac8876 100644 --- a/src/forge_invar/display.py +++ b/src/forge_invar/display.py @@ -9,8 +9,15 @@ from .constants import ( ESTIMATED_BOOT_TIME_SECONDS, FONT_1_PATH, FONT_2_PATH, + FX_GROUP, + FXA_FRAG, + FXB_FRAG, LOADING_IMG_PATH, + MFX_FRAG, SELECTED_RECT_BOUNDS, + SRC_GROUP, + SRCA_FRAG, + SRCB_FRAG, ) from .enums import ForgeState from .forge_data import ForgeData @@ -65,11 +72,11 @@ def __get_running_display(data: ForgeData) -> Image.Image: draw.circle((55 + 8 * data.page, 61), 2, fill="white") # selected - draw.rectangle(SELECTED_RECT_BOUNDS[data.selected], outline="white", fill=None) + draw.rectangle(SELECTED_RECT_BOUNDS[data.frag_selected], outline="white", fill=None) # active - draw.rectangle(ACTIVE_SRC_RECT_BOUNDS[data.active(0)], fill="white") - draw.rectangle(ACTIVE_FX_RECT_BOUNDS[data.active(1)], fill="white") + draw.rectangle(ACTIVE_SRC_RECT_BOUNDS[data.group_active(SRC_GROUP)], fill="white") + draw.rectangle(ACTIVE_FX_RECT_BOUNDS[data.group_active(FX_GROUP)], fill="white") # fx/mix values draw.rectangle((41, 16 + round(14 * (1 - data.fxa_value)), 55, 30), fill="white") @@ -78,16 +85,45 @@ def __get_running_display(data: ForgeData) -> Image.Image: draw.rectangle((70, 28 + round(14 * (1 - data.mix_value)), 84, 42), fill="white") # active shaders - draw2.text((16, 20), hex(data.frag_state(0))[2].upper(), fill="white", font=FONT_2) - draw2.text((16, 44), hex(data.frag_state(1))[2].upper(), fill="white", font=FONT_2) - draw2.text((45, 20), hex(data.frag_state(2))[2].upper(), fill="white", font=FONT_2) - draw2.text((45, 44), hex(data.frag_state(3))[2].upper(), fill="white", font=FONT_2) - draw2.text((103, 32), hex(data.frag_state(5))[2].upper(), fill="white", font=FONT_2) - # TODO print when random - # TODO print when inverted + draw2.text( + (16, 20), hex(data.frag_state(SRCA_FRAG))[2].upper(), fill="white", font=FONT_2 + ) + draw2.text( + (16, 44), hex(data.frag_state(SRCB_FRAG))[2].upper(), fill="white", font=FONT_2 + ) + draw2.text( + (45, 20), hex(data.frag_state(FXA_FRAG))[2].upper(), fill="white", font=FONT_2 + ) + draw2.text( + (45, 44), hex(data.frag_state(FXB_FRAG))[2].upper(), fill="white", font=FONT_2 + ) + draw2.text( + (103, 32), hex(data.frag_state(MFX_FRAG))[2].upper(), fill="white", font=FONT_2 + ) + # mix draw2.text((74, 32), "K" if data.mix_mode_key else "M", fill="white", font=FONT_2) + # random states indicator + if data.frag_state_rand(SRCA_FRAG): + draw2.rectangle((23, 18, 24, 19), fill="white") + if data.frag_state_rand(SRCB_FRAG): + draw2.rectangle((23, 42, 24, 43), fill="white") + if data.frag_state_rand(FXA_FRAG): + draw2.rectangle((52, 18, 53, 19), fill="white") + if data.frag_state_rand(FXB_FRAG): + draw2.rectangle((52, 42, 53, 43), fill="white") + if data.frag_state_rand(MFX_FRAG): + draw2.rectangle((110, 30, 111, 31), fill="white") + + # inverted fx + if data.fxa_inverted: + draw2.rectangle((43, 18, 53, 18), fill="white") + if data.fxb_inverted: + draw2.rectangle((43, 42, 53, 42), fill="white") + if data.mfx_inverted: + draw2.rectangle((101, 30, 111, 30), fill="white") + # TODO show feedback # TODO show blank out # TODO show inverted out diff --git a/src/forge_invar/forge_data.py b/src/forge_invar/forge_data.py index 6f4fe4b..7c1665f 100644 --- a/src/forge_invar/forge_data.py +++ b/src/forge_invar/forge_data.py @@ -2,9 +2,21 @@ import collections import pathlib import typing -from .constants import FORGE_SAVE_BACKUP_PATH, FORGE_SAVE_PATH, MOCK_SAVE_PATH +from .constants import ( + EXTRA_GROUP, + FORGE_SAVE_BACKUP_PATH, + FORGE_SAVE_PATH, + FX_GROUP, + FXA_FRAG, + FXB_FRAG, + GROUPS_DELTA, + GROUPS_SIZE, + MFX_FRAG, + MIX_FRAG, + MOCK_SAVE_PATH, +) from .enums import ForgeState -from .glsl_funcs import fract, magic, magic_trigger +from .glsl_funcs import fract, magic, magic_trigger, rand_time class ForgeRawData: @@ -65,21 +77,54 @@ class ForgeData: return True return False - def value(self, n: int) -> tuple[float, float, float]: + def __value(self, n: int) -> tuple[float, float, float]: return ( self.__data[f"value_{n}_x"] / 127, self.__data[f"value_{n}_y"] / 127, self.__data[f"value_{n}_z"] / 127, ) - def frag_state(self, n: int) -> int: - return int(self.__data[f"state_{n}"]) + def frag_state_rand(self, frag: int) -> bool: + return int(self.__data[f"state_{frag - 1}"]) >= 14 - def active(self, n: int) -> int: - return int(self.__data[f"active_{n}"]) + def frag_state(self, frag: int) -> int: + state = int(self.__data[f"state_{frag - 1}"]) + if state >= 14: + return ( + int( + rand_time(self.time, self.beats, self.frag_seed(frag) + 100.0) + * 14.0 + ) + + 1 + ) + return state + 1 - def seed(self, n: int) -> int: - return int(self.__data[f"seed_{n}"]) + def frag_seed(self, frag: int) -> int: + return int(self.__data[f"seed_{frag - 1}"]) + + @property + def frag_selected(self) -> int: + return int(self.__data["selected"] + 1) + + def group_active(self, group: int) -> int: + return int(self.__data[f"active_{group - 1}"] + 1) + + def group_value(self, group: int, layer: int, n: int) -> tuple[float, float, float]: + return self.__value( + GROUPS_DELTA[group - 1] + GROUPS_SIZE[group - 1] * (layer - 1) + n + ) + + @property + def demo(self) -> bool: + return self.__data["demo"] > 0 + + @property + def auto_random(self) -> bool: + return self.__data["auto_random"] > 0 + + @property + def auto_random_cycle(self) -> int: + return int(self.__data["auto_random_cycle"]) @property def time(self) -> float: @@ -105,38 +150,80 @@ class ForgeData: def page(self) -> int: return int(self.__data["page"]) - @property - def selected(self) -> int: - return int(self.__data["selected"]) - @property def fxa_value(self) -> float: return magic( - self.time, self.beats, self.value(18), (1.0, 0.0, 0.0), self.seed(2) + self.time, + self.beats, + self.group_value(FX_GROUP, 1, 6), + (1.0, 0.0, 0.0), + self.frag_seed(FXA_FRAG), + ) + + @property + def fxa_inverted(self) -> bool: + return magic_trigger( + self.time, + self.beats, + (self.group_value(FX_GROUP, 1, 6)[2], 0.0, 0.0), + self.frag_seed(FXA_FRAG), ) @property def fxb_value(self) -> float: return magic( - self.time, self.beats, self.value(25), (1.0, 0.0, 0.0), self.seed(3) + self.time, + self.beats, + self.group_value(FX_GROUP, 2, 6), + (1.0, 0.0, 0.0), + self.frag_seed(FXB_FRAG), + ) + + @property + def fxb_inverted(self) -> bool: + return magic_trigger( + self.time, + self.beats, + (self.group_value(FX_GROUP, 2, 6)[2], 0.0, 0.0), + self.frag_seed(FXB_FRAG), ) @property def mix_value(self) -> float: return magic( - self.time, self.beats, self.value(34), (1.0, 0.0, 0.0), self.seed(4) + self.time, + self.beats, + self.group_value(EXTRA_GROUP, 1, 1), + (1.0, 0.0, 0.0), + self.frag_seed(MIX_FRAG), ) @property def mix_mode_key(self) -> bool: return magic_trigger( - self.time, self.beats, (self.value(33)[0], 0.0, 0.0), self.seed(4) + 10.0 + self.time, + self.beats, + (self.group_value(EXTRA_GROUP, 1, 0)[0], 0.0, 0.0), + self.frag_seed(MIX_FRAG) + 10.0, ) @property def mfx_value(self) -> float: return magic( - self.time, self.beats, self.value(32), (1.0, 0.0, 0.0), self.seed(5) + self.time, + self.beats, + self.group_value(FX_GROUP, 3, 6), + (1.0, 0.0, 0.0), + self.frag_seed(MFX_FRAG), + ) + + @property + def mfx_inverted(self) -> bool: + return magic_trigger( + self.time, + self.beats, + (self.group_value(FX_GROUP, 3, 6)[2], 0.0, 0.0), + self.frag_seed(MFX_FRAG), ) def mock(self) -> typing.Self: