diff --git a/ovos_utils/enclosure/__init__.py b/ovos_utils/enclosure/__init__.py index efb4ef4b..bbebc37f 100644 --- a/ovos_utils/enclosure/__init__.py +++ b/ovos_utils/enclosure/__init__.py @@ -5,6 +5,8 @@ from typing import Optional from ovos_utils.log import LOG, deprecated +LOG.warning("ovos_utils.enclosure has been deprecated! this module will be removed in version 0.1.0") + class MycroftEnclosures(str, Enum): # TODO: Deprecate in 0.1.0 diff --git a/ovos_utils/enclosure/mark1/__init__.py b/ovos_utils/enclosure/mark1/__init__.py index 62b3992d..5e4766ac 100644 --- a/ovos_utils/enclosure/mark1/__init__.py +++ b/ovos_utils/enclosure/mark1/__init__.py @@ -1,4 +1,8 @@ from ovos_utils.enclosure.api import EnclosureAPI +from ovos_utils.log import LOG + +LOG.warning("ovos_utils.enclosure.mark1 moved to https://github.com/OpenVoiceOS/ovos-mark1-utils ;" + " this module will be removed in version 0.1.0") class Mark1EnclosureAPI(EnclosureAPI): diff --git a/ovos_utils/enclosure/mark1/eyes/__init__.py b/ovos_utils/enclosure/mark1/eyes/__init__.py index 56fb3c73..48743c48 100644 --- a/ovos_utils/enclosure/mark1/eyes/__init__.py +++ b/ovos_utils/enclosure/mark1/eyes/__init__.py @@ -1,512 +1,513 @@ -from ovos_utils.enclosure.mark1 import Mark1EnclosureAPI -from ovos_utils.messagebus import get_mycroft_bus -from ovos_utils.colors import Color -from ovos_utils import rotate_list -from time import sleep - - -class EyePixel: - def __init__(self, index, api, color=None): - self.index = index - self.api = api - self.color = color or Color() - - @property - def rgb(self): - return self.color.rgb255 - - def sync_color(self): - r, g, b = self.api.get_eyes_pixel_color(self.index) - color = Color.from_rgb(r, g, b) - self.change_color(color) - - def update_color(self): - self.change_color(self.color) - - def change_color(self, name): - if isinstance(name, str): - self.color = Color.from_name(name) - elif isinstance(name, Color): - self.color = name - else: - raise ValueError("not a Color object") - r, g, b = self.rgb - self.api.eyes_setpixel(self.index, r, g, b) - - def set_saturation(self, value): - self.color.set_saturation(value) - self.update_color() - - def set_luminance(self, value): - self.color.set_luminance(value) - self.update_color() - - def set_hue(self, value): - self.color.set_hue(value) - self.update_color() - - def __repr__(self): - return "Pixel_" + str(self.index) + ":" + self.color.color_description - - -class Eye(list): - def __init__(self, pixel_range, bus=None, color=None): - super().__init__() - self.bus = bus or get_mycroft_bus() - self.api = Mark1EnclosureAPI(self.bus) - for idx in range(pixel_range[0], pixel_range[1]): - pixel = EyePixel(idx, self.api) - self.append(pixel) - self.color = Color() - if color: +from ovos_utils.log import LOG + +LOG.warning("ovos_utils.enclosure.mark1.faceplate moved to https://github.com/OpenVoiceOS/ovos-mark1-utils ;" + " this module will be removed in version 0.1.0") + +try: + from ovos_mark1.eyes import * +except: + from ovos_utils.enclosure.mark1 import Mark1EnclosureAPI + from ovos_utils.messagebus import get_mycroft_bus + from ovos_utils.colors import Color + from ovos_utils import rotate_list + from time import sleep + + + class EyePixel: + def __init__(self, index, api, color=None): + self.index = index + self.api = api + self.color = color or Color() + + @property + def rgb(self): + return self.color.rgb255 + + def sync_color(self): + r, g, b = self.api.get_eyes_pixel_color(self.index) + color = Color.from_rgb(r, g, b) self.change_color(color) - else: - self.sync_color() - def sync_color(self): - for p in self: - p.sync_color() - sleep(0.05) - - def update_color(self): - for p in self: - p.update_color() - sleep(0.05) + def update_color(self): + self.change_color(self.color) + + def change_color(self, name): + if isinstance(name, str): + self.color = Color.from_name(name) + elif isinstance(name, Color): + self.color = name + else: + raise ValueError("not a Color object") + r, g, b = self.rgb + self.api.eyes_setpixel(self.index, r, g, b) + + def set_saturation(self, value): + self.color.set_saturation(value) + self.update_color() + + def set_luminance(self, value): + self.color.set_luminance(value) + self.update_color() + + def set_hue(self, value): + self.color.set_hue(value) + self.update_color() + + def __repr__(self): + return "Pixel_" + str(self.index) + ":" + self.color.color_description + + + class Eye(list): + def __init__(self, pixel_range, bus=None, color=None): + super().__init__() + self.bus = bus or get_mycroft_bus() + self.api = Mark1EnclosureAPI(self.bus) + for idx in range(pixel_range[0], pixel_range[1]): + pixel = EyePixel(idx, self.api) + self.append(pixel) + self.color = Color() + if color: + self.change_color(color) + else: + self.sync_color() + + def sync_color(self): + for p in self: + p.sync_color() + sleep(0.05) + + def update_color(self): + for p in self: + p.update_color() + sleep(0.05) + + def change_color(self, name): + if isinstance(name, str): + self.color = Color.from_name(name) + elif isinstance(name, Color): + self.color = name + else: + raise ValueError("not a Color object") + for led in self: + led.change_color(self.color) + # writer bugs out if messages sent too fast + sleep(0.05) + + def saturation_spin(self, speed=0.05): + values = [] + for idx, pixel in enumerate(self): + sat = 0.09 * idx + pixel.set_saturation(sat) + values.append(sat) + sleep(speed) - def change_color(self, name): - if isinstance(name, str): - self.color = Color.from_name(name) - elif isinstance(name, Color): - self.color = name - else: - raise ValueError("not a Color object") - for led in self: - led.change_color(self.color) - # writer bugs out if messages sent too fast - sleep(0.05) + while True: + values = rotate_list(values, -1) + for idx, value in enumerate(values): + self[idx].set_saturation(value) + sleep(speed) - def saturation_spin(self, speed=0.05): - values = [] - for idx, pixel in enumerate(self): - sat = 0.09 * idx - pixel.set_saturation(sat) - values.append(sat) - sleep(speed) - - while True: - values = rotate_list(values, -1) - for idx, value in enumerate(values): - self[idx].set_saturation(value) + def luminance_spin(self, speed=0.05): + values = [] + for idx, pixel in enumerate(self): + sat = 0.05 * idx + pixel.set_luminance(sat) + values.append(sat) sleep(speed) - def luminance_spin(self, speed=0.05): - values = [] - for idx, pixel in enumerate(self): - sat = 0.05 * idx - pixel.set_luminance(sat) - values.append(sat) - sleep(speed) - - while True: - values = rotate_list(values, -1) - for idx, value in enumerate(values): - self[idx].set_luminance(value) + while True: + values = rotate_list(values, -1) + for idx, value in enumerate(values): + self[idx].set_luminance(value) + sleep(speed) + + def hue_spin(self, speed=0.05): + values = [] + for idx, pixel in enumerate(self): + sat = 0.083 * idx + pixel.set_hue(sat) + values.append(sat) sleep(speed) - def hue_spin(self, speed=0.05): - values = [] - for idx, pixel in enumerate(self): - sat = 0.083 * idx - pixel.set_hue(sat) - values.append(sat) - sleep(speed) - - while True: - values = rotate_list(values, -1) - for idx, value in enumerate(values): - self[idx].set_hue(value) + while True: + values = rotate_list(values, -1) + for idx, value in enumerate(values): + self[idx].set_hue(value) + sleep(speed) + + def set_hue(self, hue): + for pixel in self: + pixel.color.set_hue(hue) + pixel.update_color() + + def set_luminance(self, value): + for pixel in self: + pixel.color.set_luminance(value) + self.update_color() + + def set_saturation(self, value): + for pixel in self: + pixel.color.set_saturation(value) + self.update_color() + + def on(self): + self.set_luminance(1) + + def off(self): + self.set_luminance(0) + + def blink_once(self): + """ + Make the eye blink + """ + raise NotImplementedError + + def blink(self, speed=0.5): + """ + Make the right eye blink in a loop + """ + while True: + self.blink_once() sleep(speed) - def set_hue(self, hue): - for pixel in self: - pixel.color.set_hue(hue) - pixel.update_color() - - def set_luminance(self, value): - for pixel in self: - pixel.color.set_luminance(value) - self.update_color() - - def set_saturation(self, value): - for pixel in self: - pixel.color.set_saturation(value) - self.update_color() - - def on(self): - self.set_luminance(1) - - def off(self): - self.set_luminance(0) - - def blink_once(self): - """ - Make the eye blink - """ - raise NotImplementedError - - def blink(self, speed=0.5): - """ - Make the right eye blink in a loop - """ - while True: - self.blink_once() - sleep(speed) - - -class RightEye(Eye): - def __init__(self, bus, color=None): - super().__init__(bus=bus, pixel_range=(12, 24), color=color) - - def sync_color(self): - pixels = self.api.get_eyes_color()[12:] - for idx, (r, g, b) in enumerate(pixels): - self[idx].color = Color.from_rgb(r, g, b) - self.update_color() - - def blink_once(self): - """ - Make the right eye blink - """ - self.api.eyes_blink("r") - - -class LeftEye(Eye): - def __init__(self, bus, color=None): - super().__init__(bus=bus, pixel_range=(0, 12), color=color) - - def sync_color(self): - pixels = self.api.get_eyes_color()[:12] - for idx, (r, g, b) in enumerate(pixels): - self[idx].color = Color.from_rgb(r, g, b) - self.update_color() - - def blink_once(self): - """ - Make the left eye blink - """ - self.api.eyes_blink("l") - - -class Eyes(list): - def __init__(self, bus=None, color=None): - super().__init__() - self.bus = bus or get_mycroft_bus() - self.api = Mark1EnclosureAPI(self.bus) - self.right = RightEye(self.bus) - self.left = LeftEye(self.bus) - self.color = Color() - if color: - self.change_color(color) - else: - self.sync_color() - - def __getitem__(self, item): - assert isinstance(item, int) - assert 0 <= item <= 23 - if item < 12: - return self.left[item] - return self.right[item - 12] - - def __setitem__(self, key, value): - assert isinstance(key, int) - assert 0 <= key <= 23 - if key < 12: - self.left[key] = value - self.right[key] = value - - def __iter__(self): - for i in range(len(self)): - yield self[i] - - def __len__(self): - return len(self.left) + len(self.right) - - def sync_color(self): - """ updates internal color value to current color """ - pixels = self.api.get_eyes_color() - for idx, (r, g, b) in enumerate(pixels): - self[idx].color = Color.from_rgb(r, g, b) - - def update_color(self): - """ updates arduino color to current pixels """ - for i in range(len(self) // 2): - self.left[i].update_color() - sleep(0.05) - self.right[i].update_color() - sleep(0.05) - def change_color(self, name): - """ changes color of both eyes """ - if isinstance(name, str): - self.color = Color.from_name(name) - elif isinstance(name, Color): - self.color = name - else: - raise ValueError("not a Color object") - r, g, b = self.color.rgb255 - self.api.eyes_color(r, g, b) - for idx in range(len(self)): - self[idx].color = self.color - - # animations - def saturation_spin(self, speed=0.05): - values = [] - for idx in range(len(self) // 2): - sat = 0.09 * idx - values.append(sat) - self.left[idx].set_saturation(sat) - sleep(0.03) - self.right[idx].set_saturation(sat) - - while True: - values = rotate_list(values, -1) - for idx, value in enumerate(values): - self.left[idx].set_saturation(value) + class RightEye(Eye): + def __init__(self, bus, color=None): + super().__init__(bus=bus, pixel_range=(12, 24), color=color) + + def sync_color(self): + pixels = self.api.get_eyes_color()[12:] + for idx, (r, g, b) in enumerate(pixels): + self[idx].color = Color.from_rgb(r, g, b) + self.update_color() + + def blink_once(self): + """ + Make the right eye blink + """ + self.api.eyes_blink("r") + + + class LeftEye(Eye): + def __init__(self, bus, color=None): + super().__init__(bus=bus, pixel_range=(0, 12), color=color) + + def sync_color(self): + pixels = self.api.get_eyes_color()[:12] + for idx, (r, g, b) in enumerate(pixels): + self[idx].color = Color.from_rgb(r, g, b) + self.update_color() + + def blink_once(self): + """ + Make the left eye blink + """ + self.api.eyes_blink("l") + + + class Eyes(list): + def __init__(self, bus=None, color=None): + super().__init__() + self.bus = bus or get_mycroft_bus() + self.api = Mark1EnclosureAPI(self.bus) + self.right = RightEye(self.bus) + self.left = LeftEye(self.bus) + self.color = Color() + if color: + self.change_color(color) + else: + self.sync_color() + + def __getitem__(self, item): + assert isinstance(item, int) + assert 0 <= item <= 23 + if item < 12: + return self.left[item] + return self.right[item - 12] + + def __setitem__(self, key, value): + assert isinstance(key, int) + assert 0 <= key <= 23 + if key < 12: + self.left[key] = value + self.right[key] = value + + def __iter__(self): + for i in range(len(self)): + yield self[i] + + def __len__(self): + return len(self.left) + len(self.right) + + def sync_color(self): + """ updates internal color value to current color """ + pixels = self.api.get_eyes_color() + for idx, (r, g, b) in enumerate(pixels): + self[idx].color = Color.from_rgb(r, g, b) + + def update_color(self): + """ updates arduino color to current pixels """ + for i in range(len(self) // 2): + self.left[i].update_color() + sleep(0.05) + self.right[i].update_color() + sleep(0.05) + + def change_color(self, name): + """ changes color of both eyes """ + if isinstance(name, str): + self.color = Color.from_name(name) + elif isinstance(name, Color): + self.color = name + else: + raise ValueError("not a Color object") + r, g, b = self.color.rgb255 + self.api.eyes_color(r, g, b) + for idx in range(len(self)): + self[idx].color = self.color + + # animations + def saturation_spin(self, speed=0.05): + values = [] + for idx in range(len(self) // 2): + sat = 0.09 * idx + values.append(sat) + self.left[idx].set_saturation(sat) sleep(0.03) - self.right[idx].set_saturation(value) - sleep(speed) + self.right[idx].set_saturation(sat) + + while True: + values = rotate_list(values, -1) + for idx, value in enumerate(values): + self.left[idx].set_saturation(value) + sleep(0.03) + self.right[idx].set_saturation(value) + sleep(speed) - def luminance_spin(self, speed=0.05): - values = [] - for idx in range(len(self) // 2): - sat = 0.05 * idx - values.append(sat) - self.left[idx].set_luminance(sat) - sleep(0.03) - self.right[idx].set_luminance(sat) - - while True: - values = rotate_list(values, -1) - for idx, value in enumerate(values): - self.left[idx].set_luminance(value) + def luminance_spin(self, speed=0.05): + values = [] + for idx in range(len(self) // 2): + sat = 0.05 * idx + values.append(sat) + self.left[idx].set_luminance(sat) sleep(0.03) - self.right[idx].set_luminance(value) - sleep(speed) + self.right[idx].set_luminance(sat) + + while True: + values = rotate_list(values, -1) + for idx, value in enumerate(values): + self.left[idx].set_luminance(value) + sleep(0.03) + self.right[idx].set_luminance(value) + sleep(speed) - def hue_spin(self, speed=0.05): - values = [] - for idx in range(len(self) // 2): - sat = 0.083 * idx - values.append(sat) - self.left[idx].set_hue(sat) - sleep(0.03) - self.right[idx].set_hue(sat) - - while True: - values = rotate_list(values, -1) - for idx, value in enumerate(values): - for pixel in self: - print(pixel) - self.left[idx].set_hue(value) + def hue_spin(self, speed=0.05): + values = [] + for idx in range(len(self) // 2): + sat = 0.083 * idx + values.append(sat) + self.left[idx].set_hue(sat) sleep(0.03) - self.right[idx].set_hue(value) - sleep(speed) + self.right[idx].set_hue(sat) + + while True: + values = rotate_list(values, -1) + for idx, value in enumerate(values): + for pixel in self: + print(pixel) + self.left[idx].set_hue(value) + sleep(0.03) + self.right[idx].set_hue(value) + sleep(speed) - def flash(self, speed=0.2): - while True: - sleep(speed) - self.on() - sleep(speed) - self.off() - - def rainbow_flash(self, speed=0.2): - colors = ["red", "orange", "yellow", "green", "cyan", "blue", - "violet", "purple"] - while True: - for color in colors: - sleep(speed) - self.off() - self.change_color(color) + def flash(self, speed=0.2): + while True: sleep(speed) self.on() - - def beacon(self, speed=0.1): - values = [i + i for i in range(30)] - while True: - for value in values: - self.set_brightness(value) sleep(speed) - values.reverse() - - def rainbow_beacon(self, speed=0.1): - values = [i + i for i in range(30)] - values += reversed(values) - colors = ["red", "orange", "yellow", "green", "cyan", "blue", - "violet", "purple"] - self.set_brightness(0) - while True: - for color in colors: - for value in values: + self.off() + + def rainbow_flash(self, speed=0.2): + colors = ["red", "orange", "yellow", "green", "cyan", "blue", + "violet", "purple"] + while True: + for color in colors: sleep(speed) + self.off() + self.change_color(color) + sleep(speed) + self.on() + + def beacon(self, speed=0.1): + values = [i + i for i in range(30)] + while True: + for value in values: self.set_brightness(value) - self.change_color(color) + sleep(speed) + values.reverse() + + def rainbow_beacon(self, speed=0.1): + values = [i + i for i in range(30)] + values += reversed(values) + colors = ["red", "orange", "yellow", "green", "cyan", "blue", + "violet", "purple"] + self.set_brightness(0) + while True: + for color in colors: + for value in values: + sleep(speed) + self.set_brightness(value) + self.change_color(color) + + # Arduino API + def set_hue(self, hue): + self.right.set_hue(hue) + sleep(0.05) + self.left.set_hue(hue) + + def set_brightness(self, level): + """ + Set the brightness of the eyes in the display. + Args: + level (int): 1-30, bigger numbers being brighter + """ + self.api.eyes_brightness(level) + + def spin(self): + self.api.eyes_spin() + + def timed_spin(self, length): + self.api.eyes_timed_spin(length) + + def reset(self): + self.api.eyes_reset() + + def fill_once(self, percent): + """ + Use the eyes as a type of progress meter + Args: + percent (int): 0-49 fills the right eye, 50-100 also covers left + """ + self.api.eyes_fill(percent) + + def look(self, side): + """Make the eyes look to the given side + Args: + side (str): 'r' for right + 'l' for left + 'u' for up + 'd' for down + 'c' for crossed + """ + self.api.eyes_look(side) + + def look_right(self): + self.look("r") + + def look_left(self): + self.look("l") + + def look_up(self): + self.look("u") + + def look_down(self): + self.look("d") + + def cross(self): + self.look("c") + + def narrow(self): + """Make the eyes look narrow, like a squint""" + self.api.eyes_narrow() + + def on(self): + """Illuminate or show the eyes.""" + self.api.eyes_on() + + def off(self): + """Turn off or hide the eyes.""" + self.api.eyes_off() + + def blink_once(self, side="b"): + """Make the eyes blink + Args: + side (str): 'r', 'l', or 'b' for 'right', 'left' or 'both' + """ + self.api.eyes_blink(side) + + def blink_right_once(self): + self.right.blink_once() + + def blink_left_once(self): + self.left.blink_once() + + def blink(self, speed=0.5): + """ + Make the eyes blink in a loop + """ + while True: + self.blink_once() + sleep(speed) + + def blink_right(self, speed=0.5): + """ + Make the right eye blink in a loop + """ + self.right.blink(speed) + + def blink_left(self, speed=0.5): + """ + Make the left eyes blink in a loop + """ + self.left.blink(speed) + + def blink_alternate(self, speed=0.5): + """ + Make the eyes blink in a loop + """ + while True: + self.blink_right_once() + sleep(speed) + self.blink_left_once() + sleep(speed) - # Arduino API - def set_hue(self, hue): - self.right.set_hue(hue) - sleep(0.05) - self.left.set_hue(hue) - - def set_brightness(self, level): - """ - Set the brightness of the eyes in the display. - Args: - level (int): 1-30, bigger numbers being brighter - """ - self.api.eyes_brightness(level) - - def spin(self): - self.api.eyes_spin() - - def timed_spin(self, length): - self.api.eyes_timed_spin(length) - - def reset(self): - self.api.eyes_reset() - - def fill_once(self, percent): - """ - Use the eyes as a type of progress meter - Args: - percent (int): 0-49 fills the right eye, 50-100 also covers left - """ - self.api.eyes_fill(percent) - - def look(self, side): - """Make the eyes look to the given side - Args: - side (str): 'r' for right - 'l' for left - 'u' for up - 'd' for down - 'c' for crossed - """ - self.api.eyes_look(side) - - def look_right(self): - self.look("r") - - def look_left(self): - self.look("l") - - def look_up(self): - self.look("u") - - def look_down(self): - self.look("d") - - def cross(self): - self.look("c") - - def narrow(self): - """Make the eyes look narrow, like a squint""" - self.api.eyes_narrow() - - def on(self): - """Illuminate or show the eyes.""" - self.api.eyes_on() - - def off(self): - """Turn off or hide the eyes.""" - self.api.eyes_off() - - def blink_once(self, side="b"): - """Make the eyes blink - Args: - side (str): 'r', 'l', or 'b' for 'right', 'left' or 'both' - """ - self.api.eyes_blink(side) - - def blink_right_once(self): - self.right.blink_once() - - def blink_left_once(self): - self.left.blink_once() - - def blink(self, speed=0.5): - """ - Make the eyes blink in a loop - """ - while True: - self.blink_once() - sleep(speed) - - def blink_right(self, speed=0.5): - """ - Make the right eye blink in a loop - """ - self.right.blink(speed) - - def blink_left(self, speed=0.5): - """ - Make the left eyes blink in a loop - """ - self.left.blink(speed) - - def blink_alternate(self, speed=0.5): - """ - Make the eyes blink in a loop - """ - while True: - self.blink_right_once() - sleep(speed) - self.blink_left_once() - sleep(speed) - - def up_down(self, speed=0.8): - """ - Make the eyes blink in a loop - """ - while True: - self.look_up() - sleep(speed) - self.look_down() - sleep(speed) - - def left_right(self, speed=0.8): - """ - Make the eyes blink in a loop - """ - while True: - self.look_left() - sleep(speed) - self.look_right() - sleep(speed) - - def fill(self, speed=0.1): - values = [i for i in range(101)] - values += reversed(values) - while True: - for percent in values: - self.fill_once(percent) + def up_down(self, speed=0.8): + """ + Make the eyes blink in a loop + """ + while True: + self.look_up() + sleep(speed) + self.look_down() sleep(speed) - def rainbow_fill(self, speed=0.1): - values = [i for i in range(101)] - values += reversed(values) - colors = ["red", "orange", "yellow", "green", "cyan", "blue", - "violet", "purple"] - while True: - for color in colors: + def left_right(self, speed=0.8): + """ + Make the eyes blink in a loop + """ + while True: + self.look_left() + sleep(speed) + self.look_right() + sleep(speed) + + def fill(self, speed=0.1): + values = [i for i in range(101)] + values += reversed(values) + while True: for percent in values: self.fill_once(percent) sleep(speed) - if percent == 100: - self.change_color(color) - - -if __name__ == "__main__": - bus = get_mycroft_bus("192.168.1.70") - eyes = Eyes(bus) - eyes.hue_spin() + def rainbow_fill(self, speed=0.1): + values = [i for i in range(101)] + values += reversed(values) + colors = ["red", "orange", "yellow", "green", "cyan", "blue", + "violet", "purple"] + while True: + for color in colors: + for percent in values: + self.fill_once(percent) + sleep(speed) + if percent == 100: + self.change_color(color) diff --git a/ovos_utils/enclosure/mark1/faceplate/__init__.py b/ovos_utils/enclosure/mark1/faceplate/__init__.py index 308f7d08..64ebcce4 100644 --- a/ovos_utils/enclosure/mark1/faceplate/__init__.py +++ b/ovos_utils/enclosure/mark1/faceplate/__init__.py @@ -7,398 +7,405 @@ from collections.abc import MutableSequence import copy - -class FaceplateGrid(MutableSequence): - encoded = None - str_grid = None - pad_char = "." - - def __init__(self, grid=None, bus=None): - self.bus = bus or get_mycroft_bus() - self._api = Mark1EnclosureAPI(self.bus) - self.grid = [] - for x in range(8): - self.grid.append([]) - for y in range(32): - self.grid[x].append(0) - if self.encoded: - self.grid = self.decode(self.encoded).grid - elif self.str_grid is not None: - self.grid = FaceplateGrid(bus=self.bus)\ - .from_string(self.str_grid).grid - elif grid is not None: - self.grid = grid - - @property - def height(self): - return len(self.grid) - - @property - def width(self): - return max([len(r) for r in self.grid]) - - def display(self, invert=True, clear=True, x_offset=0, y_offset=0): - self._api.mouth_display(self.encode(invert), - x_offset, y_offset, clear) - - def print(self, draw_padding=True, invert=False): - print(self.to_string(draw_padding=draw_padding, invert=invert)) - - def encode(self, invert=False): - # to understand how this function works you need to understand how the - # Mark I arduino proprietary encoding works to display to the faceplate - - # https://mycroft-ai.gitbook.io/docs/skill-development/displaying-information/mark-1-display - - # Each char value str_gridesents a width number starting with B=1 - # then increment 1 for the next. ie C=2 - width_codes = ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', - 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a'] - - height_codes = ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'] - - encode = width_codes[self.width - 1] - encode += height_codes[self.height - 1] - - # Turn the image pixels into binary values 1's and 0's - # the Mark I face plate encoding uses binary values to - # binary_values returns a list of 1's and 0s'. ie ['1', '1', '0', ...] - binary_values = [] - for i in range(self.width): # pixels - for j in range(self.height): # lines - pixels = self.grid[j] - - if pixels[i] is None: # padding - pixels[i] = 0 - - if pixels[i] != 0: - if invert is False: - binary_values.append('1') - else: - binary_values.append('0') - else: - if invert is False: - binary_values.append('0') +LOG.warning("ovos_utils.enclosure.mark1.faceplate moved to https://github.com/OpenVoiceOS/ovos-mark1-utils ;" + " this module will be removed in version 0.1.0") + + +try: + from ovos_mark1.faceplate import * +except ImportError: + + class FaceplateGrid(MutableSequence): + encoded = None + str_grid = None + pad_char = "." + + def __init__(self, grid=None, bus=None): + self.bus = bus or get_mycroft_bus() + self._api = Mark1EnclosureAPI(self.bus) + self.grid = [] + for x in range(8): + self.grid.append([]) + for y in range(32): + self.grid[x].append(0) + if self.encoded: + self.grid = self.decode(self.encoded).grid + elif self.str_grid is not None: + self.grid = FaceplateGrid(bus=self.bus)\ + .from_string(self.str_grid).grid + elif grid is not None: + self.grid = grid + + @property + def height(self): + return len(self.grid) + + @property + def width(self): + return max([len(r) for r in self.grid]) + + def display(self, invert=True, clear=True, x_offset=0, y_offset=0): + self._api.mouth_display(self.encode(invert), + x_offset, y_offset, clear) + + def print(self, draw_padding=True, invert=False): + print(self.to_string(draw_padding=draw_padding, invert=invert)) + + def encode(self, invert=False): + # to understand how this function works you need to understand how the + # Mark I arduino proprietary encoding works to display to the faceplate + + # https://mycroft-ai.gitbook.io/docs/skill-development/displaying-information/mark-1-display + + # Each char value str_gridesents a width number starting with B=1 + # then increment 1 for the next. ie C=2 + width_codes = ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a'] + + height_codes = ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'] + + encode = width_codes[self.width - 1] + encode += height_codes[self.height - 1] + + # Turn the image pixels into binary values 1's and 0's + # the Mark I face plate encoding uses binary values to + # binary_values returns a list of 1's and 0s'. ie ['1', '1', '0', ...] + binary_values = [] + for i in range(self.width): # pixels + for j in range(self.height): # lines + pixels = self.grid[j] + + if pixels[i] is None: # padding + pixels[i] = 0 + + if pixels[i] != 0: + if invert is False: + binary_values.append('1') + else: + binary_values.append('0') else: - binary_values.append('1') - # these values are used to determine how binary values - # needs to be grouped together - number_of_bottom_pixel = 0 - - if self.height > 4: - number_of_top_pixel = 4 - number_of_bottom_pixel = self.height - 4 - else: - number_of_top_pixel = self.height - - # this loop will group together the individual binary values - # ie. binary_list = ['1111', '001', '0101', '100'] - binary_list = [] - binary_code = '' - increment = 0 - alternate = False - for val in binary_values: - binary_code += val - increment += 1 - if increment == number_of_top_pixel and alternate is False: + if invert is False: + binary_values.append('0') + else: + binary_values.append('1') + # these values are used to determine how binary values + # needs to be grouped together + number_of_bottom_pixel = 0 + + if self.height > 4: + number_of_top_pixel = 4 + number_of_bottom_pixel = self.height - 4 + else: + number_of_top_pixel = self.height + + # this loop will group together the individual binary values + # ie. binary_list = ['1111', '001', '0101', '100'] + binary_list = [] + binary_code = '' + increment = 0 + alternate = False + for val in binary_values: + binary_code += val + increment += 1 + if increment == number_of_top_pixel and alternate is False: + # binary code is reversed for encoding + binary_list.append(binary_code[::-1]) + increment = 0 + binary_code = '' + alternate = True + elif increment == number_of_bottom_pixel and alternate is True: + binary_list.append(binary_code[::-1]) + increment = 0 + binary_code = '' + alternate = False + # Code to let the Mark I arduino know where to place the + # pixels on the faceplate + pixel_codes = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'] + for binary_values in binary_list: + number = int(binary_values, 2) + pixel_code = pixel_codes[number] + encode += pixel_code + return encode + + def decode(self, encoded, invert=False, pad=True): + codes = list(encoded) + + # Each char value str_gridesents a width number starting with B=1 + # then increment 1 for the next. ie C=2 + width_codes = ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a'] + + height_codes = ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'] + + height = height_codes.index(codes[1]) + 1 + width = width_codes.index(codes[0]) + 1 + + # Code to let the Mark I arduino know where to place the + # pixels on the faceplate + pixel_codes = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'] + codes.reverse() + binary_list = [] + for pixel_code in codes[:-2]: + number = pixel_codes.index(pixel_code.upper()) + bin_str = str(bin(number))[2:] + while not len(bin_str) == 4: + bin_str = "0" + bin_str + binary_list += [bin_str] + + binary_list.reverse() + + for idx, binary_code in enumerate(binary_list): # binary code is reversed for encoding - binary_list.append(binary_code[::-1]) - increment = 0 - binary_code = '' - alternate = True - elif increment == number_of_bottom_pixel and alternate is True: - binary_list.append(binary_code[::-1]) - increment = 0 - binary_code = '' - alternate = False - # Code to let the Mark I arduino know where to place the - # pixels on the faceplate - pixel_codes = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'] - for binary_values in binary_list: - number = int(binary_values, 2) - pixel_code = pixel_codes[number] - encode += pixel_code - return encode - - def decode(self, encoded, invert=False, pad=True): - codes = list(encoded) - - # Each char value str_gridesents a width number starting with B=1 - # then increment 1 for the next. ie C=2 - width_codes = ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', - 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a'] - - height_codes = ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'] - - height = height_codes.index(codes[1]) + 1 - width = width_codes.index(codes[0]) + 1 - - # Code to let the Mark I arduino know where to place the - # pixels on the faceplate - pixel_codes = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'] - codes.reverse() - binary_list = [] - for pixel_code in codes[:-2]: - number = pixel_codes.index(pixel_code.upper()) - bin_str = str(bin(number))[2:] - while not len(bin_str) == 4: - bin_str = "0" + bin_str - binary_list += [bin_str] - - binary_list.reverse() - - for idx, binary_code in enumerate(binary_list): - # binary code is reversed for encoding - binary_list[idx] = binary_code[::-1] - - binary_code = "".join(binary_list) - - # Turn the image pixels into binary values 1's and 0's - # the Mark I face plate encoding uses binary values to - # binary_values returns a list of 1's and 0s'. ie ['1', '1', '0', ...] - grid = [] - # binary_code is a sequence of column by column - cols = [list(binary_code)[x:x + height] for x in - range(0, len(list(binary_code)), height)] - - for x in range(height): - row = [] - for y in range(width): - bit = int(cols[y][x]) - if invert: - if bit: - bit = 0 - else: - bit = 1 - row.append(bit) - grid.append(row) - - # handle padding - if pad: - if width < self.width: - n = int((self.width - width) / 2) - if invert: - padding = [1] * n - else: - padding = [0] * n - for idx, row in enumerate(grid): - grid[idx] = padding + row + padding - if height < self.height: - pass # TODO vertical padding - self.grid = grid - return self - - def from_string(self, str_grid): - rows = [r for r in str_grid.split("\n") if len(r)] - grid = [] - for r in rows: - row = [] - for char in list(r): - if char == " ": - row.append(1) - elif char == FaceplateGrid.pad_char: - row.append(None) - else: - row.append(0) - while len(row) < self.width: - row.append(None) - grid.append(row) - self.grid = grid - return self - - def to_string(self, draw_padding=False, invert=False): - str_grid = "" - for row in self.grid: - line = "" - for col in row: - if col is None and draw_padding: - line += self.pad_char - elif col == 1: + binary_list[idx] = binary_code[::-1] + + binary_code = "".join(binary_list) + + # Turn the image pixels into binary values 1's and 0's + # the Mark I face plate encoding uses binary values to + # binary_values returns a list of 1's and 0s'. ie ['1', '1', '0', ...] + grid = [] + # binary_code is a sequence of column by column + cols = [list(binary_code)[x:x + height] for x in + range(0, len(list(binary_code)), height)] + + for x in range(height): + row = [] + for y in range(width): + bit = int(cols[y][x]) if invert: - line += "X" - else: - line += " " - elif col == 0: + if bit: + bit = 0 + else: + bit = 1 + row.append(bit) + grid.append(row) + + # handle padding + if pad: + if width < self.width: + n = int((self.width - width) / 2) if invert: - line += " " + padding = [1] * n + else: + padding = [0] * n + for idx, row in enumerate(grid): + grid[idx] = padding + row + padding + if height < self.height: + pass # TODO vertical padding + self.grid = grid + return self + + def from_string(self, str_grid): + rows = [r for r in str_grid.split("\n") if len(r)] + grid = [] + for r in rows: + row = [] + for char in list(r): + if char == " ": + row.append(1) + elif char == FaceplateGrid.pad_char: + row.append(None) else: - line += "X" - str_grid += line + "\n" - return str_grid - - def invert(self): - for x in range(self.height): - for y in range(self.width): - if self.grid[x][y] == 0: - self.grid[x][y] = 1 - elif self.grid[x][y] == 1: + row.append(0) + while len(row) < self.width: + row.append(None) + grid.append(row) + self.grid = grid + return self + + def to_string(self, draw_padding=False, invert=False): + str_grid = "" + for row in self.grid: + line = "" + for col in row: + if col is None and draw_padding: + line += self.pad_char + elif col == 1: + if invert: + line += "X" + else: + line += " " + elif col == 0: + if invert: + line += " " + else: + line += "X" + str_grid += line + "\n" + return str_grid + + def invert(self): + for x in range(self.height): + for y in range(self.width): + if self.grid[x][y] == 0: + self.grid[x][y] = 1 + elif self.grid[x][y] == 1: + self.grid[x][y] = 0 + return self + + def clear(self): + for x in range(self.height): + for y in range(self.width): self.grid[x][y] = 0 - return self - - def clear(self): - for x in range(self.height): - for y in range(self.width): - self.grid[x][y] = 0 - return self - - @property - def is_empty(self): - for x in range(self.height): - for y in range(self.width): - if self.grid[x][y] == 1: - return False - return True - - def randomize(self, n=200): - for i in range(n): - x = random.randint(0, self.height-1) - y = random.randint(0, self.width-1) - self.grid[x][y] = int(random.randint(0, 1)) - return self - - def __len__(self): - # number of pixels - return self.width * self.height + return self - def __delitem__(self, index): - self.grid.__delitem__(index) + @property + def is_empty(self): + for x in range(self.height): + for y in range(self.width): + if self.grid[x][y] == 1: + return False + return True - def insert(self, index, value): - self.grid.insert(index - 1, value) + def randomize(self, n=200): + for i in range(n): + x = random.randint(0, self.height-1) + y = random.randint(0, self.width-1) + self.grid[x][y] = int(random.randint(0, 1)) + return self - def __setitem__(self, index, value): - self.grid.__setitem__(index, value) + def __len__(self): + # number of pixels + return self.width * self.height - def __getitem__(self, index): - return self.grid.__getitem__(index) + def __delitem__(self, index): + self.grid.__delitem__(index) + def insert(self, index, value): + self.grid.insert(index - 1, value) -class FacePlateAnimation(FaceplateGrid): + def __setitem__(self, index, value): + self.grid.__setitem__(index, value) - def __init__(self, grid=None, bus=None): - super().__init__(grid, bus) - self.finished = False + def __getitem__(self, index): + return self.grid.__getitem__(index) - def animate(self): - pass - def __iter__(self): - while not self.finished: - self.animate() - yield self + class FacePlateAnimation(FaceplateGrid): - def start(self): - self.finished = False + def __init__(self, grid=None, bus=None): + super().__init__(grid, bus) + self.finished = False - def stop(self): - self.finished = True + def animate(self): + pass - def run(self, delay=0.5, callback=None, daemonic=False): - self.start() - - if delay < 0.4: - # writer bugs out if sending messages too rapidly - delay = 0.4 - - def step(callback=callback): - try: - if not self.finished: - self.animate() - if callback: - callback(self) - except Exception as e: - LOG.error(e) - - if daemonic: - create_loop(step, delay) - else: + def __iter__(self): while not self.finished: - step() - sleep(delay) - self.stop() - - def scroll_down(self): - old = copy.deepcopy(self.grid) - for x in range(self.width): - for y in range(self.height): - self.grid[y][x] = old[y - 1][x] - - def scroll_up(self): - old = copy.deepcopy(self.grid) - for x in range(self.width): - for y in range(self.height): - if y == self.height - 1: - self.grid[y][x] = old[0][x] - else: - self.grid[y][x] = old[y + 1][x] - - def scroll_right(self): - old = copy.deepcopy(self.grid) - for x in range(self.width): - for y in range(self.height): - self.grid[y][x] = old[y][x - 1] - - def scroll_left(self): - old = copy.deepcopy(self.grid) - for x in range(self.width): - for y in range(self.height): - if x == self.width -1: - self.grid[y][x] = old[y][0] - else: - self.grid[y][x] = old[y][x + 1] - - def move_down(self): - old = copy.deepcopy(self.grid) - for x in range(self.width): - for y in range(self.height): - if y - 1 < 0: - self.grid[y][x] = 0 - else: + self.animate() + yield self + + def start(self): + self.finished = False + + def stop(self): + self.finished = True + + def run(self, delay=0.5, callback=None, daemonic=False): + self.start() + + if delay < 0.4: + # writer bugs out if sending messages too rapidly + delay = 0.4 + + def step(callback=callback): + try: + if not self.finished: + self.animate() + if callback: + callback(self) + except Exception as e: + LOG.error(e) + + if daemonic: + create_loop(step, delay) + else: + while not self.finished: + step() + sleep(delay) + self.stop() + + def scroll_down(self): + old = copy.deepcopy(self.grid) + for x in range(self.width): + for y in range(self.height): self.grid[y][x] = old[y - 1][x] - def move_up(self): - old = copy.deepcopy(self.grid) - for x in range(self.width): - for y in range(self.height): - if y == self.height - 1: - self.grid[y][x] = 0 - else: - self.grid[y][x] = old[y + 1][x] - - def move_right(self): - old = copy.deepcopy(self.grid) - for x in range(self.width): - for y in range(self.height): - self.grid[y][x] = old[y][x - 1] - - def move_left(self): - old = copy.deepcopy(self.grid) - for x in range(self.width): - for y in range(self.height): - if x == self.width - 1: - self.grid[y][x] = 0 - else: - self.grid[y][x] = old[y][x + 1] - - -class BlackScreen(FaceplateGrid): - # Basically a util class to handle - # inverting on __init__ - str_grid = """ -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.invert() + def scroll_up(self): + old = copy.deepcopy(self.grid) + for x in range(self.width): + for y in range(self.height): + if y == self.height - 1: + self.grid[y][x] = old[0][x] + else: + self.grid[y][x] = old[y + 1][x] + + def scroll_right(self): + old = copy.deepcopy(self.grid) + for x in range(self.width): + for y in range(self.height): + self.grid[y][x] = old[y][x - 1] + + def scroll_left(self): + old = copy.deepcopy(self.grid) + for x in range(self.width): + for y in range(self.height): + if x == self.width -1: + self.grid[y][x] = old[y][0] + else: + self.grid[y][x] = old[y][x + 1] + + def move_down(self): + old = copy.deepcopy(self.grid) + for x in range(self.width): + for y in range(self.height): + if y - 1 < 0: + self.grid[y][x] = 0 + else: + self.grid[y][x] = old[y - 1][x] + + def move_up(self): + old = copy.deepcopy(self.grid) + for x in range(self.width): + for y in range(self.height): + if y == self.height - 1: + self.grid[y][x] = 0 + else: + self.grid[y][x] = old[y + 1][x] + + def move_right(self): + old = copy.deepcopy(self.grid) + for x in range(self.width): + for y in range(self.height): + self.grid[y][x] = old[y][x - 1] + + def move_left(self): + old = copy.deepcopy(self.grid) + for x in range(self.width): + for y in range(self.height): + if x == self.width - 1: + self.grid[y][x] = 0 + else: + self.grid[y][x] = old[y][x + 1] + + + class BlackScreen(FaceplateGrid): + # Basically a util class to handle + # inverting on __init__ + str_grid = """ + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.invert() diff --git a/ovos_utils/enclosure/mark1/faceplate/animations.py b/ovos_utils/enclosure/mark1/faceplate/animations.py index 942761e8..93042170 100644 --- a/ovos_utils/enclosure/mark1/faceplate/animations.py +++ b/ovos_utils/enclosure/mark1/faceplate/animations.py @@ -1,574 +1,570 @@ -from ovos_utils.enclosure.mark1.faceplate import FacePlateAnimation, BlackScreen import copy import random +from ovos_utils.log import LOG -# Base animations -# These are mostly meant to be subclassed (empty animations) -class HorizontalScroll(FacePlateAnimation): - def __init__(self, direction="right", grid=None, bus=None): - super().__init__(grid, bus) - assert direction.startswith("r") or direction.startswith("l") - self.direction = direction[0] - - def animate(self): - if self.direction == "r": - self.scroll_right() - else: - self.scroll_left() - if self.is_empty: - self.stop() - - -class VerticalScroll(FacePlateAnimation): - def __init__(self, direction="up", - grid=None, bus=None): - super().__init__(grid, bus) - assert direction.startswith("u") or direction.startswith("d") - self.direction = direction[0] - - def animate(self): - if self.direction == "u": - self.scroll_up() - else: - self.scroll_down() - if self.is_empty: - self.stop() - - -class LeftRight(FacePlateAnimation): - def __init__(self, direction="right", start="left", grid=None, bus=None): - super().__init__(grid, bus) - assert direction.startswith("r") or direction.startswith("l") - self.direction = direction[0] - - # start at right/left side/center - inverted = not isinstance(self, BlackScreen) - if start[0] == "l": - # left side - for y in range(self.height): - for x in range(self.width): - if not inverted and self.grid[y][x] == 1: - pass - elif inverted and self.grid[y][x] == 0: - pass - elif start[0] == "r": - pass # right side - else: - pass # center - print(self.grid[1]) - - def animate(self): - left_collision = False - right_collision = False - inverted = not isinstance(self, BlackScreen) - for y in range(self.height): - if inverted: - if self.grid[y][self.width - 1] == 0: - right_collision = True - if self.grid[y][0] == 0: - left_collision = True +LOG.warning("ovos_utils.enclosure.mark1.faceplate moved to https://github.com/OpenVoiceOS/ovos-mark1-utils ;" + " this module will be removed in version 0.1.0") + +try: + from ovos_mark1.faceplate.animations import * +except ImportError: + from ovos_utils.enclosure.mark1.faceplate import FacePlateAnimation, BlackScreen + + # Base animations + # These are mostly meant to be subclassed (empty animations) + class HorizontalScroll(FacePlateAnimation): + def __init__(self, direction="right", grid=None, bus=None): + super().__init__(grid, bus) + assert direction.startswith("r") or direction.startswith("l") + self.direction = direction[0] + + def animate(self): + if self.direction == "r": + self.scroll_right() else: - if self.grid[y][self.width - 1] == 1: - right_collision = True - if self.grid[y][0] == 1: - left_collision = True - if left_collision and right_collision: - return # No space left to animate - elif right_collision: - self.direction = "l" - elif left_collision: - self.direction = "r" - if self.direction == "r": - self.scroll_right() - else: - self.scroll_left() - if self.is_empty: - self.stop() - - -class UpDown(FacePlateAnimation): - def __init__(self, direction="up", grid=None, bus=None): - super().__init__(grid, bus) - assert direction.startswith("u") or direction.startswith("d") - self.direction = direction[0] - - def animate(self): - top_collision = False - bottom_collision = False - for x in range(self.width): - if self.grid[0][x] == 1: - top_collision = True - if self.grid[self.height - 1][x] == 1: - bottom_collision = True + self.scroll_left() + if self.is_empty: + self.stop() - if top_collision and bottom_collision: - return # No space left to animate - elif top_collision: - self.direction = "d" - elif bottom_collision: - self.direction = "u" - if self.direction == "u": - self.scroll_up() - else: - self.scroll_down() - if self.is_empty: - self.stop() - - -class CollisionBox(FacePlateAnimation): - def __init__(self, - horizontal_direction=None, - vertical_direction=None, - grid=None, bus=None): - super().__init__(grid, bus) - assert horizontal_direction is None or \ - horizontal_direction.startswith("r") or \ - horizontal_direction.startswith("l") - assert vertical_direction is None or \ - vertical_direction.startswith("u") or \ - vertical_direction.startswith("d") - self.vertical_direction = vertical_direction[0] if \ - vertical_direction else None - self.horizontal_direction = horizontal_direction[0] if \ - horizontal_direction else None - - def animate(self): - left_collision = False - right_collision = False - top_collision = False - bottom_collision = False - for y in range(self.height): - if self.grid[y][self.width - 1] == 1: - right_collision = True - if self.grid[y][0] == 1: - left_collision = True - for x in range(self.width): - if self.grid[0][x] == 1: - top_collision = True - if self.grid[self.height - 1][x] == 1: - bottom_collision = True - if top_collision and bottom_collision: - self.vertical_direction = None - elif top_collision: - self.vertical_direction = "d" - elif bottom_collision: - self.vertical_direction = "u" - - if left_collision and right_collision: - self.horizontal_direction = None - elif right_collision: - self.horizontal_direction = "l" - elif left_collision: - self.horizontal_direction = "r" - - if self.vertical_direction is None: - pass - elif self.vertical_direction == "u": - self.scroll_up() - elif self.vertical_direction == "d": - self.scroll_down() - - if self.horizontal_direction is None: - pass - elif self.horizontal_direction == "r": - self.scroll_right() - elif self.horizontal_direction == "l": - self.scroll_left() - - if self.is_empty: - self.stop() - - -# Ready to use animations -class SquareWave(HorizontalScroll): - def __init__(self, direction="r", frequency=3, - amplitude=4, grid=None, bus=None): - super().__init__(direction, grid, bus) - # frequency must be > 1 - # frequency is in number of pixels - assert 0 < frequency - # amplitude must be 2, 4 or 6. else it renders badly - # amplitude is in number of pixels - assert 0 < amplitude < self.height - assert divmod(amplitude, 2)[1] == 0 - - self.freq = frequency - self.amplitude = self.height - amplitude - - self._initial_grid() - self.invert() - - def _initial_grid(self): - # draws the initial state - count = 0 - top = True - a = self.amplitude // 2 - 1 - for x in range(self.width): - if top: - self.grid[a + 1][x] = 1 + class VerticalScroll(FacePlateAnimation): + def __init__(self, direction="up", + grid=None, bus=None): + super().__init__(grid, bus) + assert direction.startswith("u") or direction.startswith("d") + self.direction = direction[0] + + def animate(self): + if self.direction == "u": + self.scroll_up() else: - self.grid[-a - 1][x] = 1 + self.scroll_down() + if self.is_empty: + self.stop() + + + class LeftRight(FacePlateAnimation): + def __init__(self, direction="right", start="left", grid=None, bus=None): + super().__init__(grid, bus) + assert direction.startswith("r") or direction.startswith("l") + self.direction = direction[0] + + # start at right/left side/center + inverted = not isinstance(self, BlackScreen) + if start[0] == "l": + # left side + for y in range(self.height): + for x in range(self.width): + if not inverted and self.grid[y][x] == 1: + pass + elif inverted and self.grid[y][x] == 0: + pass + elif start[0] == "r": + pass # right side + else: + pass # center + print(self.grid[1]) + def animate(self): + left_collision = False + right_collision = False + inverted = not isinstance(self, BlackScreen) for y in range(self.height): - if count == 0: - self.grid[y][x] = 1 - if y <= a: - self.grid[y][x] = 0 - elif y >= self.height - a: - self.grid[y][x] = 0 - count += 1 - if count == self.freq + 1: - count = 0 - top = not top - - -class StrayDot(CollisionBox): - def __init__(self, - start_x=None, - start_y=None, - horizontal_direction=None, - vertical_direction=None, - grid=None, bus=None): - horizontal_direction = horizontal_direction or \ - random.choice(["l", "r"]) - vertical_direction = vertical_direction or \ - random.choice(["u", "d"]) - super().__init__(horizontal_direction, vertical_direction, - grid, bus) - start_x = start_x or random.randint(0, self.width - 1) - start_y = start_y or random.randint(0, self.height - 1) - self.grid[start_y][start_x] = 1 - - -class ParticleBox(FacePlateAnimation): - def __init__(self, n_particles=5, bus=None): - super().__init__(bus=bus) - assert 0 < n_particles < 11 - self.n_particles = n_particles - self.particles = [] - - class Dot: - def __init__(self, idx, x, y, vx, vy): - self.x = x - self.y = y - self.vx = vx - self.vy = vy - self.idx = idx - - for i in range(n_particles): - vx = random.choice(["l", "r"]) - vy = random.choice(["u", "d"]) - x = random.randint(0, self.width - 1) - y = random.randint(0, self.height - 1) - while self.grid[y][x] == 1: - # 2 particles can't occupy same space + if inverted: + if self.grid[y][self.width - 1] == 0: + right_collision = True + if self.grid[y][0] == 0: + left_collision = True + else: + if self.grid[y][self.width - 1] == 1: + right_collision = True + if self.grid[y][0] == 1: + left_collision = True + if left_collision and right_collision: + return # No space left to animate + elif right_collision: + self.direction = "l" + elif left_collision: + self.direction = "r" + if self.direction == "r": + self.scroll_right() + else: + self.scroll_left() + if self.is_empty: + self.stop() + + + class UpDown(FacePlateAnimation): + def __init__(self, direction="up", grid=None, bus=None): + super().__init__(grid, bus) + assert direction.startswith("u") or direction.startswith("d") + self.direction = direction[0] + + def animate(self): + top_collision = False + bottom_collision = False + for x in range(self.width): + if self.grid[0][x] == 1: + top_collision = True + if self.grid[self.height - 1][x] == 1: + bottom_collision = True + + if top_collision and bottom_collision: + return # No space left to animate + elif top_collision: + self.direction = "d" + elif bottom_collision: + self.direction = "u" + if self.direction == "u": + self.scroll_up() + else: + self.scroll_down() + if self.is_empty: + self.stop() + + + class CollisionBox(FacePlateAnimation): + def __init__(self, + horizontal_direction=None, + vertical_direction=None, + grid=None, bus=None): + super().__init__(grid, bus) + assert horizontal_direction is None or \ + horizontal_direction.startswith("r") or \ + horizontal_direction.startswith("l") + assert vertical_direction is None or \ + vertical_direction.startswith("u") or \ + vertical_direction.startswith("d") + self.vertical_direction = vertical_direction[0] if \ + vertical_direction else None + self.horizontal_direction = horizontal_direction[0] if \ + horizontal_direction else None + + def animate(self): + left_collision = False + right_collision = False + top_collision = False + bottom_collision = False + for y in range(self.height): + if self.grid[y][self.width - 1] == 1: + right_collision = True + if self.grid[y][0] == 1: + left_collision = True + for x in range(self.width): + if self.grid[0][x] == 1: + top_collision = True + if self.grid[self.height - 1][x] == 1: + bottom_collision = True + + if top_collision and bottom_collision: + self.vertical_direction = None + elif top_collision: + self.vertical_direction = "d" + elif bottom_collision: + self.vertical_direction = "u" + + if left_collision and right_collision: + self.horizontal_direction = None + elif right_collision: + self.horizontal_direction = "l" + elif left_collision: + self.horizontal_direction = "r" + + if self.vertical_direction is None: + pass + elif self.vertical_direction == "u": + self.scroll_up() + elif self.vertical_direction == "d": + self.scroll_down() + + if self.horizontal_direction is None: + pass + elif self.horizontal_direction == "r": + self.scroll_right() + elif self.horizontal_direction == "l": + self.scroll_left() + + if self.is_empty: + self.stop() + + + # Ready to use animations + class SquareWave(HorizontalScroll): + def __init__(self, direction="r", frequency=3, + amplitude=4, grid=None, bus=None): + super().__init__(direction, grid, bus) + # frequency must be > 1 + # frequency is in number of pixels + assert 0 < frequency + # amplitude must be 2, 4 or 6. else it renders badly + # amplitude is in number of pixels + assert 0 < amplitude < self.height + assert divmod(amplitude, 2)[1] == 0 + + self.freq = frequency + self.amplitude = self.height - amplitude + + self._initial_grid() + self.invert() + + def _initial_grid(self): + # draws the initial state + count = 0 + top = True + a = self.amplitude // 2 - 1 + for x in range(self.width): + if top: + self.grid[a + 1][x] = 1 + else: + self.grid[-a - 1][x] = 1 + + for y in range(self.height): + if count == 0: + self.grid[y][x] = 1 + if y <= a: + self.grid[y][x] = 0 + elif y >= self.height - a: + self.grid[y][x] = 0 + count += 1 + if count == self.freq + 1: + count = 0 + top = not top + + + class StrayDot(CollisionBox): + def __init__(self, + start_x=None, + start_y=None, + horizontal_direction=None, + vertical_direction=None, + grid=None, bus=None): + horizontal_direction = horizontal_direction or \ + random.choice(["l", "r"]) + vertical_direction = vertical_direction or \ + random.choice(["u", "d"]) + super().__init__(horizontal_direction, vertical_direction, + grid, bus) + start_x = start_x or random.randint(0, self.width - 1) + start_y = start_y or random.randint(0, self.height - 1) + self.grid[start_y][start_x] = 1 + + + class ParticleBox(FacePlateAnimation): + def __init__(self, n_particles=5, bus=None): + super().__init__(bus=bus) + assert 0 < n_particles < 11 + self.n_particles = n_particles + self.particles = [] + + class Dot: + def __init__(self, idx, x, y, vx, vy): + self.x = x + self.y = y + self.vx = vx + self.vy = vy + self.idx = idx + + for i in range(n_particles): + vx = random.choice(["l", "r"]) + vy = random.choice(["u", "d"]) x = random.randint(0, self.width - 1) y = random.randint(0, self.height - 1) - self.grid[y][x] = 1 - self.particles.append(Dot(i, x, y, vx, vy)) - - def render_particles(self): - self.clear() - for p in self.particles: - self.grid[p.y][p.x] = 1 - - def get_particle(self, x, y): - for p in self.particles: - if p.x == x and p.y == y: - return p - - def process_collisions(self): - # new particles after this turn - new_particles = copy.deepcopy(self.particles) - - # NOTE this is not a physics simulation! - # while it is behaving like an elastic collision - # if there is a 3+ particle collision results will be incorrect - # as long as only 2 particles collide it looks accurate - # max number of particles limited to 10 to minimize chance of this - # happening - for p in self.particles: - idx = p.idx - - # horizontal movement - if p.vx is None: - # not moving horizontally - if p.x != 0: - # check for collisions from left p2 -> p1 - p2 = self.get_particle(p.x - 1, p.y) - if p2 and p2.vx == "r": - # collision p2 -> p1 - if p.x == self.width - 1: - new_particles[idx].vx = None - else: - new_particles[idx].vx = "r" - new_particles[idx].x += 1 - if p.x != self.width - 1: - # check for collisions from right p1 <- p2 + while self.grid[y][x] == 1: + # 2 particles can't occupy same space + x = random.randint(0, self.width - 1) + y = random.randint(0, self.height - 1) + self.grid[y][x] = 1 + self.particles.append(Dot(i, x, y, vx, vy)) + + def render_particles(self): + self.clear() + for p in self.particles: + self.grid[p.y][p.x] = 1 + + def get_particle(self, x, y): + for p in self.particles: + if p.x == x and p.y == y: + return p + + def process_collisions(self): + # new particles after this turn + new_particles = copy.deepcopy(self.particles) + + # NOTE this is not a physics simulation! + # while it is behaving like an elastic collision + # if there is a 3+ particle collision results will be incorrect + # as long as only 2 particles collide it looks accurate + # max number of particles limited to 10 to minimize chance of this + # happening + for p in self.particles: + idx = p.idx + + # horizontal movement + if p.vx is None: + # not moving horizontally + if p.x != 0: + # check for collisions from left p2 -> p1 + p2 = self.get_particle(p.x - 1, p.y) + if p2 and p2.vx == "r": + # collision p2 -> p1 + if p.x == self.width - 1: + new_particles[idx].vx = None + else: + new_particles[idx].vx = "r" + new_particles[idx].x += 1 + if p.x != self.width - 1: + # check for collisions from right p1 <- p2 + p2 = self.get_particle(p.x + 1, p.y) + if p2 and p2.vx == "l": + # collision p1 <- p2 + if p.x == 0: + new_particles[idx].vx = None + else: + new_particles[idx].vx = "l" + new_particles[idx].x -= 1 + # moving right + elif p.vx == "r": p2 = self.get_particle(p.x + 1, p.y) - if p2 and p2.vx == "l": - # collision p1 <- p2 - if p.x == 0: + if p.x == self.width - 1: + # border collision p1 -> | + new_particles[idx].vx = "l" + new_particles[idx].x -= 1 + elif p2: + # particle collision p1 -> p2 + if p2.vx is None: + # p2 moves, p1 stops new_particles[idx].vx = None - else: - new_particles[idx].vx = "l" - new_particles[idx].x -= 1 - # moving right - elif p.vx == "r": - p2 = self.get_particle(p.x + 1, p.y) - if p.x == self.width - 1: - # border collision p1 -> | - new_particles[idx].vx = "l" - new_particles[idx].x -= 1 - elif p2: - # particle collision p1 -> p2 - if p2.vx is None: - # p2 moves, p1 stops - new_particles[idx].vx = None - elif p2.vx == "r": - # moving together + elif p2.vx == "r": + # moving together + new_particles[idx].x += 1 + elif p2 and p2.vx == "l": + if p.x == 0: + new_particles[idx].vx = None + else: + # both change direction + new_particles[idx].vx = "l" + new_particles[idx].x -= 1 + else: + # move right new_particles[idx].x += 1 - elif p2 and p2.vx == "l": - if p.x == 0: + # moving left + elif p.vx == "l": + p2 = self.get_particle(p.x - 1, p.y) + if p.x == 0: + # border collision | <- p1 + new_particles[idx].vx = "r" + new_particles[idx].x += 1 + elif p2: + # particle collision p2 <- p1 + + if p2.vx is None: + # p2 moves, p1 stops new_particles[idx].vx = None - else: - # both change direction - new_particles[idx].vx = "l" + elif p2.vx == "l": + # moving together, no collision new_particles[idx].x -= 1 - else: - # move right - new_particles[idx].x += 1 - # moving left - elif p.vx == "l": - p2 = self.get_particle(p.x - 1, p.y) - if p.x == 0: - # border collision | <- p1 - new_particles[idx].vx = "r" - new_particles[idx].x += 1 - elif p2: - # particle collision p2 <- p1 - - if p2.vx is None: - # p2 moves, p1 stops - new_particles[idx].vx = None - elif p2.vx == "l": - # moving together, no collision + elif p2.vx == "r": + if p.x == self.width - 1: + new_particles[idx].vx = None + else: + # both change direction + new_particles[idx].vx = "r" + new_particles[idx].x += 1 + else: + # move left new_particles[idx].x -= 1 - elif p2.vx == "r": - if p.x == self.width - 1: - new_particles[idx].vx = None - else: - # both change direction - new_particles[idx].vx = "r" - new_particles[idx].x += 1 - else: - # move left - new_particles[idx].x -= 1 - - # vertical movement - if p.vy is None: - # not moving vertically - if p.y != 0: - # check for collisions from top p2 -> p1 - p2 = self.get_particle(p.x, p.y - 1) - if p2 and p2.vy == "d": - if p.y == self.height - 1: + + # vertical movement + if p.vy is None: + # not moving vertically + if p.y != 0: + # check for collisions from top p2 -> p1 + p2 = self.get_particle(p.x, p.y - 1) + if p2 and p2.vy == "d": + if p.y == self.height - 1: + new_particles[idx].vy = None + else: + # collision p2 -> p1 + new_particles[idx].vy = "d" + new_particles[idx].y += 1 + if p.y != self.height - 1: + # check for collisions from bottom p1 <- p2 + p2 = self.get_particle(p.x, p.y + 1) + if p2 and p2.vy == "u": + # collision p1 <- p2 + if p.y == 0: # on top + new_particles[idx].vy = None + else: + new_particles[idx].vy = "u" + new_particles[idx].y -= 1 + # moving down + elif p.vy == "d": + p2 = self.get_particle(p.x, p.y + 1) + if p.y == self.height - 1: + # border collision p1 -> | + new_particles[idx].vy = "u" + new_particles[idx].y -= 1 + elif p2: + # particle collision p1 -> p2 + + if p2.vy is None: + # p2 moves, p1 stops new_particles[idx].vy = None - else: - # collision p2 -> p1 - new_particles[idx].vy = "d" + elif p2.vy == "d": + # moving together new_particles[idx].y += 1 - if p.y != self.height - 1: - # check for collisions from bottom p1 <- p2 - p2 = self.get_particle(p.x, p.y + 1) - if p2 and p2.vy == "u": - # collision p1 <- p2 - if p.y == 0: # on top + elif p2.vy == "u": + if p.y == 0: + new_particles[ + idx].vy = None # wall absorbed momentum + else: + # both change direction + new_particles[idx].vy = "u" + new_particles[idx].y -= 1 + else: + # move down + new_particles[idx].y += 1 + # moving up + elif p.vy == "u": + + p2 = self.get_particle(p.x, p.y - 1) + if p.y == 0: + # border collision | <- p1 + new_particles[idx].vy = "d" + new_particles[idx].y += 1 + elif p2: + # particle collision p2 <- p1 + if p2.vy is None: + # p2 moves, p1 stops new_particles[idx].vy = None - else: - new_particles[idx].vy = "u" + elif p2.vy == "u": + # moving together, no collision new_particles[idx].y -= 1 - # moving down - elif p.vy == "d": - p2 = self.get_particle(p.x, p.y + 1) - if p.y == self.height - 1: - # border collision p1 -> | - new_particles[idx].vy = "u" - new_particles[idx].y -= 1 - elif p2: - # particle collision p1 -> p2 - - if p2.vy is None: - # p2 moves, p1 stops - new_particles[idx].vy = None - elif p2.vy == "d": - # moving together - new_particles[idx].y += 1 - elif p2.vy == "u": - if p.y == 0: - new_particles[ - idx].vy = None # wall absorbed momentum - else: + elif p2.vy == "d": # both change direction - new_particles[idx].vy = "u" - new_particles[idx].y -= 1 - else: - # move down - new_particles[idx].y += 1 - # moving up - elif p.vy == "u": - - p2 = self.get_particle(p.x, p.y - 1) - if p.y == 0: - # border collision | <- p1 - new_particles[idx].vy = "d" - new_particles[idx].y += 1 - elif p2: - # particle collision p2 <- p1 - if p2.vy is None: - # p2 moves, p1 stops - new_particles[idx].vy = None - elif p2.vy == "u": - # moving together, no collision + if p.y == self.height - 1: + new_particles[idx].vy = None + else: + new_particles[idx].vy = "d" + new_particles[idx].y += 1 + else: + # move left new_particles[idx].y -= 1 - elif p2.vy == "d": - # both change direction - if p.y == self.height - 1: - new_particles[idx].vy = None - else: - new_particles[idx].vy = "d" - new_particles[idx].y += 1 - else: - # move left - new_particles[idx].y -= 1 - - # update processed particles - self.particles = new_particles - def animate(self): - self.process_collisions() - self.render_particles() + # update processed particles + self.particles = new_particles + def animate(self): + self.process_collisions() + self.render_particles() -class FallingDots(FacePlateAnimation): - def __init__(self, n=10, bus=None): - super().__init__(bus=bus) - self._create = True - assert 0 < n < 32 - self.n = n - @property - def n_dots(self): - n = 0 - for y in range(self.height): - for x in range(self.width): - if self.grid[y][x]: - n += 1 - return n - - def animate(self): - self.move_down() - if self._create: - if random.choice([True, False]): - self._create = False - x = random.randint(0, self.width - 1) - self.grid[0][x] = 1 - if self.n_dots < self.n: + class FallingDots(FacePlateAnimation): + def __init__(self, n=10, bus=None): + super().__init__(bus=bus) self._create = True + assert 0 < n < 32 + self.n = n - -class StraightParticleShooter(FacePlateAnimation): - def __init__(self, period=None, bus=None): - super().__init__(bus=bus) - self.direction = "d" - self.period = period - self.counter = 0 - # draw shooter - self.grid[0][0] = 1 - self.grid[1][0] = 1 - self.grid[1][1] = 1 - self.grid[2][0] = 1 - - def line_down(self): - old = copy.deepcopy(self.grid) - for y in range(self.height): - self.grid[y][0] = old[y - 1][0] - self.grid[y][1] = old[y - 1][1] - - def line_up(self): - old = copy.deepcopy(self.grid) - for y in range(self.height): - if y == self.height - 1: - self.grid[y][0] = old[0][0] - self.grid[y][1] = old[0][1] - else: - self.grid[y][0] = old[y + 1][0] - self.grid[y][1] = old[y + 1][1] - - def scroll_particles(self): - old = copy.deepcopy(self.grid) - for x in range(2, self.width): - for y in range(self.height): - if old[y][x] == 1: - self.grid[y][x] = 0 - if x < self.width - 1: - self.grid[y][x + 1] = 1 - - @property - def num_particles(self): - n = 0 - for x in range(2, self.width): + @property + def n_dots(self): + n = 0 for y in range(self.height): - if self.grid[y][x] == 1: - n += 1 - return n - - @property - def line(self): - for y in range(self.height): - if self.grid[y][0] == 1: - return y - return 0 - - def animate(self): - # collision detection - top_collision = False - bottom_collision = False - if self.grid[0][0] == 1: - top_collision = True - elif self.grid[self.height - 1][0] == 1: - bottom_collision = True - if top_collision: + for x in range(self.width): + if self.grid[y][x]: + n += 1 + return n + + def animate(self): + self.move_down() + if self._create: + if random.choice([True, False]): + self._create = False + x = random.randint(0, self.width - 1) + self.grid[0][x] = 1 + if self.n_dots < self.n: + self._create = True + + + class StraightParticleShooter(FacePlateAnimation): + def __init__(self, period=None, bus=None): + super().__init__(bus=bus) self.direction = "d" - elif bottom_collision: - self.direction = "u" - - # bounce the "emitter" up and down - if self.direction == "u": - self.line_up() - else: - self.line_down() - - # create particles - period = self.period or random.randint(0, 20) - if self.num_particles < 1 or self.counter >= period: - self.grid[self.line + 1][2] = 1 + self.period = period self.counter = 0 + # draw shooter + self.grid[0][0] = 1 + self.grid[1][0] = 1 + self.grid[1][1] = 1 + self.grid[2][0] = 1 + + def line_down(self): + old = copy.deepcopy(self.grid) + for y in range(self.height): + self.grid[y][0] = old[y - 1][0] + self.grid[y][1] = old[y - 1][1] - # animate particles - self.scroll_particles() - self.counter += 1 - - - -if __name__ == "__main__": - from ovos_utils.messagebus import get_mycroft_bus - from time import sleep + def line_up(self): + old = copy.deepcopy(self.grid) + for y in range(self.height): + if y == self.height - 1: + self.grid[y][0] = old[0][0] + self.grid[y][1] = old[0][1] + else: + self.grid[y][0] = old[y + 1][0] + self.grid[y][1] = old[y + 1][1] + + def scroll_particles(self): + old = copy.deepcopy(self.grid) + for x in range(2, self.width): + for y in range(self.height): + if old[y][x] == 1: + self.grid[y][x] = 0 + if x < self.width - 1: + self.grid[y][x + 1] = 1 + + @property + def num_particles(self): + n = 0 + for x in range(2, self.width): + for y in range(self.height): + if self.grid[y][x] == 1: + n += 1 + return n + + @property + def line(self): + for y in range(self.height): + if self.grid[y][0] == 1: + return y + return 0 + + def animate(self): + # collision detection + top_collision = False + bottom_collision = False + if self.grid[0][0] == 1: + top_collision = True + elif self.grid[self.height - 1][0] == 1: + bottom_collision = True + if top_collision: + self.direction = "d" + elif bottom_collision: + self.direction = "u" + + # bounce the "emitter" up and down + if self.direction == "u": + self.line_up() + else: + self.line_down() - bus = get_mycroft_bus("192.168.1.70") + # create particles + period = self.period or random.randint(0, 20) + if self.num_particles < 1 or self.counter >= period: + self.grid[self.line + 1][2] = 1 + self.counter = 0 - for faceplate in ParticleBox(bus=bus): - faceplate.display(invert=False) - sleep(0.5) \ No newline at end of file + # animate particles + self.scroll_particles() + self.counter += 1 diff --git a/ovos_utils/enclosure/mark1/faceplate/cellular_automaton.py b/ovos_utils/enclosure/mark1/faceplate/cellular_automaton.py index d543e9fa..92d43245 100644 --- a/ovos_utils/enclosure/mark1/faceplate/cellular_automaton.py +++ b/ovos_utils/enclosure/mark1/faceplate/cellular_automaton.py @@ -1,492 +1,485 @@ -from ovos_utils.enclosure.mark1.faceplate import FacePlateAnimation import copy import random +from ovos_utils.log import LOG -# Game of Life Base -class GoL(FacePlateAnimation): +LOG.warning("ovos_utils.enclosure.mark1.faceplate moved to https://github.com/OpenVoiceOS/ovos-mark1-utils ;" + " this module will be removed in version 0.1.0") - def __init__(self, entropy=0, grid=None, bus=None): - super().__init__(grid, bus) - self.entropy = entropy - if self.is_empty: - self.randomize() +try: + from ovos_mark1.faceplate.cellular_automaton import * +except: + from ovos_utils.enclosure.mark1.faceplate import FacePlateAnimation - def _live_neighbours(self, y, x): - """Returns the number of live neighbours.""" - count = 0 - if y > 0: - if self.grid[y - 1][x]: - count = count + 1 - if x > 0: - if self.grid[y - 1][x - 1]: - count = count + 1 - if self.width > (x + 1): - if self.grid[y - 1][x + 1]: - count = count + 1 - if x > 0: - if self.grid[y][x - 1]: - count = count + 1 - if self.width > (x + 1): - if self.grid[y][x + 1]: - count = count + 1 + # Game of Life Base + class GoL(FacePlateAnimation): + + def __init__(self, entropy=0, grid=None, bus=None): + super().__init__(grid, bus) + self.entropy = entropy + if self.is_empty: + self.randomize() + + def _live_neighbours(self, y, x): + """Returns the number of live neighbours.""" + count = 0 + if y > 0: + if self.grid[y - 1][x]: + count = count + 1 + if x > 0: + if self.grid[y - 1][x - 1]: + count = count + 1 + if self.width > (x + 1): + if self.grid[y - 1][x + 1]: + count = count + 1 - if self.height > (y + 1): - if self.grid[y + 1][x]: - count = count + 1 if x > 0: - if self.grid[y + 1][x - 1]: + if self.grid[y][x - 1]: count = count + 1 if self.width > (x + 1): - if self.grid[y + 1][x + 1]: + if self.grid[y][x + 1]: count = count + 1 - return count - - def animate(self): - """Game of Life turn""" - nt = copy.deepcopy(self.grid) - for y in range(0, self.height): - for x in range(0, self.width): - neighbours = self._live_neighbours(y, x) - if self.grid[y][x] == 0: - if neighbours == 3: - nt[y][x] = 1 - else: - if (neighbours < 2) or (neighbours > 3): - nt[y][x] = 0 - if nt == self.grid and self.entropy <= 0: - self.stop() - self.grid = nt - self.randomize(self.entropy) - if self.is_empty: - self.stop() - - -# Langtons Ant base -class _Ant: - def __init__(self, x, y, direction, height=8, width=32): - self.x = x - self.y = y - assert direction[0] in ["r", "l", "u", "d"] - self.direction = direction[0] - self.grid_height = height - self.grid_width = width - self.dead = False - - def move_forward(self): - if self.direction == "r": - self.x += 1 - if self.x == self.grid_width: - self.dead = True - elif self.direction == "d": - self.y += 1 - if self.y == self.grid_height: - self.dead = True - elif self.direction == "l": - self.x -= 1 - if self.x == -1: - self.dead = True - elif self.direction == "u": - self.y -= 1 - if self.y == -1: - self.dead = True - - def turn_right(self): - if self.direction == "r": - self.direction = "d" - elif self.direction == "d": - self.direction = "l" - elif self.direction == "l": - self.direction = "u" - elif self.direction == "u": - self.direction = "r" - - def turn_left(self): - if self.direction == "r": - self.direction = "u" - elif self.direction == "d": - self.direction = "r" - elif self.direction == "l": - self.direction = "d" - elif self.direction == "u": - self.direction = "l" - - -class _InfiniteAnt(_Ant): - def move_forward(self): - if self.direction == "r": - self.x += 1 - if self.x == self.grid_width: - self.x = 0 - elif self.direction == "d": - self.y += 1 - if self.y == self.grid_height: - self.y = 0 - elif self.direction == "l": - self.x -= 1 - if self.x == -1: - self.x = self.grid_width - 1 - elif self.direction == "u": - self.y -= 1 - if self.y == -1: - self.y = self.grid_height - 1 - - -class _ReverseAnt(_Ant): - def turn_left(self): - super().turn_right() - - def turn_right(self): - super().turn_left() - - -class _ReverseInfiniteAnt(_InfiniteAnt): - def turn_left(self): - super().turn_right() - - def turn_right(self): - super().turn_left() - - -class LangtonsAnt(FacePlateAnimation): - def __init__(self, ants=1, continuous=True, gen_reverse=False, - grid=None, bus=None): - super().__init__(grid=grid, bus=bus) - self.ants = [] - # if continuous loops around the board - # height + 1 -> 0 - # width + 1 -> 0 - # else ant is removed - self.continuous = continuous - if isinstance(ants, int): - # spawn N ants - assert 0 <= ants < 256 - for i in range(ants): - x = random.randint(0, self.width - 1) - y = random.randint(0, self.height - 1) - direction = random.choice(["u", "d", "l", "r"]) - reverse = False - if gen_reverse: - reverse = random.choice([True, False]) - ant = self.ant_factory(x, y, direction, reverse) - self.ants.append(ant) - elif isinstance(ants, list): - # Ant objects - self.ants = ants - for ant in self.ants: - assert isinstance(ant, _Ant) - else: - raise ValueError - - def ant_factory(self, x, y, direction, reverse=False): - # reverse ants exit black squares to the opposite direction - if self.continuous: - # loops around the board instead of dying + if self.height > (y + 1): + if self.grid[y + 1][x]: + count = count + 1 + if x > 0: + if self.grid[y + 1][x - 1]: + count = count + 1 + if self.width > (x + 1): + if self.grid[y + 1][x + 1]: + count = count + 1 + + return count + + def animate(self): + """Game of Life turn""" + nt = copy.deepcopy(self.grid) + for y in range(0, self.height): + for x in range(0, self.width): + neighbours = self._live_neighbours(y, x) + if self.grid[y][x] == 0: + if neighbours == 3: + nt[y][x] = 1 + else: + if (neighbours < 2) or (neighbours > 3): + nt[y][x] = 0 + if nt == self.grid and self.entropy <= 0: + self.stop() + self.grid = nt + self.randomize(self.entropy) + if self.is_empty: + self.stop() + + + # Langtons Ant base + class _Ant: + def __init__(self, x, y, direction, height=8, width=32): + self.x = x + self.y = y + assert direction[0] in ["r", "l", "u", "d"] + self.direction = direction[0] + self.grid_height = height + self.grid_width = width + self.dead = False + + def move_forward(self): + if self.direction == "r": + self.x += 1 + if self.x == self.grid_width: + self.dead = True + elif self.direction == "d": + self.y += 1 + if self.y == self.grid_height: + self.dead = True + elif self.direction == "l": + self.x -= 1 + if self.x == -1: + self.dead = True + elif self.direction == "u": + self.y -= 1 + if self.y == -1: + self.dead = True + + def turn_right(self): + if self.direction == "r": + self.direction = "d" + elif self.direction == "d": + self.direction = "l" + elif self.direction == "l": + self.direction = "u" + elif self.direction == "u": + self.direction = "r" + + def turn_left(self): + if self.direction == "r": + self.direction = "u" + elif self.direction == "d": + self.direction = "r" + elif self.direction == "l": + self.direction = "d" + elif self.direction == "u": + self.direction = "l" + + + class _InfiniteAnt(_Ant): + def move_forward(self): + if self.direction == "r": + self.x += 1 + if self.x == self.grid_width: + self.x = 0 + elif self.direction == "d": + self.y += 1 + if self.y == self.grid_height: + self.y = 0 + elif self.direction == "l": + self.x -= 1 + if self.x == -1: + self.x = self.grid_width - 1 + elif self.direction == "u": + self.y -= 1 + if self.y == -1: + self.y = self.grid_height - 1 + + + class _ReverseAnt(_Ant): + def turn_left(self): + super().turn_right() + + def turn_right(self): + super().turn_left() + + + class _ReverseInfiniteAnt(_InfiniteAnt): + def turn_left(self): + super().turn_right() + + def turn_right(self): + super().turn_left() + + + class LangtonsAnt(FacePlateAnimation): + def __init__(self, ants=1, continuous=True, gen_reverse=False, + grid=None, bus=None): + super().__init__(grid=grid, bus=bus) + self.ants = [] + # if continuous loops around the board + # height + 1 -> 0 + # width + 1 -> 0 + # else ant is removed + self.continuous = continuous + if isinstance(ants, int): + # spawn N ants + assert 0 <= ants < 256 + for i in range(ants): + x = random.randint(0, self.width - 1) + y = random.randint(0, self.height - 1) + direction = random.choice(["u", "d", "l", "r"]) + reverse = False + if gen_reverse: + reverse = random.choice([True, False]) + ant = self.ant_factory(x, y, direction, reverse) + self.ants.append(ant) + elif isinstance(ants, list): + # Ant objects + self.ants = ants + for ant in self.ants: + assert isinstance(ant, _Ant) + else: + raise ValueError + + def ant_factory(self, x, y, direction, reverse=False): + # reverse ants exit black squares to the opposite direction + if self.continuous: + # loops around the board instead of dying + if reverse: + return _ReverseInfiniteAnt(x, y, direction) + return _InfiniteAnt(x, y, direction) if reverse: - return _ReverseInfiniteAnt(x, y, direction) - return _InfiniteAnt(x, y, direction) - if reverse: - return _ReverseAnt(x, y, direction) - return _Ant(x, y, direction) - - def move_ants(self): - # copy grid, multiple ants might want to flip same square - # end result is the same so this is not a problem as long as it does - # not change during iteration - old_grid = copy.deepcopy(self.grid) - - for idx, ant in enumerate(self.ants): - if self.ants[idx].dead: - continue - if old_grid[ant.y][ant.x] == 1: - # black - self.ants[idx].turn_left() + return _ReverseAnt(x, y, direction) + return _Ant(x, y, direction) + + def move_ants(self): + # copy grid, multiple ants might want to flip same square + # end result is the same so this is not a problem as long as it does + # not change during iteration + old_grid = copy.deepcopy(self.grid) + + for idx, ant in enumerate(self.ants): + if self.ants[idx].dead: + continue + if old_grid[ant.y][ant.x] == 1: + # black + self.ants[idx].turn_left() + else: + # white + self.ants[idx].turn_right() + # flip color + self.grid[ant.y][ant.x] = not old_grid[ant.y][ant.x] + # update ant position + self.ants[idx].move_forward() + + def animate(self): + self.move_ants() + # Stop condition -> all ants moved out of the board + dead_ants = [ant for ant in self.ants if ant.dead] + if len(dead_ants) == len(self.ants): + self.stop() + + + # Game of Life Animations + class SpaceInvader(GoL): + # This basically half "pulsar" + str_grid = """ + XXXXXXXXXXXX XXX XXXXXXXXXXXXX + XXXXXXXXXX X X X X X XXXXXXXXXXX + XXXXXXXX XX X XX XXXXXXXXX + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXX XXX XXXXXXXXXXXXX + XXXXXXXXXXXX XXXXX XXXXXXXXXXXXX + XXXXXXXXXXXX XXXXX XXXXXXXXXXXXX + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + """ + + + # Langton's Ant animations + + # Single Ant + class LangtonsLineDisplacer(LangtonsAnt): + # see pattern here + # https://youtu.be/w6XQQhCgq5c?t=84 + + def __init__(self, x=None, y=None, continuous=True, bus=None): + super().__init__(0, continuous, bus=bus) + x = x if x is not None else random.randint(0, self.width - 1) + y = y if y is not None else random.randint(0, self.height - 1) + ant = self.ant_factory(x, y - 1, "u") + self.ants.append(ant) + # create initial line + for i in range(0, self.width): + self.grid[y][i] = 1 + + + # 2 Ants + class LangtonsAntsOscillator(LangtonsAnt): + # see pattern here + # https://youtu.be/w6XQQhCgq5c?t=103 + + def __init__(self, x=None, y=None, bus=None): + super().__init__(0, bus=bus) + x1 = x2 = x if x is not None else self.width // 2 + y1 = y if y is not None else self.height // 2 + y2 = y1 - 1 + dir1 = "d" + dir2 = "u" + ant1 = self.ant_factory(x1, y1, dir1) + ant2 = self.ant_factory(x2, y2, dir2) + self.ants += [ant1, ant2] + + + class LangtonsAntsOscillator2(LangtonsAnt): + def __init__(self, x=None, y=None, bus=None): + super().__init__(0, bus=bus) + x1 = x2 = x if x is not None else self.width // 2 + y1 = y if y is not None else self.height // 2 + y2 = y1 - 1 + dir1 = dir2 = "u" + ant1 = self.ant_factory(x1, y1, dir1) + ant2 = self.ant_factory(x2, y2, dir2) + self.ants += [ant1, ant2] + + + class LangtonsAntsOscillator3(LangtonsAnt): + def __init__(self, x=None, y=None, bus=None): + super().__init__(0, bus=bus) + x1 = x2 = x if x is not None else self.width // 2 + y1 = y if y is not None else self.height // 2 + y2 = y1 - 1 + dir1 = "l" + dir2 = "r" + ant1 = self.ant_factory(x1, y1, dir1) + ant2 = self.ant_factory(x2, y2, dir2) + self.ants += [ant1, ant2] + + + class LangtonsAntsOscillator4(LangtonsAnt): + def __init__(self, x=None, y=None, bus=None): + super().__init__(0, bus=bus) + x1 = x2 = x if x is not None else self.width // 2 + y1 = y if y is not None else self.height // 2 + y2 = y1 - 1 + dir1 = dir2 = "l" + ant1 = self.ant_factory(x1, y1, dir1) + ant2 = self.ant_factory(x2, y2, dir2) + self.ants += [ant1, ant2] + + + class LangtonsAntsOscillator5(LangtonsAnt): + def __init__(self, x=None, y=None, bus=None): + super().__init__(0, bus=bus) + x1 = x2 = x if x is not None else self.width // 2 + y1 = y if y is not None else self.height // 2 + y2 = y1 - 1 + dir1 = "u" + dir2 = "d" + ant1 = self.ant_factory(x1, y1, dir1) + ant2 = self.ant_factory(x2, y2, dir2) + self.ants += [ant1, ant2] + + + class LangtonsAntTrail(LangtonsAnt): + # see pattern here + # https://youtu.be/w6XQQhCgq5c?t=159 + + def __init__(self, x=None, y=None, bus=None): + super().__init__(0, bus=bus) + x = x if x is not None else random.randint(0, self.width - 1) + y = y if y is not None else random.randint(0, self.height - 1) + dir1 = "u" + dir2 = "d" + ant1 = self.ant_factory(x, y - 1, dir1) + ant2 = self.ant_factory(x, y, dir2, reverse=True) + self.ants += [ant1, ant2] + + + class LangtonsAntDotTransporter(LangtonsAnt): + # https://youtu.be/w6XQQhCgq5c?t=171 + + def __init__(self, x=None, y=None, bus=None): + super().__init__(0, bus=bus) + x = x if x is not None else random.randint(0, self.width - 1) + y = y if y is not None else random.randint(0, self.height - 1) + dir1 = dir2 = "u" + ant1 = self.ant_factory(x, y, dir1) + ant2 = self.ant_factory(x + 1, y, dir2, reverse=True) + self.ants += [ant1, ant2] + # initial grid + if y + 1 == self.height: + self.grid[0][x + 1] = 1 # bellow anti-ant else: - # white - self.ants[idx].turn_right() - # flip color - self.grid[ant.y][ant.x] = not old_grid[ant.y][ant.x] - # update ant position - self.ants[idx].move_forward() - - def animate(self): - self.move_ants() - # Stop condition -> all ants moved out of the board - dead_ants = [ant for ant in self.ants if ant.dead] - if len(dead_ants) == len(self.ants): - self.stop() - - -# Game of Life Animations -class SpaceInvader(GoL): - # This basically half "pulsar" - str_grid = """ -XXXXXXXXXXXX XXX XXXXXXXXXXXXX -XXXXXXXXXX X X X X X XXXXXXXXXXX -XXXXXXXX XX X XX XXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXX XXX XXXXXXXXXXXXX -XXXXXXXXXXXX XXXXX XXXXXXXXXXXXX -XXXXXXXXXXXX XXXXX XXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -""" - - -# Langton's Ant animations - -# Single Ant -class LangtonsLineDisplacer(LangtonsAnt): - # see pattern here - # https://youtu.be/w6XQQhCgq5c?t=84 - - def __init__(self, x=None, y=None, continuous=True, bus=None): - super().__init__(0, continuous, bus=bus) - x = x if x is not None else random.randint(0, self.width - 1) - y = y if y is not None else random.randint(0, self.height - 1) - ant = self.ant_factory(x, y - 1, "u") - self.ants.append(ant) - # create initial line - for i in range(0, self.width): - self.grid[y][i] = 1 - - -# 2 Ants -class LangtonsAntsOscillator(LangtonsAnt): - # see pattern here - # https://youtu.be/w6XQQhCgq5c?t=103 - - def __init__(self, x=None, y=None, bus=None): - super().__init__(0, bus=bus) - x1 = x2 = x if x is not None else self.width // 2 - y1 = y if y is not None else self.height // 2 - y2 = y1 - 1 - dir1 = "d" - dir2 = "u" - ant1 = self.ant_factory(x1, y1, dir1) - ant2 = self.ant_factory(x2, y2, dir2) - self.ants += [ant1, ant2] - - -class LangtonsAntsOscillator2(LangtonsAnt): - def __init__(self, x=None, y=None, bus=None): - super().__init__(0, bus=bus) - x1 = x2 = x if x is not None else self.width // 2 - y1 = y if y is not None else self.height // 2 - y2 = y1 - 1 - dir1 = dir2 = "u" - ant1 = self.ant_factory(x1, y1, dir1) - ant2 = self.ant_factory(x2, y2, dir2) - self.ants += [ant1, ant2] - - -class LangtonsAntsOscillator3(LangtonsAnt): - def __init__(self, x=None, y=None, bus=None): - super().__init__(0, bus=bus) - x1 = x2 = x if x is not None else self.width // 2 - y1 = y if y is not None else self.height // 2 - y2 = y1 - 1 - dir1 = "l" - dir2 = "r" - ant1 = self.ant_factory(x1, y1, dir1) - ant2 = self.ant_factory(x2, y2, dir2) - self.ants += [ant1, ant2] - - -class LangtonsAntsOscillator4(LangtonsAnt): - def __init__(self, x=None, y=None, bus=None): - super().__init__(0, bus=bus) - x1 = x2 = x if x is not None else self.width // 2 - y1 = y if y is not None else self.height // 2 - y2 = y1 - 1 - dir1 = dir2 = "l" - ant1 = self.ant_factory(x1, y1, dir1) - ant2 = self.ant_factory(x2, y2, dir2) - self.ants += [ant1, ant2] - - -class LangtonsAntsOscillator5(LangtonsAnt): - def __init__(self, x=None, y=None, bus=None): - super().__init__(0, bus=bus) - x1 = x2 = x if x is not None else self.width // 2 - y1 = y if y is not None else self.height // 2 - y2 = y1 - 1 - dir1 = "u" - dir2 = "d" - ant1 = self.ant_factory(x1, y1, dir1) - ant2 = self.ant_factory(x2, y2, dir2) - self.ants += [ant1, ant2] - - -class LangtonsAntTrail(LangtonsAnt): - # see pattern here - # https://youtu.be/w6XQQhCgq5c?t=159 - - def __init__(self, x=None, y=None, bus=None): - super().__init__(0, bus=bus) - x = x if x is not None else random.randint(0, self.width - 1) - y = y if y is not None else random.randint(0, self.height - 1) - dir1 = "u" - dir2 = "d" - ant1 = self.ant_factory(x, y - 1, dir1) - ant2 = self.ant_factory(x, y, dir2, reverse=True) - self.ants += [ant1, ant2] - - -class LangtonsAntDotTransporter(LangtonsAnt): - # https://youtu.be/w6XQQhCgq5c?t=171 - - def __init__(self, x=None, y=None, bus=None): - super().__init__(0, bus=bus) - x = x if x is not None else random.randint(0, self.width - 1) - y = y if y is not None else random.randint(0, self.height - 1) - dir1 = dir2 = "u" - ant1 = self.ant_factory(x, y, dir1) - ant2 = self.ant_factory(x + 1, y, dir2, reverse=True) - self.ants += [ant1, ant2] - # initial grid - if y + 1 == self.height: - self.grid[0][x + 1] = 1 # bellow anti-ant - else: - self.grid[y + 1][x + 1] = 1 # bellow anti-ant - self.grid[y - 1][x] = 1 # above ant - - -# 1D / elementar automata - -class ElementarAutomata(FacePlateAnimation): - def __init__(self, direction="u", idx=0, seed=None, grid=None, bus=None): - super().__init__(grid, bus) - assert direction[0] in ["u", "d", "l", "r"] - self.direction = direction[0] - self.row = idx - self.initial_state(seed) - - def initial_state(self, seed=None): - if seed is not None: - line = seed - else: - line = [0 for i in range(self.width)] - self.grid[self.row] = line - - def rule(self): - # process the line - raise NotImplementedError - - def animate(self): - old = copy.deepcopy(self.grid) - new_line = self.rule() - if self.direction == "u": - self.move_up() - elif self.direction == "d": - self.move_down() - elif self.direction == "l": - self.move_left() - elif self.direction == "r": - self.move_right() - self.grid[self.row] = new_line - if old == self.grid: - self.stop() - - -class SierpinskiTriangle(ElementarAutomata): - def __init__(self, direction="u", seed=None, bus=None): - assert direction[0] in ["u", "d"] - if direction[0] == "u": - idx = -1 - else: - idx = 0 - super().__init__(direction, idx, seed=seed, bus=bus) - - def initial_state(self, seed=None): - if seed is not None: - line = seed - else: - line = [0 for i in range(self.width)] - idx = self.width // 2 - line[idx] = 1 - self.grid[self.row] = line - - def rule(self): - new_line = copy.deepcopy(self.grid[self.row]) - # handle middle - for i in range(1, self.width - 1): - left = self.grid[self.row][i - 1] - right = self.grid[self.row][i + 1] - if left and right: - new_line[i] = 0 - elif left or right: - new_line[i] = 1 + self.grid[y + 1][x + 1] = 1 # bellow anti-ant + self.grid[y - 1][x] = 1 # above ant + + + # 1D / elementar automata + + class ElementarAutomata(FacePlateAnimation): + def __init__(self, direction="u", idx=0, seed=None, grid=None, bus=None): + super().__init__(grid, bus) + assert direction[0] in ["u", "d", "l", "r"] + self.direction = direction[0] + self.row = idx + self.initial_state(seed) + + def initial_state(self, seed=None): + if seed is not None: + line = seed + else: + line = [0 for i in range(self.width)] + self.grid[self.row] = line + + def rule(self): + # process the line + raise NotImplementedError + + def animate(self): + old = copy.deepcopy(self.grid) + new_line = self.rule() + if self.direction == "u": + self.move_up() + elif self.direction == "d": + self.move_down() + elif self.direction == "l": + self.move_left() + elif self.direction == "r": + self.move_right() + self.grid[self.row] = new_line + if old == self.grid: + self.stop() + + + class SierpinskiTriangle(ElementarAutomata): + def __init__(self, direction="u", seed=None, bus=None): + assert direction[0] in ["u", "d"] + if direction[0] == "u": + idx = -1 + else: + idx = 0 + super().__init__(direction, idx, seed=seed, bus=bus) + + def initial_state(self, seed=None): + if seed is not None: + line = seed + else: + line = [0 for i in range(self.width)] + idx = self.width // 2 + line[idx] = 1 + self.grid[self.row] = line + + def rule(self): + new_line = copy.deepcopy(self.grid[self.row]) + # handle middle + for i in range(1, self.width - 1): + left = self.grid[self.row][i - 1] + right = self.grid[self.row][i + 1] + if left and right: + new_line[i] = 0 + elif left or right: + new_line[i] = 1 + else: + new_line[i] = 0 + + # handle edges + # if self.grid[0][1] == 1: + # new_line[0] = 1 + # else: + # new_line[0] = 0 + # if self.grid[0][-2] == 1: + # new_line[-1] = 1 + # else: + # new_line[-1] = 0 + return new_line + + + class Rule110(ElementarAutomata): + def __init__(self, direction="u", seed=None, bus=None): + assert direction[0] in ["u", "d"] + if direction[0] == "u": + idx = -1 + else: + idx = 0 + super().__init__(direction, idx, seed=seed, bus=bus) + + def initial_state(self, seed=None): + if seed is not None: + line = seed + else: + line = [0 for i in range(self.width)] + idx = self.width - 1 + line[idx] = 1 + self.grid[self.row] = line + + def rule(self): + new_line = copy.deepcopy(self.grid[self.row]) + # handle middle + for i in range(1, self.width - 1): + left = self.grid[self.row][i - 1] + mid = self.grid[self.row][i] + right = self.grid[self.row][i + 1] + if str(left) + str(mid) + str(right) in \ + ["110", "101", "011", "010", "001"]: + new_line[i] = 1 + else: + new_line[i] = 0 + + # handle edges + if self.grid[0][1] == 1: + new_line[0] = 1 else: - new_line[i] = 0 - - # handle edges - #if self.grid[0][1] == 1: - # new_line[0] = 1 - #else: - # new_line[0] = 0 - #if self.grid[0][-2] == 1: - # new_line[-1] = 1 - #else: - # new_line[-1] = 0 - return new_line - - -class Rule110(ElementarAutomata): - def __init__(self, direction="u", seed=None, bus=None): - assert direction[0] in ["u", "d"] - if direction[0] == "u": - idx = -1 - else: - idx = 0 - super().__init__(direction, idx, seed=seed, bus=bus) - - def initial_state(self, seed=None): - if seed is not None: - line = seed - else: - line = [0 for i in range(self.width)] - idx = self.width - 1 - line[idx] = 1 - self.grid[self.row] = line - - def rule(self): - new_line = copy.deepcopy(self.grid[self.row]) - # handle middle - for i in range(1, self.width - 1): - left = self.grid[self.row][i - 1] - mid = self.grid[self.row][i] - right = self.grid[self.row][i + 1] - if str(left) + str(mid) + str(right) in \ - ["110", "101", "011", "010", "001"]: - new_line[i] = 1 + new_line[0] = 0 + if self.grid[0][-2] == 1: + new_line[-1] = 1 else: - new_line[i] = 0 - - # handle edges - if self.grid[0][1] == 1: - new_line[0] = 1 - else: - new_line[0] = 0 - if self.grid[0][-2] == 1: - new_line[-1] = 1 - else: - new_line[-1] = 0 - return new_line - - -if __name__ == "__main__": - from time import sleep - from ovos_utils.messagebus import get_mycroft_bus - from time import sleep - - bus = get_mycroft_bus("192.168.1.70") - - a = Rule110(bus=bus) - a.print() - - for grid in a: - grid.print() - grid.display(invert=False) - sleep(0.5) \ No newline at end of file + new_line[-1] = 0 + return new_line diff --git a/ovos_utils/enclosure/mark1/faceplate/icons.py b/ovos_utils/enclosure/mark1/faceplate/icons.py index 7def1227..3db4ae83 100644 --- a/ovos_utils/enclosure/mark1/faceplate/icons.py +++ b/ovos_utils/enclosure/mark1/faceplate/icons.py @@ -1,235 +1,239 @@ -from ovos_utils.enclosure.mark1.faceplate import FaceplateGrid, BlackScreen - - -# drawing in python - - -class MusicIcon(BlackScreen): - str_grid = """ -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXX XXXXXXXXXXXXX -XXXXXXXXXXXXXX XXXXXXXXXXXXX -XXXXXXXXXXXXXX XXX XXXXXXXXXXXXX -XXXXXXXXXXXXXX XXX XXXXXXXXXXXXX -XXXXXXXXXXXXX XX XXXXXXXXXXXXX -XXXXXXXXXXXX X XXXXXXXXXXXXX -XXXXXXXXXXXXX XXX XXXXXXXXXXXXXX -""" - - -class PlusIcon(BlackScreen): - str_grid = """ -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXX XXXXXXXXXXXXXX -XXXXXXXXXXXXXXX XXXXXXXXXXXXXX -XXXXXXXXXXXXX XXXXXXXXXXXX -XXXXXXXXXXXXX XXXXXXXXXXXX -XXXXXXXXXXXXX XXXXXXXXXXXX -XXXXXXXXXXXXXXX XXXXXXXXXXXXXX -XXXXXXXXXXXXXXX XXXXXXXXXXXXXX -""" - - -class HeartIcon(FaceplateGrid): - str_grid = """ - - xx xx - xxxx xxxx - xxxxxxxxx - xxxxxxx - xxxxx - xxx - x -""" - - -class HollowHeartIcon(FaceplateGrid): - str_grid = """ - - xx xx - x x x x - x x x - x x - x x - x x - x -""" - - -class SkullIcon(FaceplateGrid): - str_grid = """ - - xxxxxxx - x xxx x - xxxxxxxxx - xxx xxx - xxxxx - x x x - x x x -""" - - -class DeadFishIcon(FaceplateGrid): - str_grid = """ - - x xxxx - x x x x xx xxx - xxxxxxxxxxxxxxx - x x x x xxxxxx - x xxxx -""" - - -class InfoIcon(BlackScreen): - str_grid = """ -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXX XXXXXXXXXXXXXX -XXXXXXXXXXXXXXX XXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXX XXXXXXXXXXXXXX -XXXXXXXXXXXXXXX XXXXXXXXXXXXXX -XXXXXXXXXXXXXXX XXXXXXXXXXXXXX -XXXXXXXXXXXXXXX XXXXXXXXXXXXX -""" - - -class ArrowLeftIcon(BlackScreen): - str_grid = """ -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXX XXXXXXXXXXXXXXXX -XXXXXXXXXXXX XXXXXXXXXXXXXXXXX -XXXXXXXXXXX X XXXXXXXXXX -XXXXXXXXXX X XXXXXXXXXX -XXXXXXXXXXX X XXXXXXXXXX -XXXXXXXXXXXX XXXXXXXXXXXXXXXXX -XXXXXXXXXXXXX XXXXXXXXXXXXXXXX -""" - - -class WarningIcon(BlackScreen): - str_grid = """ -XXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX -XXXXXXXXXXXXXX XXXXXXXXXXXXXXX -XXXXXXXXXXXXX X XXXXXXXXXXXXXX -XXXXXXXXXXXX XXX XXXXXXXXXXXXX -XXXXXXXXXXX XXX XXXXXXXXXXXX -XXXXXXXXXX XXXXXXXXXXX -XXXXXXXXX XXX XXXXXXXXXX -XXXXXXXX X XXXXXXXXX -""" - - -class CrossIcon(BlackScreen): - str_grid = """ -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXX XXXXX XXXXXXXXXXXX -XXXXXXXXXXXX XXX XXXXXXXXXXX -XXXXXXXXXXXXX X XXXXXXXXXXXX -XXXXXXXXXXXXXXX XXXXXXXXXXXXXX -XXXXXXXXXXXXX X XXXXXXXXXXXX -XXXXXXXXXXXX XXX XXXXXXXXXXX -XXXXXXXXXXXXX XXXXX XXXXXXXXXXXX -""" - - -class JarbasAI(BlackScreen): - str_grid = """ -X XXXXXXXXXXXXXXXXXXXXXXX X -XX XXXXXXXXXXXXXXXXXXXXXXXX X XX -XX XXXXXXXXXX XXXXXXXXXXXXX X X -XX XXXXXXXXXX XXXXXXXXX X X -XX XX X X X XX X XXX X X - X X XX X XX XX X XX X X X X - X X XX X XXX XX X XX XXX X X X - X X XXX X X X X X -""" - - -class SpaceInvader1(BlackScreen): - str_grid = """ -XXXXXXXXXXXXXX XXXXXXXXXXXXX -XXXXXXXXXXXXX XXXXXXXXXXXX -XXXXXXXXXX x x XXXXXXXXX -XXXXXXXXXX XX XX XXXXXXXXX -XXXXXXXXXXXXXX XXXXXXXXXXXXX -XXXXXXXXXX XXXXXXXXX -XXXXXXXXXX XXXXXXXXXXX XXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -""" - - -class SpaceInvader2(BlackScreen): - str_grid = """ -XXXXXXXXXXXXX XXXXXXXXXXXX -XXXXXXXXXXXX x x XXXXXXXXXXX -XXXXXXXXXXXXX XXXXXXXXXXXX -XXXXXXXXXX X X XXXXXXXXX -XXXXXXXXXX XXXXXXXXX -XXXXXXXXXX XXX XXX XXXXXXXXX -XXXXXXXXXXXXXXX X XXXXXXXXXXXXXX -XXXXXXXXXXXXXXX X XXXXXXXXXXXXXX -""" - - -class SpaceInvader3(BlackScreen): - str_grid = """ -XXXXXXXXXXXXX XXXXXXXXXXXX -XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXX -XXXXXXXXXXXXX XXXXXXXXXXX -XXXXXXXXXXXX X X XXXXXXXXXXX -XXXXXXXXXX XXXXXXXXX -XXXXXXXXXX XX XXX XX XXXXXXXXX -XXXXXXXXXXXXXX XXXXXXXXXXXXX -XXXXXXXXXXXXX XXX XXXXXXXXXXXX -""" - - -class SpaceInvader4(BlackScreen): - str_grid = """ -XXXXXXXXXXXXX XXXXXXXXXXXX -XXXXXXXXXXXXXXX XXXXXXXXXXXXXX -XXXXXXXXXXXXX XXXXXXXXXXX -XXXXXXXXXXXX X XXXXXXXXXXX -XXXXXXXXXX XXXXXXXXX -XXXXXXXXXX XX XX XXXXXXXXX -XXXXXXXXXXXXXX X XXXXXXXXXXXXX -XXXXXXXXXXXXX XXX XXXXXXXXXXXX -""" - - -# Encoded icons -class Boat(BlackScreen): - encoded = "QIAAABACAGIEMEOEPHAEAGACABABAAAAAA" - - -# Default weather icons for mark1 -class SunnyIcon(FaceplateGrid): - encoded = "IICEIBMDNLMDIBCEAA" - - -class PartlyCloudyIcon(FaceplateGrid): - encoded = "IIEEGBGDHLHDHBGEEA" - +from ovos_utils.log import LOG + +LOG.warning("ovos_utils.enclosure.mark1.faceplate moved to https://github.com/OpenVoiceOS/ovos-mark1-utils ;" + " this module will be removed in version 0.1.0") + +try: + from ovos_mark1.faceplate.icons import * +except: + from ovos_utils.enclosure.mark1.faceplate import FaceplateGrid, BlackScreen + + class MusicIcon(BlackScreen): + str_grid = """ + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXXX XXXXXXXXXXXXX + XXXXXXXXXXXXXX XXXXXXXXXXXXX + XXXXXXXXXXXXXX XXX XXXXXXXXXXXXX + XXXXXXXXXXXXXX XXX XXXXXXXXXXXXX + XXXXXXXXXXXXX XX XXXXXXXXXXXXX + XXXXXXXXXXXX X XXXXXXXXXXXXX + XXXXXXXXXXXXX XXX XXXXXXXXXXXXXX + """ + + + class PlusIcon(BlackScreen): + str_grid = """ + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXXXX XXXXXXXXXXXXXX + XXXXXXXXXXXXXXX XXXXXXXXXXXXXX + XXXXXXXXXXXXX XXXXXXXXXXXX + XXXXXXXXXXXXX XXXXXXXXXXXX + XXXXXXXXXXXXX XXXXXXXXXXXX + XXXXXXXXXXXXXXX XXXXXXXXXXXXXX + XXXXXXXXXXXXXXX XXXXXXXXXXXXXX + """ + + + class HeartIcon(FaceplateGrid): + str_grid = """ + + xx xx + xxxx xxxx + xxxxxxxxx + xxxxxxx + xxxxx + xxx + x + """ + + + class HollowHeartIcon(FaceplateGrid): + str_grid = """ + + xx xx + x x x x + x x x + x x + x x + x x + x + """ + + + class SkullIcon(FaceplateGrid): + str_grid = """ + + xxxxxxx + x xxx x + xxxxxxxxx + xxx xxx + xxxxx + x x x + x x x + """ + + + class DeadFishIcon(FaceplateGrid): + str_grid = """ + + x xxxx + x x x x xx xxx + xxxxxxxxxxxxxxx + x x x x xxxxxx + x xxxx + """ + + + class InfoIcon(BlackScreen): + str_grid = """ + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXXXX XXXXXXXXXXXXXX + XXXXXXXXXXXXXXX XXXXXXXXXXXXXX + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXXX XXXXXXXXXXXXXX + XXXXXXXXXXXXXXX XXXXXXXXXXXXXX + XXXXXXXXXXXXXXX XXXXXXXXXXXXXX + XXXXXXXXXXXXXXX XXXXXXXXXXXXX + """ + + + class ArrowLeftIcon(BlackScreen): + str_grid = """ + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXX XXXXXXXXXXXXXXXX + XXXXXXXXXXXX XXXXXXXXXXXXXXXXX + XXXXXXXXXXX X XXXXXXXXXX + XXXXXXXXXX X XXXXXXXXXX + XXXXXXXXXXX X XXXXXXXXXX + XXXXXXXXXXXX XXXXXXXXXXXXXXXXX + XXXXXXXXXXXXX XXXXXXXXXXXXXXXX + """ + + + class WarningIcon(BlackScreen): + str_grid = """ + XXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX + XXXXXXXXXXXXXX XXXXXXXXXXXXXXX + XXXXXXXXXXXXX X XXXXXXXXXXXXXX + XXXXXXXXXXXX XXX XXXXXXXXXXXXX + XXXXXXXXXXX XXX XXXXXXXXXXXX + XXXXXXXXXX XXXXXXXXXXX + XXXXXXXXX XXX XXXXXXXXXX + XXXXXXXX X XXXXXXXXX + """ + + + class CrossIcon(BlackScreen): + str_grid = """ + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + XXXXXXXXXXXXX XXXXX XXXXXXXXXXXX + XXXXXXXXXXXX XXX XXXXXXXXXXX + XXXXXXXXXXXXX X XXXXXXXXXXXX + XXXXXXXXXXXXXXX XXXXXXXXXXXXXX + XXXXXXXXXXXXX X XXXXXXXXXXXX + XXXXXXXXXXXX XXX XXXXXXXXXXX + XXXXXXXXXXXXX XXXXX XXXXXXXXXXXX + """ + + + class JarbasAI(BlackScreen): + str_grid = """ + X XXXXXXXXXXXXXXXXXXXXXXX X + XX XXXXXXXXXXXXXXXXXXXXXXXX X XX + XX XXXXXXXXXX XXXXXXXXXXXXX X X + XX XXXXXXXXXX XXXXXXXXX X X + XX XX X X X XX X XXX X X + X X XX X XX XX X XX X X X X + X X XX X XXX XX X XX XXX X X X + X X XXX X X X X X + """ + + + class SpaceInvader1(BlackScreen): + str_grid = """ + XXXXXXXXXXXXXX XXXXXXXXXXXXX + XXXXXXXXXXXXX XXXXXXXXXXXX + XXXXXXXXXX x x XXXXXXXXX + XXXXXXXXXX XX XX XXXXXXXXX + XXXXXXXXXXXXXX XXXXXXXXXXXXX + XXXXXXXXXX XXXXXXXXX + XXXXXXXXXX XXXXXXXXXXX XXXXXXXXX + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + """ + + + class SpaceInvader2(BlackScreen): + str_grid = """ + XXXXXXXXXXXXX XXXXXXXXXXXX + XXXXXXXXXXXX x x XXXXXXXXXXX + XXXXXXXXXXXXX XXXXXXXXXXXX + XXXXXXXXXX X X XXXXXXXXX + XXXXXXXXXX XXXXXXXXX + XXXXXXXXXX XXX XXX XXXXXXXXX + XXXXXXXXXXXXXXX X XXXXXXXXXXXXXX + XXXXXXXXXXXXXXX X XXXXXXXXXXXXXX + """ + + + class SpaceInvader3(BlackScreen): + str_grid = """ + XXXXXXXXXXXXX XXXXXXXXXXXX + XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXX + XXXXXXXXXXXXX XXXXXXXXXXX + XXXXXXXXXXXX X X XXXXXXXXXXX + XXXXXXXXXX XXXXXXXXX + XXXXXXXXXX XX XXX XX XXXXXXXXX + XXXXXXXXXXXXXX XXXXXXXXXXXXX + XXXXXXXXXXXXX XXX XXXXXXXXXXXX + """ + + + class SpaceInvader4(BlackScreen): + str_grid = """ + XXXXXXXXXXXXX XXXXXXXXXXXX + XXXXXXXXXXXXXXX XXXXXXXXXXXXXX + XXXXXXXXXXXXX XXXXXXXXXXX + XXXXXXXXXXXX X XXXXXXXXXXX + XXXXXXXXXX XXXXXXXXX + XXXXXXXXXX XX XX XXXXXXXXX + XXXXXXXXXXXXXX X XXXXXXXXXXXXX + XXXXXXXXXXXXX XXX XXXXXXXXXXXX + """ + + + # Encoded icons + class Boat(BlackScreen): + encoded = "QIAAABACAGIEMEOEPHAEAGACABABAAAAAA" + + + # Default weather icons for mark1 + class SunnyIcon(FaceplateGrid): + encoded = "IICEIBMDNLMDIBCEAA" + + + class PartlyCloudyIcon(FaceplateGrid): + encoded = "IIEEGBGDHLHDHBGEEA" + -class CloudyIcon(FaceplateGrid): - encoded = "IIIBMDMDODODODMDIB" + class CloudyIcon(FaceplateGrid): + encoded = "IIIBMDMDODODODMDIB" -class LightRainIcon(FaceplateGrid): - encoded = "IIMAOJOFPBPJPFOBMA" + class LightRainIcon(FaceplateGrid): + encoded = "IIMAOJOFPBPJPFOBMA" -class RainIcon(FaceplateGrid): - encoded = "IIMIOFOBPFPDPJOFMA" + class RainIcon(FaceplateGrid): + encoded = "IIMIOFOBPFPDPJOFMA" -class StormIcon(FaceplateGrid): - encoded = "IIAAIIMEODLBJAAAAA" + class StormIcon(FaceplateGrid): + encoded = "IIAAIIMEODLBJAAAAA" -class SnowIcon(FaceplateGrid): - encoded = "IIJEKCMBPHMBKCJEAA" + class SnowIcon(FaceplateGrid): + encoded = "IIJEKCMBPHMBKCJEAA" -class WindIcon(FaceplateGrid): - encoded = "IIABIBIBIJIJJGJAGA" + class WindIcon(FaceplateGrid): + encoded = "IIABIBIBIJIJJGJAGA"