Skip to content
Snippets Groups Projects
Commit 18964bde authored by bav6096's avatar bav6096
Browse files

refactoring

parent 2c09a192
Branches
Tags
No related merge requests found
Showing
with 143 additions and 179 deletions
#!/usr/bin/env python
import re
import importlib.resources
from visualising.illustration.animation.color.rgb import RGB
from visualising.illustration.animation.pixel import Pixel
from visualising.illustration.animation.frame import Frame
class Ensemble:
def __init__(self, l_frame, r_frame):
self.l_frame = l_frame
self.r_frame = r_frame
def illuminated(self, index):
if not 0 <= index <= 31:
raise ValueError("The parameter index must be an integer between 0 and 31!")
if index < 16:
return self.l_frame.illuminated(index)
else:
return self.r_frame.illuminated(index - 16)
def replace_pixel(self, index, pixel):
if not 0 <= index <= 31:
raise ValueError("The parameter index must be an integer between 0 and 31!")
......@@ -12,3 +28,13 @@ class Ensemble:
self.l_frame.replace_pixel(index, pixel)
else:
self.r_frame.replace_pixel(index - 16, pixel)
@staticmethod
def empty():
l_pixels = []
r_pixels = []
for _ in range(16):
l_pixels.append(Pixel(RGB(0, 0, 0)))
r_pixels.append(Pixel(RGB(0, 0, 0)))
return Ensemble(Frame(l_pixels), Frame(r_pixels))
......@@ -17,9 +17,9 @@ class Frame:
def illuminated(self, index):
if not 0 <= index <= 15:
raise ValueError("The parameter index must be an integer between 0 and 15!")
return self._pixels[index].color.illuminated
return self.pixels[index].color.illuminated
def replace_pixel(self, index, pixel):
if not 0 <= index <= 15:
raise ValueError("The parameter index must be an integer between 0 and 15!")
self._pixels[index] = pixel
self.pixels[index] = pixel
......@@ -2,7 +2,6 @@
import time
from visualising.communication.channel.connection import Connection
from visualising.communication.channel.message.msg_frame import MsgFrame
from visualising.communication.channel.message.msg_instr import MsgInstr
from visualising.communication.channel.message.response import Response
......@@ -11,53 +10,43 @@ from visualising.communication.illustration.animation import Animation
class Arduino:
def __init__(self, connection):
self._connection = connection
@property
def connection(self):
return self._connection
@connection.setter
def connection(self, connection):
if not isinstance(connection, Connection):
raise TypeError("The parameter connection must be an object of the Connection class!")
self._connection = connection
self.connection = connection
# Creates instruction message and sends it to the Arduino,
# to play a loaded animation.
def start_playback(self):
self._connection.confirm_msg(MsgInstr('B'), [Response(b'\x1f')], 5)
self.connection.confirm_msg(MsgInstr('B'), [Response(b'\x1f')], 5)
# Creates instruction message and sends it to the Arduino,
# to pause a playing animation.
def pause_playback(self):
self._connection.confirm_msg(MsgInstr('C'), [Response(b'\x5f')], 5)
self.connection.confirm_msg(MsgInstr('C'), [Response(b'\x5f')], 5)
# Creates instruction message and sends it to the Arduino,
# to resume playback of a loaded animation.
def resume_playback(self):
self._connection.confirm_msg(MsgInstr('D'), [Response(b'\x6f')], 5)
self.connection.confirm_msg(MsgInstr('D'), [Response(b'\x6f')], 5)
# Creates instruction message and sends it to the Arduino,
# to clear the NeoPixel rings.
def reset_display(self):
self._connection.confirm_msg(MsgInstr('E'), [Response(b'\x7f')], 5)
self.connection.confirm_msg(MsgInstr('E'), [Response(b'\x7f')], 5)
# Creates one instruction messages and multiple frame messages to load an animation
# onto the Arduino.
def load_animation(self, animation):
if not isinstance(animation, Animation) or len(animation.ensembles) > 16:
raise ValueError("The animation parameter must be an object of the Animation class, which may contain a "
"maximum of 16 ensembles!")
if len(animation.ensembles) > 16:
raise ValueError("The animation parameter must be an object of the Animation class, "
"which may contain a maximum of 16 ensembles!")
self._connection.confirm_msg(MsgInstr('A', animation), [Response(b'\x0f')], 5)
self.connection.confirm_msg(MsgInstr('A', animation), [Response(b'\x0f')], 5)
for ensemble in animation.ensembles:
l_frame = ensemble.l_frame
r_frame = ensemble.r_frame
self._connection.confirm_msg(MsgFrame(l_frame), [Response(b'\x2f'), Response(b'\x3f')], 10)
self._connection.confirm_msg(MsgFrame(r_frame), [Response(b'\x2f'), Response(b'\x3f')], 10)
self.connection.confirm_msg(MsgFrame(l_frame), [Response(b'\x2f'), Response(b'\x3f')], 10)
self.connection.confirm_msg(MsgFrame(r_frame), [Response(b'\x2f'), Response(b'\x3f')], 10)
# First, an animation is loaded onto the Arduino. The playback of this
# animation is then started. In addition, it is checked whether the
......@@ -74,7 +63,7 @@ class Arduino:
# Current time in seconds.
curr_time = time.time()
while not Response(b'\x4f').compare(self._connection.receive_msg()):
while not Response(b'\x4f').compare(self.connection.receive_msg()):
# Wait 10 times the estimated animation playback time for a response.
if time.time() > curr_time + playback_time * 10:
raise ArduinoException("Successful playback of animation can't be confirmed!")
......@@ -91,6 +80,7 @@ class Arduino:
for ensemble in animation.ensembles:
self.play_animation(Animation([ensemble], animation.ensemble_time, 1))
# Streams multiple animations in succession to the Arduino.
def stream_animations(self, animations):
for animation in animations:
self.stream_animation(animation)
......@@ -22,7 +22,7 @@ class MsgInstr(Message):
# actual message, most of the message consists only of zeros. The first and last bit
# are intended for authentication as an instruction message.
def export_message(self):
message = [bytes('[', 'ascii'), bytes(self._instr, 'ascii')]
message = [bytes('[', 'ascii'), bytes(self.instr, 'ascii')]
if self.animation is not None:
num_frames = len(self.animation.ensembles) * 2
......
import time
from visualising.communication.arduino import Arduino
from visualising.communication.illustration.animation import Animation
from visualising.expression.library.raw import Raw
from visualising.communication.illustration.ensemble import Ensemble
from visualising.communication.illustration.color.rgb import RGB
from visualising.expression.expression import Expression
from visualising.expression.tool import Tool
......@@ -9,7 +9,7 @@ from visualising.expression.tool import Tool
class Emotion(Expression):
def __init__(self, arduino):
self._arduino = arduino
self.arduino = arduino
self.playback_start = 0
self.refresh = 5
......@@ -17,16 +17,6 @@ class Emotion(Expression):
self.ok = "expression_ok.txt"
self.angry = "expression_angry.txt"
@property
def arduino(self):
return self._arduino
@arduino.setter
def arduino(self, arduino):
if not isinstance(arduino, Arduino):
raise TypeError("The parameter arduino must be an object of the Arduino class!")
self._arduino = arduino
def react(self, state):
if time.time() - self.playback_start > self.refresh:
color = Tool.state_to_color(state[0], RGB(25, 25, 25), RGB(25, 0, 0))
......@@ -39,53 +29,36 @@ class Emotion(Expression):
self.visualise(self.happy, color)
def visualise(self, filepath, color):
ensembles = Tool.create_ensembles(filepath, "emotion")
self.arduino.stream_animation(Emotion.build_emotion_parallel(ensembles[0], 50, color))
still = Raw.to_ensemble_list(filepath, "emotion")
buildup = Tool.build_emotion(still[0], color)
animation = Animation(buildup, 50, 1)
self.arduino.stream_animation(animation)
self.playback_start = time.time()
@staticmethod
def build_emotion(blueprint, time, color):
def build_emotion(still, color):
ensembles = []
for i in range(32):
ensemble = Tool.create_empty_ensemble()
for i in range(32 + 1):
empty = Ensemble.empty()
for j in range(i + 1):
if j < 16:
illuminated = blueprint.l_frame.illuminated(j)
else:
illuminated = bluepring.r_frame.illuminated(j % 16)
if illuminated or j == i:
ensemble = Tool.set_pixel_color(ensemble, j, color)
ensembles.append(ensemble)
ensemble = Tool.create_empty_ensemble()
for k in range(32):
if k < 16:
illuminated = blueprint.l_frame.illuminated(k)
else:
illuminated = blueprint.r_frame.illuminated(k % 16)
if illuminated:
ensemble = Tool.set_pixel_color(ensemble, k, color)
ensembles.append(ensemble)
return Animation(ensembles, time, 1)
if still.illuminated(j) or (j == i and i != 32):
empty.replace_pixel(j, Pixel(color))
ensembles.append(empty)
return ensembles
@staticmethod
def build_emotion_parallel(blueprint, time, color):
def build_emotion_parallel(still, color):
ensembles = []
for i in range(16):
ensemble = Tool.create_empty_ensemble()
for i in range(16 + 1):
empty = Ensemble.empty()
for j in range(i + 1):
if blueprint.l_frame.illuminated(j):
ensemble = Tool.set_pixel_color(ensemble, j, color)
if blueprint.r_frame.illuminated(j):
ensemble = Tool.set_pixel_color(ensemble, 16 + j, color)
if still.l_frame.illuminated(j):
empty.replace_pixel(j, Pixel(color))
if still.r_frame.illuminated(j):
empty.replace_pixel(j, Pixel(color))
if j == i:
ensemble = Tool.set_pixel_color(ensemble, j, color)
ensemble = Tool.set_pixel_color(ensemble, 16 + j, color)
empty.replace_pixel(j, Pixel(color))
empty.replace_pixel(16 + j, Pixel(color))
ensembles.append(ensemble)
......
#!/usr/bin/env python
import re
import importlib.resources
from visualising.illustration.animation.color.rgb import RGB
from visualising.illustration.animation.pixel import Pixel
from visualising.illustration.animation.frame import Frame
from visualising.illustration.animation.ensemble import Ensemble
from visualising.illustration.animation.animation import Animation
from visualising.expression.library import alphabet
from visualising.expression.library import expression
class Raw:
def __init__(self):
pass
@staticmethod
def to_ensemble_list(filename, directory):
def load():
if directory != "expression" and directory != "alphabet":
raise ValueError("The parameter directory describes a unknown directory!")
if directory == "expression":
return importlib.resources.open_text(emotion, filename)
if directory == "alphabet":
return importlib.resources.open_text(alphabet, filename)
def to_pixel_list(line):
pixels = []
for (r, g, b) in line.split(","):
pixels.append(
Pixel(
RGB(
int(re.search(r'\d+', r).group()),
int(re.search(r'\d+', g).group()),
int(re.search(r'\d+', b).group())
)
)
)
return pixels
file = load()
ensembles = []
while True:
l_line = file.readline()
r_line = file.readline()
if bool(l_line) != bool(r_line):
raise ValueError("Animation must have an even number of frames!")
if not l_line and not r_line:
break
l_frame = Frame(to_pixel_list(l_line))
r_frame = Frame(to_pixel_list(r_line))
ensembles.append(Ensemble(l_frame, r_frame))
return ensembles
@staticmethod
def to_animation(filename, directory, ensemble_time, num_iter):
ensemble_list = Raw.to_ensemble_list(filename, directory)
return Animation(ensemble_list, ensemble_time, num_iter)
#!/usr/bin/env python
import re
import numpy as np
import importlib.resources
from visualising.communication.illustration.animation import Animation
from visualising.communication.illustration.ensemble import Ensemble
from visualising.communication.illustration.frame import Frame
from visualising.communication.illustration.pixel import Pixel
from visualising.communication.illustration.color.rgb import RGB
from visualising.expression.library import alphabet
from visualising.expression.library import emotion
class Tool:
def __init__(self):
pass
@staticmethod
def read_raw_ensembles(filename, directory):
def read_raw_frame(line):
raw_frame = []
for value in line.split(","):
raw_frame.append(
int(re.search(r'\d+', value).group())
)
return raw_frame
if directory == "emotion":
file = importlib.resources.open_text(emotion, filename)
elif directory == "alphabet":
file = importlib.resources.open_text(alphabet, filename)
else:
raise ValueError("The parameter directory describes a unknown directory!")
raw_ensembles = []
while True:
l_line = file.readline()
r_line = file.readline()
if bool(l_line) != bool(r_line):
raise ValueError("Animation must have an even number of frames!")
if not l_line and not r_line:
break
l_raw_frame = read_raw_frame(l_line)
r_raw_frame = read_raw_frame(r_line)
raw_ensembles.append([l_raw_frame, r_raw_frame])
return raw_ensembles
@staticmethod
def create_ensembles(filename, directory):
def create_frame(raw_pixels):
if len(raw_pixels) != 48:
raise ValueError("Frame must have exactly 48 values!")
pixels = []
i = 0
while i < 48:
r = raw_pixels[i + 0]
g = raw_pixels[i + 1]
b = raw_pixels[i + 2]
pixels.append(Pixel(RGB(r, g, b)))
i = i + 3
return Frame(pixels)
raw_ensembles = Tool.read_raw_ensembles(filename, directory)
ensembles = []
for raw_ensemble in raw_ensembles:
if len(raw_ensemble) != 2:
raise ValueError("Ensemble must have exactly two frames!")
l_frame = create_frame(raw_ensemble[0])
r_frame = create_frame(raw_ensemble[1])
ensembles.append(Ensemble(l_frame, r_frame))
return ensembles
@staticmethod
def create_animation(filename, directory, ensemble_time, iterations):
ensembles = Tool.create_ensembles(filename, directory)
return Animation(ensembles, ensemble_time, iterations)
@staticmethod
def create_empty_ensemble():
l_pixels = []
r_pixels = []
for _ in range(16):
l_pixels.append(Pixel(RGB(0, 0, 0)))
r_pixels.append(Pixel(RGB(0, 0, 0)))
return Ensemble(Frame(l_pixels), Frame(r_pixels))
@staticmethod
def set_pixel_color(ensemble, index, color):
if not isinstance(index, int) or not 0 <= index <= 31:
raise ValueError("The parameter index must be an integer between 0 and 31!")
if index < 16:
ensemble.l_frame.replace_pixel(index, Pixel(color))
else:
index = index - 16
ensemble.r_frame.replace_pixel(index, Pixel(color))
return ensemble
@staticmethod
def state_to_color(state, start_color, end_color):
if not 0 <= state <= 1:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment