diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index bcdef70..0000000 --- a/Cargo.toml +++ /dev/null @@ -1,3 +0,0 @@ -[workspace] -resolver = "2" -members = ["modules/*/crates/*"] \ No newline at end of file diff --git a/install-linux.sh b/install-linux.sh index 26d7de9..2e82e8c 100644 --- a/install-linux.sh +++ b/install-linux.sh @@ -146,11 +146,11 @@ if [ -d ~/houdini19.5 ]; then mkdir -p ~/houdini19.5/scripts; ln -sf $EWCONFIG_R if [ -d ~/.config/blender/3.6 ]; then ln -sf $EWCONFIG_ROOT/configs/blender/3.x/scripts/addons/* ~/.config/blender/3.6/scripts/addons/; fi # If we have `cargo` and $NO_RUST is not set, build and install rust tooling -if type -p cargo > /dev/null && [ -z "$NO_RUST" ]; then - mkdir -p $EWCONFIG_ROOT/rust-bin - cargo build --all --release || true - cp $EWCONFIG_ROOT/target/release/* $EWCONFIG_ROOT/rust-bin -fi +# if type -p cargo > /dev/null && [ -z "$NO_RUST" ]; then +# mkdir -p $EWCONFIG_ROOT/rust-bin +# cargo build --all --release || true +# cp $EWCONFIG_ROOT/target/release/* $EWCONFIG_ROOT/rust-bin +# fi # -- Finalization -- diff --git a/keyboards/qmk/keymaps/tg4x/case.scad b/keyboards/qmk/keymaps/tg4x/case.scad new file mode 100644 index 0000000..84fd936 --- /dev/null +++ b/keyboards/qmk/keymaps/tg4x/case.scad @@ -0,0 +1,104 @@ +// Filler plate for the TG4X + +// Keyboard dimensions +KBD_WIDTH = 255; +KBD_HEIGHT = 84; + +// Global cutout sizing +CUTOUT_DEPTH = 20; + +// Used to automatically render each half to a different STL file +ENABLE_HALVES = [ true, true ]; + +module place_standoff_holes(x = 5, y = 5) { + linear_extrude(height = CUTOUT_DEPTH) { + // Left + translate([ 62, 0, 0 ]) square([ x, y ]); + // Middle + translate([ 125, 0, 0 ]) square([ x, y ]); + // Right + translate([ 188, 0, 0 ]) square([ x, y ]); + } +} + +// Used to slice the model into halves for printing +difference() { + // Model + difference() { + // The fill shape + union() { + // Keyboard bounds + cube([ KBD_WIDTH, KBD_HEIGHT, 3 ]); + + // Walls + union() { + // Right wall + translate([ KBD_WIDTH - 3, 0, 0 ]) cube([ 3, KBD_HEIGHT, 11 ]); + // Top wall + translate([ 0, KBD_HEIGHT - 4, 0 ]) cube([ KBD_WIDTH, 4, 11 ]); + // Left wall + translate([ 0, 0, 0 ]) cube([ 4, KBD_HEIGHT, 11 ]); + // Front wall + difference() { + translate([ 0, 0, 0 ]) cube([ KBD_WIDTH, 3, 11 ]); + translate([ KBD_WIDTH / 2, 0, 0 ]) cube([ 35, 10, 20 ]); + } + } + } + + // Cut out the standoff holes + union() { + // Corner cuts + linear_extrude(height = CUTOUT_DEPTH) { + // Top left + translate([ 0, 79, 0 ]) square([ 5, 5 ]); + // Bottom left + translate([ 0, 0, 0 ]) square([ 5, 5 ]); + // Top right + translate([ 250, 79, 0 ]) square([ 5, 5 ]); + // Bottom right + translate([ 250, 0, 0 ]) square([ 5, 5]); + } + + // Centeral standoff holes + translate([ 0, 80, 0 ]) place_standoff_holes(); + translate([ 0, 40, 0 ]) place_standoff_holes(y = 5); + translate([ 0, 0, 0 ]) place_standoff_holes(); + } + + // Cut out a space for the controller + translate([ 0, 22, 0 ]) cube([ 45, 21, CUTOUT_DEPTH ]); + + // Removal of unnecessary material + union() { + // Section 1 + union() { + translate([ 10, 10, 0 ]) cube([ 45, 25, 10 ]); + translate([ 10, KBD_HEIGHT - 10 - 25, 0 ]) cube([ 45, 25, 10 ]); + } + // Section 2 + union() { + translate([ 72, 10, 0 ]) cube([ 48, 25, 10 ]); + translate([ 72, KBD_HEIGHT - 10 - 25, 0 ]) cube([ 48, 25, 10 ]); + } + // Section 3 + union() { + translate([ 135, 10, 0 ]) cube([ 45, 25, 10 ]); + translate([ 135, KBD_HEIGHT - 10 - 25, 0 ]) cube([ 45, 25, 10 ]); + } + // Section 4 + union() { + translate([ 197, 10, 0 ]) cube([ 48, 25, 10 ]); + translate([ 197, KBD_HEIGHT - 10 - 25, 0 ]) cube([ 48, 25, 10 ]); + } + } + } + + // Slicing functionality + if (!ENABLE_HALVES[0]) { + translate([ 0, 0, 0 ]) cube([ KBD_WIDTH / 2, KBD_HEIGHT, 30 ]); + } + if (!ENABLE_HALVES[1]) { + translate([ KBD_WIDTH / 2, 0, 0 ]) cube([ KBD_WIDTH / 2, KBD_HEIGHT, 30 ]); + } +} \ No newline at end of file diff --git a/scripts/filmsim b/scripts/filmsim new file mode 100755 index 0000000..c3ecb0c --- /dev/null +++ b/scripts/filmsim @@ -0,0 +1,140 @@ +#! /usr/bin/env python3 + +import argparse +import sys +import logging +import cv2 +import numpy as np +from pathlib import Path +from inspect import getmembers, isfunction + +logger = logging.getLogger(__name__) + + +def apply_filter_bw(image): + image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) + image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) + return image + + +def apply_filter_grain(image): + noise = np.random.randint(0, 256, image.shape, dtype="uint8") + noise = cv2.cvtColor(noise, cv2.COLOR_BGR2GRAY) + noise = cv2.cvtColor(noise, cv2.COLOR_GRAY2BGR) + scale = 0.8 + image = cv2.addWeighted(image, scale, noise, 1 - scale, 0) + return image + + +def main() -> int: + # Handle program arguments + ap = argparse.ArgumentParser( + prog="filmsim", description="Experimental film effect simulator" + ) + ap.add_argument( + "--mode", + help="Mode to operate in (image or video)", + default="video", + choices=["image", "video"], + ) + ap.add_argument( + "--input", + help="Path to the input image or video (uses webcam if unset)", + type=Path, + ) + ap.add_argument( + "--output", + help="Path to the output image or video (displays output if unset)", + type=Path, + ) + ap.add_argument( + "--filters", help="Comma-separated list of filters to apply", default="bw,grain" + ) + ap.add_argument( + "-v", "--verbose", help="Enable verbose logging", action="store_true" + ) + args = ap.parse_args() + + # Configure logging + logging.basicConfig( + level=logging.DEBUG if args.verbose else logging.INFO, + format="%(levelname)s: %(message)s", + ) + + # Build filter chain + filter_functions = getmembers( + sys.modules[__name__], + lambda member: isfunction(member) + and member.__name__.startswith("apply_filter_"), + ) + enabled_filters = args.filters.split(",") + filters = [ + filter_function + for filter_name, filter_function in filter_functions + if filter_name.replace("apply_filter_", "") in enabled_filters + ] + + # Read input image or video + if args.mode == "image": + if args.input is None: + webcam = cv2.VideoCapture(0) + ret, image = webcam.read() + if not ret: + logger.error("Failed to capture image from webcam") + return 1 + else: + image = cv2.imread(str(args.input)) + if image is None: + logger.error("Failed to read input image") + return 1 + + # Apply filters + for filter_function in filters: + image = filter_function(image) + + # Write output image + if args.output is None: + cv2.imshow("Output", image) + cv2.waitKey(0) + else: + cv2.imwrite(str(args.output), image) + + elif args.mode == "video": + if args.input is None: + webcam = cv2.VideoCapture(0) + else: + webcam = cv2.VideoCapture(str(args.input)) + if not webcam.isOpened(): + logger.error("Failed to open input video") + return 1 + + # Read video frame by frame + while True: + ret, frame = webcam.read() + if not ret: + logger.error("Failed to read frame from video") + return 1 + + # Apply filters + for filter_function in filters: + frame = filter_function(frame) + + # Write output frame + if args.output is None: + # Flip frame if using webcam + if args.input is None: + frame = cv2.flip(frame, 1) + cv2.imshow("Output", frame) + if cv2.waitKey(1) & 0xFF == ord("q"): + break + # else: + # cv2.imwrite(str(args.output), frame) + + webcam.release() + cv2.destroyAllWindows() + + return 0 + + +if __name__ == "__main__": + sys.exit(main())