diff --git a/boxes/__init__.py b/boxes/__init__.py
index a7a767c9..382742bf 100755
--- a/boxes/__init__.py
+++ b/boxes/__init__.py
@@ -581,6 +581,8 @@ def addParts(self, parts):
for part in parts:
self.addPart(part)
+ fingerHolesAt : Any
+
def _buildObjects(self):
"""Add default edges and parts"""
self.edges = {}
diff --git a/boxes/generators/matrix.py b/boxes/generators/matrix.py
new file mode 100644
index 00000000..97f66ca0
--- /dev/null
+++ b/boxes/generators/matrix.py
@@ -0,0 +1,371 @@
+# Copyright (C) 2024 fidoriel
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+from boxes import *
+
+
+class Matrix(Boxes):
+ """WS2812b matrix enclosure"""
+
+ description = """## Simple WS2812b matrix enclosure
+ WS2812b matrix enclosure for cheap chinease prebuild led matrixes.
+ This design assumes that the distance between the leds is equal in both directions.
+
+ There are several parts to this design:
+ - The inner frame to hold the pcb in place
+ - The front frame to hold a sandwich of plexiglass, spacer and the pcb
+ - The plexiglass to protect and diffuse the leds.
+ You may add car tint foil to the plexiglass to achieve a black look
+ - The spacer to keep the plexiglass from touching the leds
+ - The back box with an optional mounting hole. Please add a hole for the
+ power supply to the side panels and the power and data cables
+ through the led mount frame with your favorite svg editor.
+ - The side panels with finger joints to hold everything together
+
+ Assembly:
+ 1. Cut the parts
+ 2. Assemble the frame (side panels and inner frame)
+ 3. Insert the pcb, the spacers and the plexiglass
+ 4. Close the front frame
+ 5. Insert electronics in the back box
+ (for example a USB C port on the side panel, a esp32 with wled firmware)
+ 6. Close the back box
+
+ The inner frame and spacer should keep everything in place without the need for glue.
+ If you are using multiple modules,
+ you can add the layout parameters, so that the inner frame adjusts accordingly.
+
+ Please Note: if you are creating a large matrix build of multiple individual modules,
+ you need to enter absolut values across all modules for all parameters.
+ Please cut the plane labeled "Plexiglass" out of plexiglass :)
+ You can use a different thickness for the plexiglass, but make sure to adjust the settings accordingly.
+ """
+ ui_group = "misc"
+
+ led_width: int
+ led_height: int
+ pysical_led_y: int
+ pysical_led_x: int
+
+ distance_between_leds: float
+ plexiglass_thicknes: float
+
+ h: int
+
+ matrix_back_frame_border: int = 20
+ matrix_front_frame_border_offset: int = 10
+
+ height_pcb: float
+
+ bottom_edge: str = "F"
+
+ mounting_holes: bool
+ mounting_hole_diameter: float
+
+ matrix_count_x: int
+ matrix_count_y: int
+
+ def __init__(self) -> None:
+ Boxes.__init__(self)
+ self.addSettingsArgs(edges.FingerJointSettings)
+
+ self.argparser.add_argument(
+ "--led_width",
+ action="store",
+ type=int,
+ default=16,
+ help="Width of the LED matrix in pixels",
+ )
+
+ self.argparser.add_argument(
+ "--led_height",
+ action="store",
+ type=int,
+ default=16,
+ help="Height of the LED matrix in pixels",
+ )
+
+ self.argparser.add_argument(
+ "--pysical_led_y",
+ action="store",
+ type=int,
+ default=160,
+ help="Width of the LED matrix pcb in mm",
+ )
+
+ self.argparser.add_argument(
+ "--pysical_led_x",
+ action="store",
+ type=int,
+ default=160,
+ help="Height of the LED matrix pcb in mm",
+ )
+
+ self.argparser.add_argument(
+ "--matrix_back_frame_border",
+ action="store",
+ type=int,
+ default=20,
+ help="Border of the back frame bo keep the pcb in blace but allow for air flow and cable management",
+ )
+
+ self.argparser.add_argument(
+ "--matrix_front_frame_border_offset",
+ action="store",
+ type=int,
+ default=10,
+ help="Offset of the front frame to allow for the plexiglass to be attached",
+ )
+
+ self.argparser.add_argument(
+ "--distance_between_leds",
+ action="store",
+ type=float,
+ default=1,
+ help="Distance of the color dividers. Make sure your machine is able to cut thin structures.",
+ )
+
+ self.argparser.add_argument(
+ "--h",
+ action="store",
+ type=int,
+ default=30,
+ help="Height of the matrix",
+ )
+
+ self.argparser.add_argument(
+ "--height_pcb",
+ action="store",
+ type=float,
+ default=0.2,
+ help="Height of the pcb including the highest non led components in mm",
+ )
+
+ self.argparser.add_argument(
+ "--plexiglass_thicknes",
+ action="store",
+ type=float,
+ default=3,
+ help="Thickness of the plexiglass in mm",
+ )
+
+ self.argparser.add_argument(
+ "--mounting_holes",
+ action="store",
+ type=boolarg,
+ default=False,
+ help="Add mounting holes for the enclosure",
+ )
+
+ self.argparser.add_argument(
+ "--mounting_hole_diameter",
+ action="store",
+ type=float,
+ default=5,
+ help="Diameter of the mounting holes in mm",
+ )
+
+ self.argparser.add_argument(
+ "--matrix_count_x",
+ action="store",
+ type=int,
+ default=1,
+ help="Number of modules in x direction",
+ )
+
+ self.argparser.add_argument(
+ "--matrix_count_y",
+ action="store",
+ type=int,
+ default=1,
+ help="Number of modules in y direction",
+ )
+
+ self.buildArgParser()
+
+ def draw_frame(self, sizex: int, sizey: int, posx: int, posy: int):
+ self.rectangularHole(
+ x=posx,
+ y=posy,
+ dx=sizex,
+ dy=sizey,
+ r=0,
+ center_x=False,
+ center_y=False,
+ )
+
+ def matrix_back_sideholes(self, length: int):
+ sandwich_height = (
+ 2 * self.thickness + self.plexiglass_thicknes + self.height_pcb
+ )
+ h = -0.5 * self.thickness + self.h - sandwich_height
+ self.fingerHolesAt(0, h, length, angle=0)
+
+ def draw_led_grid(self):
+ space_per_led_x = self.pysical_led_x / self.led_width
+ space_per_led_y = self.pysical_led_y / self.led_height
+ for x in range(self.led_width):
+ for y in range(self.led_height):
+ self.rectangularHole(
+ x=self.matrix_front_frame_border_offset
+ + (x * space_per_led_x)
+ + self.distance_between_leds / 2,
+ y=self.matrix_front_frame_border_offset
+ + (y * space_per_led_y)
+ + self.distance_between_leds / 2,
+ dx=space_per_led_x - self.distance_between_leds,
+ dy=space_per_led_y - self.distance_between_leds,
+ r=0,
+ center_x=False,
+ center_y=False,
+ )
+
+ def create_mounting_holes(self):
+ if self.mounting_holes:
+ pos_x = (self.pysical_led_x + 2 * self.matrix_front_frame_border_offset) / 2
+ pos_y = (
+ (self.pysical_led_y + 2 * self.matrix_front_frame_border_offset) * 3 / 4
+ )
+ self.rectangularHole(
+ x=pos_x,
+ y=pos_y,
+ dx=self.mounting_hole_diameter,
+ dy=self.mounting_hole_diameter,
+ r=self.mounting_hole_diameter,
+ )
+ self.rectangularHole(
+ x=pos_x,
+ y=pos_y + self.mounting_hole_diameter / 2,
+ dx=self.mounting_hole_diameter / 2,
+ dy=self.mounting_hole_diameter / 2,
+ r=self.mounting_hole_diameter,
+ )
+
+ def render(self):
+ x, y, h = (
+ self.pysical_led_x + 2 * self.matrix_front_frame_border_offset,
+ self.pysical_led_y + 2 * self.matrix_front_frame_border_offset,
+ self.h,
+ )
+
+ d2 = edges.Bolts(2)
+ d3 = edges.Bolts(3)
+
+ d2 = d3 = None
+
+ self.rectangularWall(
+ x,
+ h,
+ "FFFF",
+ bedBolts=[d2] * 4,
+ move="up",
+ label="Wall 1",
+ callback=[
+ lambda: self.matrix_back_sideholes(
+ self.pysical_led_x + 2 * self.matrix_front_frame_border_offset
+ )
+ ],
+ )
+ self.rectangularWall(
+ y,
+ h,
+ "FfFf",
+ bedBolts=[d3, d2, d3, d2],
+ move="up",
+ label="Wall 2",
+ callback=[
+ lambda: self.matrix_back_sideholes(
+ self.pysical_led_x + 2 * self.matrix_front_frame_border_offset
+ )
+ ],
+ )
+ self.rectangularWall(
+ y,
+ h,
+ "FfFf",
+ move="up",
+ bedBolts=[d3, d2, d3, d2],
+ label="Wall 4",
+ callback=[
+ lambda: self.matrix_back_sideholes(
+ self.pysical_led_y + 2 * self.matrix_front_frame_border_offset
+ )
+ ],
+ )
+ self.rectangularWall(
+ x,
+ h,
+ "FFFF",
+ bedBolts=[d2] * 4,
+ move="up",
+ label="Wall 3",
+ callback=[
+ lambda: self.matrix_back_sideholes(
+ self.pysical_led_y + 2 * self.matrix_front_frame_border_offset
+ )
+ ],
+ )
+
+ self.rectangularWall(
+ x,
+ y,
+ "ffff",
+ bedBolts=[d2, d3, d2, d3],
+ move="right",
+ label="Top",
+ callback=[
+ lambda: self.draw_frame(
+ sizex=self.pysical_led_x,
+ sizey=self.pysical_led_y,
+ posx=self.matrix_front_frame_border_offset,
+ posy=self.matrix_front_frame_border_offset,
+ )
+ ],
+ )
+ self.rectangularWall(
+ x,
+ y,
+ "ffff",
+ bedBolts=[d2, d3, d2, d3],
+ move="right",
+ label="Bottom",
+ callback=[self.create_mounting_holes],
+ )
+ self.rectangularWall(
+ x,
+ y,
+ "ffff",
+ bedBolts=[d2, d3, d2, d3],
+ move="right",
+ label="matrix mount frame, please add cable holes as needed",
+ )
+
+ self.rectangularWall(
+ x,
+ y,
+ label="led_grid",
+ move="right",
+ callback=[lambda: self.draw_led_grid()],
+ )
+
+ self.rectangularWall(
+ x,
+ y,
+ label="led_grid",
+ move="right",
+ callback=[lambda: self.draw_led_grid()],
+ )
+
+ self.rectangularWall(x, y, label="Plexiglass")
diff --git a/examples/Matrix.svg b/examples/Matrix.svg
new file mode 100644
index 00000000..427a3c51
--- /dev/null
+++ b/examples/Matrix.svg
@@ -0,0 +1,694 @@
+
+
\ No newline at end of file
diff --git a/static/samples/Matrix-thumb.jpg b/static/samples/Matrix-thumb.jpg
new file mode 100644
index 00000000..f638ace1
Binary files /dev/null and b/static/samples/Matrix-thumb.jpg differ
diff --git a/static/samples/Matrix.jpg b/static/samples/Matrix.jpg
new file mode 100644
index 00000000..5b928c01
Binary files /dev/null and b/static/samples/Matrix.jpg differ
diff --git a/static/samples/samples.sha256 b/static/samples/samples.sha256
index 45441c26..99dafde5 100644
--- a/static/samples/samples.sha256
+++ b/static/samples/samples.sha256
@@ -174,3 +174,4 @@ f51e3cb0e74e380beda4c0966ee770142ff4fa38c266ca026177f9f4978190a4 ../static/samp
192047afde6a53a10715473e7a3efcf4e11e6d610f5b3fc658c2dfe329304856 ../static/samples/Shadowbox.jpg
a0865738425d5d9966dc6975d7e73559bac3c307c9614e8b48bae4abdf3efb5b ../static/samples/BrickSorter-2.jpg
2dcb314cdfa8b136b59288d2f4f7e501b4290ff68560216b6bedd779a32095ad ../static/samples/Shadowbox-diagram.jpg
+21333db253007b6e101333826a1231d788cf10e0e2afec84ff2a3f983330091d ../static/samples/Matrix.jpg