Initial commit of yaml parse test.
A complete new controll system. Still some bugs remain
This commit is contained in:
parent
2530dd51be
commit
6403cad191
20 changed files with 1294 additions and 0 deletions
21
yaml parse test/.vscode/c_cpp_properties.json
vendored
Normal file
21
yaml parse test/.vscode/c_cpp_properties.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Win32",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"defines": [
|
||||
"_DEBUG",
|
||||
"UNICODE",
|
||||
"_UNICODE"
|
||||
],
|
||||
"windowsSdkVersion": "10.0.22000.0",
|
||||
"compilerPath": "cl.exe",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "windows-msvc-x64"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
4
yaml parse test/Dockerfile
Normal file
4
yaml parse test/Dockerfile
Normal file
|
@ -0,0 +1,4 @@
|
|||
FROM ghcr.io/cross-rs/armv7-unknown-linux-gnueabihf:main
|
||||
RUN dpkg --add-architecture armhf
|
||||
RUN apt-get update
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -yq python3.9:armhf python3.9-dev:armhf libpython3.9-dev:armhf
|
272
yaml parse test/Point2D.py
Normal file
272
yaml parse test/Point2D.py
Normal file
|
@ -0,0 +1,272 @@
|
|||
import hashlib
|
||||
import json
|
||||
import os.path
|
||||
import math
|
||||
from scipy.optimize import linear_sum_assignment
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import fastproot
|
||||
|
||||
|
||||
# location to store array cache
|
||||
CACHE_FILE_PATH = "./cache//point_array_cache.json"
|
||||
LOADED_IMAGE_PATH = "./graphics/loaded.png"
|
||||
|
||||
CACHE_FILE_PATH = os.path.normpath(CACHE_FILE_PATH)
|
||||
LOADED_IMAGE_PATH = os.path.normpath(LOADED_IMAGE_PATH)
|
||||
|
||||
class Point2D:
|
||||
x = 0
|
||||
y = 0
|
||||
color: tuple[int, int, int] = (0, 0, 0)
|
||||
|
||||
def __init__(self, x, y, color: tuple[int, int, int] = (0, 0, 0)):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.color = color
|
||||
|
||||
def round(self):
|
||||
self.x = round(self.x)
|
||||
self.y = round(self.y)
|
||||
return self
|
||||
|
||||
def distance(self, other):
|
||||
dx = self.x - other.x
|
||||
dy = self.y - other.y
|
||||
return math.sqrt(dx ** 2 + dy ** 2)
|
||||
|
||||
def interpolate(self, other, percentage):
|
||||
new_x = self.x + (other.x - self.x) * percentage
|
||||
new_y = self.y + (other.y - self.y) * percentage
|
||||
new_color = tuple(int((1 - percentage) * self.color[i] + percentage * other.color[i]) for i in range(3))
|
||||
return Point2D(new_x, new_y, new_color)
|
||||
|
||||
def __eq__(self, other):
|
||||
return (self.x, self.y) == (other.x, other.y)
|
||||
|
||||
|
||||
def divide_points_into_groups(points):
|
||||
result = fastproot.divide_into_groups(points)
|
||||
res = [[Point2D(p[0], p[1], p[2]) for p in arr] for arr in result]
|
||||
return res
|
||||
|
||||
def _divide_points_into_groups(points):
|
||||
def flood_fill(point, group):
|
||||
if point not in group:
|
||||
group.append(point)
|
||||
|
||||
neighbors = [(point.x + 1, point.y), (point.x - 1, point.y), (point.x, point.y + 1), (point.x, point.y - 1)]
|
||||
for neighbor_coords in neighbors:
|
||||
neighbor = next((p for p in points if p.x == neighbor_coords[0] and p.y == neighbor_coords[1]), None)
|
||||
if neighbor and neighbor not in group:
|
||||
flood_fill(neighbor, group)
|
||||
|
||||
groups = []
|
||||
remaining_points = [point for point in points if point.x < 64] # Filter points with x < 64
|
||||
|
||||
while remaining_points:
|
||||
group = []
|
||||
flood_fill(remaining_points[0], group)
|
||||
groups.append(group)
|
||||
|
||||
# Remove points in the group from the remaining points
|
||||
remaining_points = [point for point in remaining_points if point not in group]
|
||||
|
||||
return groups
|
||||
|
||||
|
||||
def compute_bounding_box(points: list[Point2D]) -> tuple[float, float, float, float]:
|
||||
min_x = min(point.x for point in points)
|
||||
max_x = max(point.x for point in points)
|
||||
min_y = min(point.y for point in points)
|
||||
max_y = max(point.y for point in points)
|
||||
return min_x, max_x, min_y, max_y
|
||||
|
||||
def calculate_distance(points1: list[Point2D], points2: list[Point2D]) -> float:
|
||||
# Calculate the distance between the centers of two groups
|
||||
center1_x = (points1[0].x + points1[-1].x) / 2
|
||||
center1_y = (points1[0].y + points1[-1].y) / 2
|
||||
center2_x = (points2[0].x + points2[-1].x) / 2
|
||||
center2_y = (points2[0].y + points2[-1].y) / 2
|
||||
return ((center1_x - center2_x) ** 2 + (center1_y - center2_y) ** 2) ** 0.5
|
||||
|
||||
def pair_groups(set_a: list[list[Point2D]], set_b: list[list[Point2D]]) -> list[tuple[list[Point2D], list[Point2D]]]:
|
||||
pairs = []
|
||||
|
||||
# Create dictionaries to store bounding boxes for each group
|
||||
bounding_boxes_a: dict[int, tuple[float, float, float, float]] = {}
|
||||
bounding_boxes_b: dict[int, tuple[float, float, float, float]] = {}
|
||||
|
||||
# Calculate bounding boxes for all groups in both sets
|
||||
for i, group in enumerate(set_a + set_b):
|
||||
bounding_box = compute_bounding_box(group)
|
||||
if i < len(set_a):
|
||||
bounding_boxes_a[i] = bounding_box
|
||||
else:
|
||||
bounding_boxes_b[i - len(set_a)] = bounding_box
|
||||
|
||||
# Check for overlaps and determine pairs
|
||||
for i, group_a in enumerate(set_a):
|
||||
overlap_detected = False
|
||||
for j, group_b in enumerate(set_b):
|
||||
bounding_box_a = bounding_boxes_a[i]
|
||||
bounding_box_b = bounding_boxes_b[j]
|
||||
if (
|
||||
bounding_box_a[0] <= bounding_box_b[1] and
|
||||
bounding_box_a[1] >= bounding_box_b[0] and
|
||||
bounding_box_a[2] <= bounding_box_b[3] and
|
||||
bounding_box_a[3] >= bounding_box_b[2]
|
||||
):
|
||||
pairs.append((group_a, group_b))
|
||||
overlap_detected = True
|
||||
break
|
||||
|
||||
if not overlap_detected:
|
||||
# Find the nearest neighbor in set B
|
||||
nearest_group = min(set_b, key=lambda group: calculate_distance(group_a, group))
|
||||
pairs.append((group_a, nearest_group))
|
||||
|
||||
return pairs
|
||||
|
||||
def mirror_points(points: list[Point2D]) -> list[Point2D]:
|
||||
mirrored_points = []
|
||||
for point in points:
|
||||
mirrored_x = 128 - point.x # Calculate the mirrored x-coordinate
|
||||
mirrored_point = Point2D(mirrored_x, point.y, point.color)
|
||||
mirrored_points.append(mirrored_point)
|
||||
return mirrored_points
|
||||
|
||||
|
||||
def get_image_hash(image):
|
||||
image_hash = hashlib.sha1(image.tobytes()).hexdigest()
|
||||
return image_hash
|
||||
|
||||
|
||||
def load_cached_point_arrays():
|
||||
cached_point_arrays = {}
|
||||
|
||||
if os.path.isfile(CACHE_FILE_PATH):
|
||||
with open(CACHE_FILE_PATH, "r") as file:
|
||||
cached_point_arrays = json.load(file)
|
||||
|
||||
return cached_point_arrays
|
||||
|
||||
|
||||
def save_cached_point_arrays(cached_point_arrays):
|
||||
if not os.path.isfile(CACHE_FILE_PATH):
|
||||
open(CACHE_FILE_PATH, "x").close()
|
||||
|
||||
with open(CACHE_FILE_PATH, "w") as file:
|
||||
json.dump(cached_point_arrays, file)
|
||||
|
||||
|
||||
def generate_point_array_from_image(image):
|
||||
image.load()
|
||||
image = image.convert("RGB") # Convert image to RGB color mode
|
||||
image_hash = get_image_hash(image)
|
||||
cached_point_arrays = load_cached_point_arrays()
|
||||
|
||||
if image_hash in cached_point_arrays:
|
||||
print("Found existing point array matching png. Using existing array.")
|
||||
return [Point2D(point["x"], point["y"], tuple(point["color"])) for point in cached_point_arrays[image_hash]]
|
||||
|
||||
print("No existing point array matching png found. Generating now.")
|
||||
width, height = image.size
|
||||
pixel_array = []
|
||||
|
||||
image.save(LOADED_IMAGE_PATH)
|
||||
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
pixel = image.getpixel((x, y))
|
||||
if sum(pixel) > 15: # any pixel which total color value is greater than 15.
|
||||
point = {"x": x, "y": y, "color": pixel}
|
||||
pixel_array.append(point)
|
||||
|
||||
cached_point_arrays[image_hash] = pixel_array
|
||||
save_cached_point_arrays(cached_point_arrays)
|
||||
|
||||
print("Point array generated and stored.")
|
||||
return [Point2D(point["x"], point["y"], tuple(point["color"])) for point in pixel_array]
|
||||
|
||||
|
||||
def generate_image_from_point_array(points: list[Point2D], width: int, height: int) -> Image:
|
||||
# Create a new blank image
|
||||
image = Image.new("RGB", (width, height), "black")
|
||||
|
||||
# Set the pixels corresponding to the points as white
|
||||
pixels = image.load()
|
||||
for point in points:
|
||||
point = point.round()
|
||||
x = point.x
|
||||
y = point.y
|
||||
pixels[x, y] = point.color
|
||||
|
||||
return image
|
||||
|
||||
|
||||
def interpolate_point_pairs(pairs: list[tuple[Point2D, Point2D]], percentage: float) -> list[Point2D]:
|
||||
interpolated_points:list[Point2D] = []
|
||||
for pair in pairs:
|
||||
point1, point2 = pair
|
||||
interpolated_point = point1.interpolate(point2, percentage)
|
||||
interpolated_points.append(interpolated_point)
|
||||
return interpolated_points
|
||||
|
||||
|
||||
def pair_points(points1: list[Point2D], points2: list[Point2D]) -> list[tuple[Point2D, Point2D]]:
|
||||
# Update the size of the point arrays
|
||||
size1 = len(points1)
|
||||
size2 = len(points2)
|
||||
|
||||
# Duplicate points in the smaller array to match the size of the larger array
|
||||
if size1 > size2:
|
||||
num_duplicates = size1 - size2
|
||||
duplicated_points = np.random.choice(points2, size=num_duplicates).tolist()
|
||||
points2 += duplicated_points
|
||||
elif size2 > size1:
|
||||
num_duplicates = size2 - size1
|
||||
duplicated_points = np.random.choice(points1, size=num_duplicates).tolist()
|
||||
points1 += duplicated_points
|
||||
|
||||
row_ind, col_ind = fastproot.solve(points1, points2)
|
||||
|
||||
# Create pairs of points based on the optimal assignment
|
||||
pairs = []
|
||||
for i, j in zip(row_ind, col_ind):
|
||||
pairs.append((points1[i], points2[j]))
|
||||
|
||||
return pairs
|
||||
|
||||
|
||||
|
||||
def _pair_points(points1: list[Point2D], points2: list[Point2D]) -> list[tuple[Point2D, Point2D]]:
|
||||
# Update the size of the point arrays
|
||||
size1 = len(points1)
|
||||
size2 = len(points2)
|
||||
|
||||
# Duplicate points in the smaller array to match the size of the larger array
|
||||
if size1 > size2:
|
||||
num_duplicates = size1 - size2
|
||||
duplicated_points = np.random.choice(points2, size=num_duplicates).tolist()
|
||||
points2 += duplicated_points
|
||||
elif size2 > size1:
|
||||
num_duplicates = size2 - size1
|
||||
duplicated_points = np.random.choice(points1, size=num_duplicates).tolist()
|
||||
points1 += duplicated_points
|
||||
|
||||
# Create a new cost matrix with the updated sizes
|
||||
cost_matrix = np.zeros((size1, size2))
|
||||
for i in range(size1):
|
||||
for j in range(size2):
|
||||
cost_matrix[i, j] = points1[i].distance(points2[j])
|
||||
|
||||
# Solve the assignment problem using the Hungarian algorithm
|
||||
row_ind, col_ind = linear_sum_assignment(cost_matrix)
|
||||
|
||||
# Create pairs of points based on the optimal assignment
|
||||
pairs = []
|
||||
for i, j in zip(row_ind, col_ind):
|
||||
pairs.append((points1[i], points2[j]))
|
||||
|
||||
return pairs
|
5
yaml parse test/README.md
Normal file
5
yaml parse test/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
Crosscompile instructions:
|
||||
* Build docker image with python armv7 installed:
|
||||
`docker build -t my-image .`
|
||||
* Use cross to crosscompile:
|
||||
`cross.exe build --target=armv7-unknown-linux-gnueabihf --release`
|
BIN
yaml parse test/__pycache__/Point2D.cpython-311.pyc
Normal file
BIN
yaml parse test/__pycache__/Point2D.cpython-311.pyc
Normal file
Binary file not shown.
BIN
yaml parse test/animation1_frame1.png
Normal file
BIN
yaml parse test/animation1_frame1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 306 B |
BIN
yaml parse test/animation1_frame2.png
Normal file
BIN
yaml parse test/animation1_frame2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 306 B |
1
yaml parse test/cache/point_array_cache.json
vendored
Normal file
1
yaml parse test/cache/point_array_cache.json
vendored
Normal file
File diff suppressed because one or more lines are too long
67
yaml parse test/devideIntoGroups.c
Normal file
67
yaml parse test/devideIntoGroups.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include <Python.h>
|
||||
|
||||
static PyObject* divide_points_into_groups(PyObject* self, PyObject* args) {
|
||||
PyObject* points_list; // Input list of points
|
||||
if (!PyArg_ParseTuple(args, "O", &points_list)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expected a list of points.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check if the input is a list
|
||||
if (!PyList_Check(points_list)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Input must be a list of points.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create a Python list to store groups
|
||||
PyObject* groups_list = PyList_New(0);
|
||||
|
||||
// Iterate through the input points and divide them into groups
|
||||
// (Simplified logic - you should implement your own logic here)
|
||||
// In this example, we assume points are (x, y) pairs as Python tuples
|
||||
// and groups are lists of points.
|
||||
Py_ssize_t num_points = PyList_Size(points_list);
|
||||
PyObject* current_group = PyList_New(0);
|
||||
|
||||
for (Py_ssize_t i = 0; i < num_points; i++) {
|
||||
PyObject* point = PyList_GetItem(points_list, i);
|
||||
PyObject* x_obj = PyTuple_GetItem(point, 0);
|
||||
PyObject* y_obj = PyTuple_GetItem(point, 1);
|
||||
|
||||
// Process the point (x, y) here
|
||||
// (You should implement your grouping logic)
|
||||
|
||||
// For simplicity, we add points to the current group
|
||||
PyList_Append(current_group, point);
|
||||
|
||||
// Assume a condition for creating a new group (e.g., x > 50)
|
||||
if (PyLong_AsLong(x_obj) > 50) {
|
||||
PyList_Append(groups_list, current_group);
|
||||
current_group = PyList_New(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Append the last group if it's not empty
|
||||
if (PyList_Size(current_group) > 0) {
|
||||
PyList_Append(groups_list, current_group);
|
||||
}
|
||||
|
||||
return groups_list;
|
||||
}
|
||||
|
||||
static PyMethodDef methods[] = {
|
||||
{"divide_points_into_groups", divide_points_into_groups, METH_VARARGS, "Divide points into groups."},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
static struct PyModuleDef module = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"point_extension",
|
||||
NULL,
|
||||
-1,
|
||||
methods
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC PyInit_point_extension(void) {
|
||||
return PyModule_Create(&module);
|
||||
}
|
BIN
yaml parse test/dizzyFace.png
Normal file
BIN
yaml parse test/dizzyFace.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1,023 B |
BIN
yaml parse test/eyesClosed_neutral.png
Normal file
BIN
yaml parse test/eyesClosed_neutral.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 277 B |
317
yaml parse test/fastproot/Cargo.lock
generated
Normal file
317
yaml parse test/fastproot/Cargo.lock
generated
Normal file
|
@ -0,0 +1,317 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastproot"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"lsap",
|
||||
"pyo3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.148"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lsap"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68f4e260c26278f22a66a40b147d6e37ba24c6528210f11226c5ad6de567a774"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e681a6cfdc4adcc93b4d3cf993749a4552018ee0a9b65fc0ccfad74352c72a38"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"indoc",
|
||||
"libc",
|
||||
"memoffset",
|
||||
"parking_lot",
|
||||
"pyo3-build-config",
|
||||
"pyo3-ffi",
|
||||
"pyo3-macros",
|
||||
"unindent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-build-config"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "076c73d0bc438f7a4ef6fdd0c3bb4732149136abd952b110ac93e4edb13a6ba5"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-ffi"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e53cee42e77ebe256066ba8aa77eff722b3bb91f3419177cf4cd0f304d3284d9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pyo3-build-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfeb4c99597e136528c6dd7d5e3de5434d1ceaf487436a3f03b2d56b6fc9efd1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-macros-backend",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros-backend"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "947dc12175c254889edc0c02e399476c2f652b4b9ebd123aa655c224de259536"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c"
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
18
yaml parse test/fastproot/Cargo.toml
Normal file
18
yaml parse test/fastproot/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "fastproot"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
lsap = "1.0.2"
|
||||
pyo3 = { version = "0.19.2", features = ["abi3-py39"] }
|
||||
|
||||
[lib]
|
||||
name = "fastproot"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.cross.target.armv7-unknown-linux-gnueabihf]
|
||||
image = "my-image"
|
||||
pre-build = ["ln -s /usr/bin/python3.9 /usr/bin/python"]
|
131
yaml parse test/fastproot/src/lib.rs
Normal file
131
yaml parse test/fastproot/src/lib.rs
Normal file
|
@ -0,0 +1,131 @@
|
|||
use pyo3::prelude::*;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, FromPyObject)]
|
||||
pub struct Point {
|
||||
x: u8,
|
||||
y: u8,
|
||||
|
||||
color: (u8, u8, u8),
|
||||
}
|
||||
|
||||
type TuplePoint = (u8,u8, (u8,u8,u8));
|
||||
|
||||
impl Point {
|
||||
fn distance(self, other: Point) -> f64 {
|
||||
let dx = self.x as f64 - other.x as f64;
|
||||
let dy = self.y as f64 - other.y as f64;
|
||||
(dx.powf(2.0) + dy.powf(2.0)).sqrt()
|
||||
}
|
||||
|
||||
fn into_tuple(&self) -> TuplePoint {
|
||||
(self.x, self.y, self.color)
|
||||
}
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
pub fn solve(points1: Vec<Point>, points2: Vec<Point>) -> (Vec<usize>, Vec<usize>) {
|
||||
let mut cost_matrix = Vec::with_capacity(points1.len() * points2.len());
|
||||
|
||||
for i in &points1 {
|
||||
for j in &points2 {
|
||||
cost_matrix.push(i.distance(*j))
|
||||
}
|
||||
}
|
||||
|
||||
lsap::solve(points1.len(), points2.len(), &cost_matrix, false).unwrap()
|
||||
}
|
||||
|
||||
pub fn flood_fill(point: Point, mut group: Vec<Point>, points: Vec<Point>) -> Vec<Point> {
|
||||
if !group.contains(&point) {
|
||||
group.push(point);
|
||||
|
||||
let neighbors = [(point.x + 1, point.y), (point.x - 1, point.y), (point.x, point.y + 1), (point.x, point.y - 1)];
|
||||
for neighbor_coords in neighbors {
|
||||
let neighbor = points.iter().find(|p| p.x == neighbor_coords.0 && p.y == neighbor_coords.1);
|
||||
|
||||
if let Some(neighbor) = neighbor {
|
||||
if !group.contains(&neighbor) {
|
||||
group = flood_fill(*neighbor, group, points.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
pub fn divide_into_groups(points: Vec<Point>) -> Vec<Vec<TuplePoint>> {
|
||||
let mut groups = vec![];
|
||||
|
||||
let mut remaining = points.iter().filter(|p| p.x < 64).collect::<Vec<_>>();
|
||||
while let Some(&point) = remaining.get(0) {
|
||||
let group = flood_fill(*point, vec![], points.clone());
|
||||
groups.push(group.iter().map(|v| v.into_tuple()).collect());
|
||||
|
||||
remaining = remaining.into_iter().filter(|p| !group.contains(p)).collect();
|
||||
}
|
||||
|
||||
groups
|
||||
}
|
||||
|
||||
// #[pyfunction]
|
||||
// fn pair_groups(set_a: Vec<Vec<Point>>, set_b: Vec<Vec<Point>>) -> Vec<(TuplePoint, TuplePoint)> {
|
||||
// let mut pairs = vec![];
|
||||
|
||||
// // Create dictionaries to store bounding boxes for each group
|
||||
// let mut bounding_boxes_a: std::collections::HashMap::new(); // dict[int, tuple[float, float, float, float]] = {}
|
||||
// let mut bounding_boxes_a: std::collections::HashMap::new();
|
||||
|
||||
// // Calculate bounding boxes for all groups in both sets
|
||||
// for (i, group) in set_a.iter().chain(set_b).iter().enumerate() {
|
||||
// let bounding_box = compute_bounding_box(group);
|
||||
// if i < set_a.len() {
|
||||
// bounding_boxes_a[i] = bounding_box;
|
||||
// } else {
|
||||
// bounding_boxes_b[i - set_a.len()] = bounding_box;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Check for overlaps and determine pairs
|
||||
// for (i, group_a) in set_a.iter().enumerate() {
|
||||
// let overlap_detected = false;
|
||||
// for (j, group_b) in set_b.iter().enenumerate() {
|
||||
// let bounding_box_a = bounding_boxes_a[i];
|
||||
// let bounding_box_b = bounding_boxes_b[j];
|
||||
// if (
|
||||
// bounding_box_a[0] <= bounding_box_b[1] &&
|
||||
// bounding_box_a[1] >= bounding_box_b[0] &&
|
||||
// bounding_box_a[2] <= bounding_box_b[3] &&
|
||||
// bounding_box_a[3] >= bounding_box_b[2]
|
||||
// ) {
|
||||
// pairs.push((group_a, group_b));
|
||||
// overlap_detected = true;
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
// if !overlap_detected {
|
||||
// // Find the nearest neighbor in set B
|
||||
// let mut nearest_group = set_b[0];
|
||||
// let mut nearest_value = 0.0f64;
|
||||
// for val in set_b.iter() {
|
||||
// if calculate_distance(group_a, val) < nearest_value {
|
||||
// nearest_group = val;
|
||||
// }
|
||||
// }
|
||||
// pairs.append((group_a, nearest_group));
|
||||
// }
|
||||
// }
|
||||
|
||||
// pairs
|
||||
// }
|
||||
|
||||
#[pymodule]
|
||||
fn fastproot(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
||||
m.add_function(wrap_pyfunction!(solve, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(divide_into_groups, m)?)?;
|
||||
// m.add_function(wrap_pyfunction!(pair_groups, m)?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
BIN
yaml parse test/graphics/loaded.png
Normal file
BIN
yaml parse test/graphics/loaded.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
yaml parse test/neutral.png
Normal file
BIN
yaml parse test/neutral.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 306 B |
366
yaml parse test/prootOS.py
Normal file
366
yaml parse test/prootOS.py
Normal file
|
@ -0,0 +1,366 @@
|
|||
import yaml
|
||||
import paho.mqtt.client as mqtt
|
||||
import threading
|
||||
from Point2D import divide_points_into_groups, generate_image_from_point_array, generate_point_array_from_image, interpolate_point_pairs, mirror_points, pair_groups, pair_points
|
||||
from PIL import Image
|
||||
import time
|
||||
import random
|
||||
from rgbmatrix import RGBMatrix, RGBMatrixOptions
|
||||
|
||||
# Configuration for the matrix screens
|
||||
options = RGBMatrixOptions()
|
||||
options.rows = 32
|
||||
options.cols = 64
|
||||
options.chain_length = 2
|
||||
options.parallel = 1
|
||||
options.hardware_mapping = 'regular'
|
||||
matrix = RGBMatrix(options=options)
|
||||
|
||||
class DuplicateTriggerError(Exception):
|
||||
"""Custom exception for duplicated triggers."""
|
||||
|
||||
def load_animations(yaml_file):
|
||||
try:
|
||||
with open(yaml_file, 'r') as file:
|
||||
animations_data = yaml.safe_load(file)
|
||||
animations = animations_data.get('animations', [])
|
||||
|
||||
# Create a dictionary to store animations by trigger
|
||||
trigger_animations = {}
|
||||
|
||||
# Iterate through animations and modify graphics as needed
|
||||
for animation in animations:
|
||||
trigger = animation.get('trigger')
|
||||
|
||||
# Check if the trigger is already in the dictionary
|
||||
if trigger in trigger_animations:
|
||||
trigger_animations[trigger].append(animation.get('name', 'unnamed animation'))
|
||||
else:
|
||||
trigger_animations[trigger] = [animation.get('name', 'unnamed animation')]
|
||||
|
||||
graphics = animation.get('graphics', [])
|
||||
|
||||
for graphic in graphics:
|
||||
if graphic.get('type') == 'transition':
|
||||
to_file = graphic.get('to_file')
|
||||
point_array = generate_point_array_from_image(Image.open(to_file))
|
||||
graphic['to_point_array'] = point_array # Store the point array
|
||||
|
||||
elif graphic.get('type') == 'image' or graphic.get('type') == 'animation':
|
||||
# Check if the 'source' is set to 'current'
|
||||
if graphic.get('source') != 'current':
|
||||
graphic['source_file'] = graphic.get('source_file')
|
||||
source_file = graphic.get('source_file')
|
||||
point_array = generate_point_array_from_image(Image.open(source_file))
|
||||
graphic['point_array'] = point_array # Store the point array
|
||||
|
||||
# Check for duplicated triggers
|
||||
duplicated_triggers = [trigger for trigger, animations in trigger_animations.items() if len(animations) > 1]
|
||||
if duplicated_triggers:
|
||||
error_message = "Duplicated trigger(s) found:\n"
|
||||
for trigger in duplicated_triggers:
|
||||
error_message += f"Trigger: {trigger}, Animations: {', '.join(trigger_animations[trigger])}\n"
|
||||
raise DuplicateTriggerError(error_message)
|
||||
|
||||
return animations
|
||||
except DuplicateTriggerError as e:
|
||||
print(e) # Print the error message with duplicated triggers
|
||||
raise e
|
||||
except Exception as e:
|
||||
print(f"Error parsing YAML file: {e}")
|
||||
return []
|
||||
|
||||
|
||||
|
||||
|
||||
# Function to simulate playing an animation
|
||||
def play_animation(animation):
|
||||
print(f"Playing animation: {animation['name']}")
|
||||
|
||||
|
||||
# Decision-Making Layer
|
||||
class DecisionLayer:
|
||||
def __init__(self, animations, rendering_layer):
|
||||
self.animations = animations
|
||||
self.rendering_layer = rendering_layer
|
||||
|
||||
def get_animation_by_trigger(self, trigger):
|
||||
"""
|
||||
Get an animation from the list of parsed animations based on the specified trigger.
|
||||
|
||||
Args:
|
||||
animations (list): List of parsed animations.
|
||||
trigger (str): The trigger to match.
|
||||
|
||||
Returns:
|
||||
dict: The animation dictionary matching the trigger, or None if not found.
|
||||
"""
|
||||
for animation in self.animations:
|
||||
if animation.get('trigger') == trigger:
|
||||
return animation
|
||||
return None # Return None if no animation with the specified trigger is found
|
||||
|
||||
|
||||
def handle_trigger(self, source, payload):
|
||||
trigger = None
|
||||
if source == "boot":
|
||||
trigger = "boot"
|
||||
|
||||
elif source == "blinkTimer":
|
||||
if len(self.rendering_layer.animation_queue) == 0:
|
||||
trigger = "blinkTimer"
|
||||
|
||||
elif source == "mqtt":
|
||||
print("Received message '" + str(payload))
|
||||
if len(str(payload)) < 17:
|
||||
print("received massage too short to be valid")
|
||||
sensor_type = str(payload)[2:6]
|
||||
sensor_specifier = str(payload)[7:11]
|
||||
sensor_state = str(payload)[12:16]
|
||||
|
||||
if sensor_type == "Bean":
|
||||
if sensor_state == "0000":
|
||||
print("Bean ID:" + sensor_specifier + " Fell.")
|
||||
elif sensor_state == "0001":
|
||||
print("Bean ID:" + sensor_specifier + " Rose.")
|
||||
else:
|
||||
print("Bean ID:" + sensor_specifier + " in illegal state.")
|
||||
|
||||
elif sensor_type == "Butn":
|
||||
if sensor_state == "0000":
|
||||
print("Button ID:" + sensor_specifier + " Fell.")
|
||||
elif sensor_state == "0001":
|
||||
print("Button ID:" + sensor_specifier + " Rose.")
|
||||
else:
|
||||
print("Received illegal state: " + sensor_state + " for Button ID:" + sensor_specifier)
|
||||
|
||||
elif sensor_type == "Move":
|
||||
if sensor_specifier == "000X":
|
||||
print("Movement in X axis: " + sensor_state)
|
||||
elif sensor_specifier == "000Y":
|
||||
print("Movement in Y axis: " + sensor_state)
|
||||
elif sensor_specifier == "000Z":
|
||||
print("Movement in Z axis: " + sensor_state)
|
||||
else:
|
||||
print("Received illegal movement axis.")
|
||||
|
||||
elif sensor_type == "Rott":
|
||||
if sensor_specifier == "000X":
|
||||
print("Rotation in X axis: " + sensor_state)
|
||||
elif sensor_specifier == "000Y":
|
||||
print("Rotation in Y axis: " + sensor_state)
|
||||
elif sensor_specifier == "000Z":
|
||||
print("Rotation in Z axis: " + sensor_state)
|
||||
else:
|
||||
print("Received illegal Rotation axis.")
|
||||
|
||||
elif sensor_type == "Gest":
|
||||
if sensor_specifier == "XXXX":
|
||||
print("Gesture received: " + sensor_state)
|
||||
else:
|
||||
print("Received illegal gesture")
|
||||
|
||||
else:
|
||||
print("received illegal sensor type: " + sensor_type)
|
||||
|
||||
trigger = str(payload)[2:16]
|
||||
|
||||
# Implement logic to decide which animation to play based on the trigger and payload
|
||||
for animation in self.animations:
|
||||
if animation.get('trigger') == trigger:
|
||||
self.rendering_layer.play_animation(animation)
|
||||
|
||||
|
||||
class RenderingLayer:
|
||||
def __init__(self, animations, frame_rate=40):
|
||||
self.animations = animations
|
||||
self.current_point_array = []
|
||||
self.current_animation_action = {}
|
||||
self.frame_rate = frame_rate # Set the desired frame rate
|
||||
self.frame_duration = 1.0 / frame_rate # Calculate the frame duration
|
||||
self.animation_queue = [] # Initialize the animation queue
|
||||
|
||||
def play_animation_by_name(self, animation_name):
|
||||
for animation in self.animations:
|
||||
if animation.get('name') == animation_name:
|
||||
self.play_animation(animation)
|
||||
|
||||
def play_animation(self, animation):
|
||||
if len(self.animation_queue) > 0:
|
||||
print("Stopping current animation...")
|
||||
# Replace the currently playing animation with the new one
|
||||
self.animation_queue = self.generate_animation_queue(animation) # Add frames to the queue
|
||||
|
||||
|
||||
def append_animation(self, animation):
|
||||
self.animation_queue = self.animation_queue + self.generate_animation_queue(animation) # Add frames to the queue
|
||||
|
||||
def generate_animation_queue(self, animation):
|
||||
animation_queue = []
|
||||
graphics = animation.get('graphics', [])
|
||||
|
||||
for graphic in graphics:
|
||||
if graphic.get('type') == 'image':
|
||||
point_array = graphic.get('point_array')
|
||||
duration = graphic.get('duration', 1) # Default duration is 1 frame if not specified
|
||||
|
||||
# Add frames to the queue based on the specified duration
|
||||
for _ in range(int(duration)):
|
||||
animation_queue.append({'type': graphic.get('type'), 'point_array': point_array})
|
||||
|
||||
if graphic.get('type') == 'transition':
|
||||
to_point_array = graphic.get('to_point_array')
|
||||
duration = graphic.get('duration', 1) # Default duration is 1 frame if not specified
|
||||
|
||||
# Add frames to the queue based on the specified duration
|
||||
for i in range(int(duration)):
|
||||
animation_queue.append({'type': graphic.get('type'), 'to_point_array': to_point_array, 'stepPercentage' : (1/(int(duration)-i))})
|
||||
|
||||
return animation_queue
|
||||
|
||||
def start_rendering(self):
|
||||
frameCount = 0
|
||||
new_image = Image.new("RGB", (128, 32), "black")
|
||||
|
||||
devisiontime = 0
|
||||
pairgrouptime = 0
|
||||
pairpointtime = 0
|
||||
imagingtime = 0
|
||||
transitionFrameCount = 1
|
||||
|
||||
while True:
|
||||
|
||||
start_time = time.time() # Get the current time before rendering
|
||||
|
||||
if len(self.animation_queue) > 0:
|
||||
current_animation_action = self.animation_queue.pop(0)
|
||||
print("update action is: " + current_animation_action.get('type'))
|
||||
|
||||
|
||||
# Render the next frame in the queue
|
||||
if current_animation_action.get('type') == "image":
|
||||
new_image = generate_image_from_point_array(current_animation_action.get('point_array'), 128, 32)
|
||||
|
||||
self.current_point_array = current_animation_action.get('point_array')
|
||||
print("image generated")
|
||||
|
||||
elif current_animation_action.get('type') == "transition":
|
||||
transitionFrameCount += 1
|
||||
divtime_start = time.time()
|
||||
groupsa = divide_points_into_groups(self.current_point_array)
|
||||
groupsb = divide_points_into_groups(current_animation_action.get('to_point_array'))
|
||||
devisiontime += time.time() - divtime_start
|
||||
|
||||
pairgrouptime_start = time.time()
|
||||
paired_groups = pair_groups(groupsa, groupsb)
|
||||
pairgrouptime += time.time() - pairgrouptime_start
|
||||
|
||||
new_point_array = []
|
||||
for pair in paired_groups:
|
||||
pairpointtime_start = time.time()
|
||||
point_pairs = pair_points(pair[0], pair[1])
|
||||
pairpointtime += time.time() - pairpointtime_start
|
||||
print(str(current_animation_action.get('stepPercentage')))
|
||||
new_point_array += interpolate_point_pairs(point_pairs, current_animation_action.get('stepPercentage'))
|
||||
|
||||
imagingtime_start = time.time()
|
||||
new_image = generate_image_from_point_array(new_point_array + mirror_points(new_point_array), 128, 32)
|
||||
|
||||
imagingtime += time.time() - imagingtime_start
|
||||
|
||||
self.current_point_array = new_point_array
|
||||
|
||||
offscreen_canvas = matrix.CreateFrameCanvas()
|
||||
offscreen_canvas.SetImage(new_image, unsafe=False)
|
||||
matrix.SwapOnVSync(offscreen_canvas)
|
||||
|
||||
# Save the image to a file with the desired format and file name
|
||||
# new_image.save("output/frameNumber"+str(frameCount)+".png")
|
||||
frameCount += 1
|
||||
|
||||
elapsed_time = time.time() - start_time # Calculate time elapsed during rendering
|
||||
|
||||
# Calculate the time to sleep to achieve the desired frame rate
|
||||
sleep_time = self.frame_duration - elapsed_time
|
||||
print("remaining time in frame: " + str(sleep_time))
|
||||
if sleep_time > 0:
|
||||
time.sleep(sleep_time)
|
||||
print("average time cost per part for transition frames:")
|
||||
print("devisiontime :" + str(devisiontime /transitionFrameCount ))
|
||||
print("pairgrouptime :" + str(pairgrouptime /transitionFrameCount))
|
||||
print("pairpointtime :" + str(pairpointtime /transitionFrameCount))
|
||||
print("imagingtime :" + str(imagingtime /transitionFrameCount))
|
||||
|
||||
|
||||
devisiontime = 0
|
||||
pairgrouptime = 0
|
||||
pairpointtime = 0
|
||||
imagingtime = 0
|
||||
transitionFrameCount = 0
|
||||
|
||||
|
||||
|
||||
|
||||
# Function responsible for the blinking behaviour when Idle
|
||||
def random_blinks():
|
||||
while True:
|
||||
time.sleep(random.randint(5, 7))
|
||||
decision_layer.handle_trigger("blinkTimer", "")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# functions called by the MQTT listener
|
||||
def on_connect(client, userdata, flags, response_code):
|
||||
print("Connected to MQTT broker with result code " + str(response_code))
|
||||
client.subscribe("test")
|
||||
|
||||
|
||||
# Function to handle MQTT message reception
|
||||
def on_message(client, userdata, message):
|
||||
# Pass the received message to the decision-making layer
|
||||
decision_layer.handle_trigger("mqtt", message.payload)
|
||||
|
||||
|
||||
def main():
|
||||
yaml_file = 'testAnimationYaml.yaml' # Replace with the path to your YAML file
|
||||
|
||||
# Parse the YAML file to get animations
|
||||
animations = load_animations(yaml_file)
|
||||
|
||||
|
||||
# MQTT broker configuration
|
||||
broker_address = "localhost"
|
||||
broker_port = 1883
|
||||
broker_keepalive = 60
|
||||
|
||||
mqtt_client = mqtt.Client()
|
||||
mqtt_client.on_connect = on_connect
|
||||
mqtt_client.on_message = on_message
|
||||
|
||||
mqtt_client.connect(broker_address, broker_port, broker_keepalive)
|
||||
|
||||
mqtt_client.loop_start()
|
||||
|
||||
# Initialize the rendering layer
|
||||
rendering_layer = RenderingLayer(animations, frame_rate=10)
|
||||
rendering_thread = threading.Thread(target=rendering_layer.start_rendering)
|
||||
rendering_thread.start()
|
||||
|
||||
# Initialize the decision-making layer
|
||||
global decision_layer
|
||||
decision_layer = DecisionLayer(animations, rendering_layer)
|
||||
|
||||
# Create and start random blinks interrupts
|
||||
screen_update_thread = threading.Thread(target=random_blinks)
|
||||
screen_update_thread.start()
|
||||
|
||||
|
||||
decision_layer.handle_trigger("boot", "")
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
BIN
yaml parse test/shared.so
Normal file
BIN
yaml parse test/shared.so
Normal file
Binary file not shown.
9
yaml parse test/test.py
Normal file
9
yaml parse test/test.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from timeit import default_timer as timer
|
||||
from ctypes import *
|
||||
|
||||
|
||||
proot = CDLL("./fastproot/target/debug/fastproot.dll")
|
||||
start = timer()
|
||||
proot.test()
|
||||
end = timer()
|
||||
print(end - start)
|
83
yaml parse test/testAnimationYaml.yaml
Normal file
83
yaml parse test/testAnimationYaml.yaml
Normal file
|
@ -0,0 +1,83 @@
|
|||
animations:
|
||||
- name: example animation
|
||||
description: This is the example animation to showcase some of the options
|
||||
loop_count: 1 # Number of loops (1 for single play, 0 for infinite)
|
||||
|
||||
# Define the trigger for this animation.
|
||||
# In this case, it triggers when a button is pressed.
|
||||
trigger: Butn_0001_0001 #Button 1 pressed
|
||||
|
||||
# Specify whether this animation can be overridden by another animation.
|
||||
overrideable: true
|
||||
|
||||
graphics:
|
||||
- type: transition
|
||||
to_file: animation1_frame1.png # Specify the new PNG image for the transition
|
||||
duration: 10 # The amount of frames the transition will take.
|
||||
|
||||
- type: image
|
||||
source_file: animation1_frame1.png
|
||||
duration: 10 # The amount of frames the image will be shown.
|
||||
# This is the initial frame of the animation.
|
||||
|
||||
- type: transition
|
||||
to_file: animation1_frame2.png
|
||||
duration: 10 # The amount of frames the transition will take.
|
||||
# This is a transition from the initial frame to the next frame.
|
||||
# Transitions can be used to create smooth animations.
|
||||
|
||||
# You can add more graphics elements as needed for this animation.
|
||||
|
||||
# Additional comments or configuration options for Animation1 can go here.
|
||||
# For example, you can specify the duration, sound effects, or other details.
|
||||
|
||||
- name: blink
|
||||
description: Animation for blinking
|
||||
loop_count: 1
|
||||
|
||||
trigger: blinkTimer #Button 2 pressed
|
||||
|
||||
overrideable: true # blink can be interupted at any time
|
||||
|
||||
graphics:
|
||||
- type: transition # close the eye from whatever the current state
|
||||
to_file: dizzyFace.png
|
||||
duration: 5
|
||||
|
||||
- type: image # hold eye closed
|
||||
source_file: dizzyFace.png
|
||||
duration: 10
|
||||
|
||||
- type: transition # open the eye again from being closed
|
||||
to_file: neutral.png
|
||||
duration: 5
|
||||
|
||||
- name: openEye
|
||||
description: Animation for blinking
|
||||
loop_count: 1
|
||||
|
||||
trigger: boot
|
||||
|
||||
overrideable: true # blink can be interupted at any time
|
||||
|
||||
graphics:
|
||||
- type: image # hold eye closed
|
||||
source_file: eyesClosed_neutral.png
|
||||
duration: 1
|
||||
|
||||
- type: transition # open the eye again from being closed
|
||||
to_file: dizzyFace.png
|
||||
duration: 5
|
||||
|
||||
- name: make dizzy
|
||||
description: Animation for making dizzy
|
||||
loop_count: 1
|
||||
|
||||
trigger: Butn_0002_0001
|
||||
|
||||
overrideable: true # blink can be interupted at any time
|
||||
|
||||
graphics:
|
||||
- type: transition # open the eye again from being closed
|
||||
to_file: dizzyFace.png
|
||||
duration: 5
|
Loading…
Reference in a new issue