Showing preview only (6,805K chars total). Download the full file or copy to clipboard to get everything.
Repository: scottbez1/splitflap
Branch: master
Commit: 76252e0ed1de
Files: 408
Total size: 6.4 MB
Directory structure:
gitextract_endh529h/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ ├── 3d.yml
│ ├── electronics.yml
│ ├── electronics_v2.yml
│ ├── js.yml
│ └── pio.yml
├── .gitignore
├── .gitmodules
├── 3d/
│ ├── 28byj-48.scad
│ ├── assert.scad
│ ├── color_util.scad
│ ├── combined_front_panel.scad
│ ├── flap.scad
│ ├── flap_3dp.scad
│ ├── flap_characters.scad
│ ├── flap_dimensions.scad
│ ├── flap_fonts.scad
│ ├── font_generator.scad
│ ├── fonts/
│ │ ├── Epilogue/
│ │ │ └── OFL.txt
│ │ └── roboto/
│ │ └── LICENSE.txt
│ ├── global_constants.scad
│ ├── label.scad
│ ├── m4_dimensions.scad
│ ├── pcb.scad
│ ├── projection_renderer.scad
│ ├── rough7380.scad
│ ├── scripts/
│ │ ├── colored_stl_exporter.py
│ │ ├── dependencies.sh
│ │ ├── generate_2d.py
│ │ ├── generate_3d_print_flaps.py
│ │ ├── generate_combined_front_panel.py
│ │ ├── generate_fonts.py
│ │ ├── generate_gif.py
│ │ ├── generate_snapshot.py
│ │ ├── generate_stl.py
│ │ ├── kerf_presets.py
│ │ ├── openscad.py
│ │ ├── projection_renderer.py
│ │ ├── requirements.txt
│ │ └── svg_processor.py
│ ├── sensor_pcb_dimensions.scad
│ ├── shapes.scad
│ ├── splitflap.scad
│ ├── spool.scad
│ └── tools/
│ ├── classic_pcb_mount.scad
│ ├── connector_case.scad
│ ├── flap_container.scad
│ ├── mounting_bracket.scad
│ ├── punch_jig.scad
│ └── scoring_jig.scad
├── LICENSE.txt
├── README.md
├── __init__.py
├── arduino/
│ └── splitflap/
│ └── MOVED.txt
├── docs/
│ ├── DocumentationIndex.md
│ ├── ElectronicsGuide.md
│ ├── Flaps.md
│ ├── MotorGuide.md
│ ├── OpenSauce2024.md
│ ├── v0/
│ │ ├── Assembly.md
│ │ ├── OrderingComplete.md
│ │ └── OrderingEasy.md
│ └── v2/
│ ├── Assembly.md
│ ├── OrderingComplete.md
│ └── OrderingEasy.md
├── electronics/
│ ├── __init__.py
│ ├── chainlinkBase/
│ │ ├── .gitignore
│ │ ├── chainlinkBase-cache.lib
│ │ ├── chainlinkBase.kibot.yml
│ │ ├── chainlinkBase.kicad_pcb
│ │ ├── chainlinkBase.lib
│ │ ├── chainlinkBase.pro
│ │ ├── chainlinkBase.sch
│ │ ├── fp-lib-table
│ │ ├── powerChannel.sch
│ │ └── sym-lib-table
│ ├── chainlinkBuddyBreadboard/
│ │ ├── chainlinkBuddyBreadboard-cache.lib
│ │ ├── chainlinkBuddyBreadboard.kibot.yml
│ │ ├── chainlinkBuddyBreadboard.kicad_pcb
│ │ ├── chainlinkBuddyBreadboard.pro
│ │ ├── chainlinkBuddyBreadboard.sch
│ │ ├── fp-lib-table
│ │ ├── kikit_panelize.json
│ │ └── sym-lib-table
│ ├── chainlinkBuddyTDisplay/
│ │ ├── chainlinkBuddyTDisplay-cache.lib
│ │ ├── chainlinkBuddyTDisplay.kibot.yml
│ │ ├── chainlinkBuddyTDisplay.kicad_pcb
│ │ ├── chainlinkBuddyTDisplay.pro
│ │ ├── chainlinkBuddyTDisplay.sch
│ │ ├── fp-lib-table
│ │ ├── kikit_panelize.json
│ │ └── sym-lib-table
│ ├── chainlinkDriver/
│ │ ├── .gitignore
│ │ ├── chainlinkDriver-cache.lib
│ │ ├── chainlinkDriver.kibot.yml
│ │ ├── chainlinkDriver.kicad_pcb
│ │ ├── chainlinkDriver.lib
│ │ ├── chainlinkDriver.pro
│ │ ├── chainlinkDriver.sch
│ │ ├── fp-lib-table
│ │ └── sym-lib-table
│ ├── chainlinkDriverTester/
│ │ ├── .gitignore
│ │ ├── chainlinkDriverTester-cache.lib
│ │ ├── chainlinkDriverTester.kibot.yml
│ │ ├── chainlinkDriverTester.kicad_pcb
│ │ ├── chainlinkDriverTester.lib
│ │ ├── chainlinkDriverTester.pro
│ │ ├── chainlinkDriverTester.sch
│ │ ├── fp-lib-table
│ │ └── sym-lib-table
│ ├── lib/
│ │ ├── 54-00131.STEP
│ │ ├── 74HC125.dcm
│ │ ├── 74HC125.lib
│ │ ├── 74HC165-DIP.pretty/
│ │ │ └── 74HC165_DIP-16_W7.62mm.kicad_mod
│ │ ├── 74HC165.lib
│ │ ├── 74HC165_2.dcm
│ │ ├── 74HC165_2.lib
│ │ ├── ArduinoUnoShield.pretty/
│ │ │ └── arduino_uno_shield.kicad_mod
│ │ ├── BK-6013.models/
│ │ │ └── Memory_Protection_Devices_-_BK-6013.step
│ │ ├── BK-6013.pretty/
│ │ │ ├── BK-6013.kicad_mod
│ │ │ └── Memory_Protection_Devices-BK-6013-0-0-0.kicad_mod
│ │ ├── Buck.pretty/
│ │ │ ├── BuckModule.kicad_mod
│ │ │ └── BuckModuleBackSilk.kicad_mod
│ │ ├── BuckModule.dcm
│ │ ├── BuckModule.lib
│ │ ├── CustomPower.dcm
│ │ ├── CustomPower.lib
│ │ ├── CustomSymbols.pretty/
│ │ │ └── PolarityCenterPositive.kicad_mod
│ │ ├── DML3006LFDS.dcm
│ │ ├── DML3006LFDS.lib
│ │ ├── Dummy.pretty/
│ │ │ └── Dummy.kicad_mod
│ │ ├── ESP32.pretty/
│ │ │ ├── D1_32.kicad_mod
│ │ │ ├── T-DISPLAY.kicad_mod
│ │ │ ├── T-DISPLAY_extra_pins.kicad_mod
│ │ │ ├── T-DISPLAY_extra_pins_labeled.kicad_mod
│ │ │ └── T-DISPLAY_extra_pins_labeled_double.kicad_mod
│ │ ├── ESP32Modules.dcm
│ │ ├── ESP32Modules.lib
│ │ ├── GP2S60.pretty/
│ │ │ ├── GP2S60.kicad_mod
│ │ │ └── GP2S60_WITH_MOUNT.kicad_mod
│ │ ├── INA219.pretty/
│ │ │ ├── INA219_LARGE.kicad_mod
│ │ │ └── INA219_SMALL.kicad_mod
│ │ ├── INA219_Breakout.dcm
│ │ ├── INA219_Breakout.lib
│ │ ├── JLCPCB.pretty/
│ │ │ └── AssemblyToolingHole.kicad_mod
│ │ ├── JST_XH_Connectors.pretty/
│ │ │ └── JST_XH_2-5mm_5pin.kicad_mod
│ │ ├── LCD.dcm
│ │ ├── LCD.lib
│ │ ├── LED3mmBetterSilkScreen.pretty/
│ │ │ └── LED_D3.0mm.kicad_mod
│ │ ├── LM339.dcm
│ │ ├── LM339.lib
│ │ ├── LevelShifterModule.dcm
│ │ ├── LevelShifterModule.lib
│ │ ├── LevelShifterModule.pretty/
│ │ │ └── LevelShifterModule.kicad_mod
│ │ ├── MIC5842.dcm
│ │ ├── MIC5842.lib
│ │ ├── Mega2560Shield.dcm
│ │ ├── Mega2560Shield.lib
│ │ ├── Mega2560Shield.pretty/
│ │ │ ├── Mega2560Shield.kicad_mod
│ │ │ └── Mega2560Shield_Modified.kicad_mod
│ │ ├── ModifiedSymbols.pretty/
│ │ │ ├── IDC-Header_2x04_P2.54mm_Vertical.kicad_mod
│ │ │ ├── LED_0603_1608Metric_Silkscreen.kicad_mod
│ │ │ ├── LED_0805_2012Metric_Silkscreen.kicad_mod
│ │ │ ├── LED_WS2812B_PLCC4_5.0x5.0mm_P3.2mm.kicad_mod
│ │ │ ├── PinHeader_1x03_P2.54mm_Vertical_SolderJumper.kicad_mod
│ │ │ ├── PinHeader_1x05_P2.54mm_Vertical_NoSilk.kicad_mod
│ │ │ ├── PinHoles_1x04_P2.54mm_NoSilk.kicad_mod
│ │ │ ├── Pin_Header_Right_Angle_1x03.kicad_mod
│ │ │ ├── SOIC-14_3.9x8.7mm_P1.27mm_silk.kicad_mod
│ │ │ ├── SOIC-16_3.9x9.9mm_P1.27mm_silk.kicad_mod
│ │ │ └── TO-220-3_Vertical.kicad_mod
│ │ ├── MountingHoles.pretty/
│ │ │ ├── M4_mount.kicad_mod
│ │ │ ├── M4_mount_2mm_play.kicad_mod
│ │ │ └── M4_mount_4mm_play.kicad_mod
│ │ ├── NCP45560.dcm
│ │ ├── NCP45560.lib
│ │ ├── PJ-202A.pretty/
│ │ │ └── PJ-202A.kicad_mod
│ │ ├── PinHeaders.pretty/
│ │ │ ├── Pin_Header_Straight_1x03.kicad_mod
│ │ │ ├── Pin_Header_Straight_1x04.kicad_mod
│ │ │ └── Pin_Header_Straight_2x07_Pitch2.54mm_IDC_Shrouded.kicad_mod
│ │ ├── Pogo.models/
│ │ │ ├── pogo1mmPoint.step
│ │ │ └── pogo2mmCup.step
│ │ ├── RS485.pretty/
│ │ │ └── RS485Module.kicad_mod
│ │ ├── RS485Module.dcm
│ │ ├── RS485Module.lib
│ │ ├── Resistor0805ThroughHole.pretty/
│ │ │ └── R_0805_ThroughHole.kicad_mod
│ │ ├── SSD1306.pretty/
│ │ │ └── SSD1306_128x32.kicad_mod
│ │ ├── ST7789.pretty/
│ │ │ ├── ST7789_240x240.kicad_mod
│ │ │ └── ST7789_80x160.kicad_mod
│ │ ├── ScrewTerminals.pretty/
│ │ │ ├── C72334_WJ500V-5.08-3P.kicad_mod
│ │ │ ├── C8465_WJ500V-5.08-2P.kicad_mod
│ │ │ ├── EB147A-02-D.kicad_mod
│ │ │ ├── Generic-5.08-2P.kicad_mod
│ │ │ └── Generic-5.08-3P.kicad_mod
│ │ ├── SolderJumpers.pretty/
│ │ │ └── SolderJumper-2_P1.3mm_Bridged_Pad1.0x1.5mm.kicad_mod
│ │ ├── SwitchESE13.pretty/
│ │ │ └── ESE13V01D.kicad_mod
│ │ ├── TTGO.models/
│ │ │ └── TTGO_TDisplay.step
│ │ ├── ULN2003AModule.dcm
│ │ ├── ULN2003AModule.lib
│ │ ├── ULN2003AModule.pretty/
│ │ │ ├── ULN2003AModule.kicad_mod
│ │ │ ├── ULN2003AModule_No_EN.kicad_mod
│ │ │ └── ULN2003AModule_No_EN_No_FSilk.kicad_mod
│ │ ├── VN7003ALHTR.models/
│ │ │ └── STMicroelectronics_-_VN7003ALHTR.step
│ │ ├── VN7007ALHTR.dcm
│ │ ├── VN7007ALHTR.lib
│ │ ├── VN7007ALHTR.pretty/
│ │ │ └── VN7007ALHTR.kicad_mod
│ │ ├── arduino_uno_shield.dcm
│ │ ├── arduino_uno_shield.lib
│ │ ├── gp2s60.dcm
│ │ ├── gp2s60.lib
│ │ ├── hall_effect.dcm
│ │ ├── hall_effect.lib
│ │ ├── hall_effect.pretty/
│ │ │ ├── hall_effect.kicad_mod
│ │ │ └── hall_effect_wide.kicad_mod
│ │ ├── mount.dcm
│ │ ├── mount.lib
│ │ ├── no_pin.dcm
│ │ ├── no_pin.lib
│ │ ├── oled.dcm
│ │ ├── oled.lib
│ │ ├── pogoPins.pretty/
│ │ │ ├── pogo1mm.kicad_mod
│ │ │ └── pogo2mm.kicad_mod
│ │ ├── screw_terminal_01x03_power_output.dcm
│ │ ├── screw_terminal_01x03_power_output.lib
│ │ ├── sensor_smd_lib.pretty/
│ │ │ ├── SolderJumper-2_P1.3mm_Bridged_Pad1.0x1.5mm.kicad_mod
│ │ │ ├── header_3.kicad_mod
│ │ │ ├── header_3_smd.kicad_mod
│ │ │ └── hole_9.4.kicad_mod
│ │ ├── ws2812b.dcm
│ │ └── ws2812b.lib
│ ├── scripts/
│ │ ├── __init__.py
│ │ ├── config/
│ │ │ ├── eeschema
│ │ │ ├── kikit_panelize_classic.json
│ │ │ ├── kikit_panelize_sensor.json
│ │ │ ├── pcbnew
│ │ │ └── policy.xml
│ │ ├── dependencies.sh
│ │ ├── dependencies_v2.sh
│ │ ├── export_3d.py
│ │ ├── export_jlcpcb.py
│ │ ├── export_schematic.py
│ │ ├── export_util.py
│ │ ├── generate_pdf.py
│ │ ├── generate_svg.py
│ │ ├── pcb_util.py
│ │ └── svg_processor.py
│ ├── sensor/
│ │ ├── fp-lib-table
│ │ ├── sensor-cache.lib
│ │ ├── sensor.kicad_pcb
│ │ ├── sensor.pro
│ │ ├── sensor.sch
│ │ └── sym-lib-table
│ └── sensor_smd/
│ ├── fp-lib-table
│ ├── kikit_panelize.json
│ ├── sensor_smd-panelized.kibot.yml
│ ├── sensor_smd.kibot.yml
│ ├── sensor_smd.kicad_pcb
│ ├── sensor_smd.kicad_prl
│ ├── sensor_smd.kicad_pro
│ └── sensor_smd.kicad_sch
├── firmware/
│ ├── .gitignore
│ ├── buildscript_build_info_macros.py
│ ├── esp32/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── base/
│ │ │ ├── base_config.h
│ │ │ ├── base_supervisor_task.cpp
│ │ │ └── base_supervisor_task.h
│ │ ├── core/
│ │ │ ├── common.h
│ │ │ ├── configuration.cpp
│ │ │ ├── configuration.h
│ │ │ ├── logger.h
│ │ │ ├── recursive_semaphore_guard.h
│ │ │ ├── semaphore_guard.h
│ │ │ ├── splitflap_task.cpp
│ │ │ ├── splitflap_task.h
│ │ │ ├── task.h
│ │ │ ├── uart_stream.cpp
│ │ │ └── uart_stream.h
│ │ ├── proto_gen/
│ │ │ ├── splitflap.pb.c
│ │ │ └── splitflap.pb.h
│ │ ├── splitflap/
│ │ │ ├── crc32.cpp
│ │ │ ├── crc32.h
│ │ │ ├── debug_build_info.cpp
│ │ │ ├── debug_build_info.h
│ │ │ ├── display_layouts.h
│ │ │ ├── display_task.cpp
│ │ │ ├── display_task.h
│ │ │ ├── http_task.cpp
│ │ │ ├── http_task.h
│ │ │ ├── main.cpp
│ │ │ ├── mqtt_task.cpp
│ │ │ ├── mqtt_task.h
│ │ │ ├── secrets.h.example
│ │ │ ├── serial_legacy_json_protocol.cpp
│ │ │ ├── serial_legacy_json_protocol.h
│ │ │ ├── serial_proto_protocol.cpp
│ │ │ ├── serial_proto_protocol.h
│ │ │ ├── serial_protocol.h
│ │ │ ├── serial_task.cpp
│ │ │ └── serial_task.h
│ │ └── tester/
│ │ ├── base64url.cpp
│ │ ├── base64url.h
│ │ ├── firestore.cpp
│ │ ├── firestore.h
│ │ ├── firestore_test_reporter.cpp
│ │ ├── firestore_test_reporter.h
│ │ ├── jwt.cpp
│ │ ├── jwt.h
│ │ ├── main.cpp
│ │ ├── result.h
│ │ ├── secrets.h.example
│ │ ├── tester_task.cpp
│ │ └── tester_task.h
│ ├── include/
│ │ └── README
│ ├── lib/
│ │ ├── README
│ │ └── json11/
│ │ ├── CMakeLists.txt
│ │ ├── LICENSE.txt
│ │ ├── Makefile
│ │ ├── README.md
│ │ ├── json11.cpp
│ │ ├── json11.hpp
│ │ ├── json11.pc.in
│ │ └── test.cpp
│ ├── src/
│ │ ├── Adafruit_INA219.cpp
│ │ ├── Adafruit_INA219.h
│ │ ├── acceleration.h
│ │ ├── basic_io_config.h
│ │ ├── config.h
│ │ ├── generate_acceleration.py
│ │ ├── spi_io_config.h
│ │ ├── splitflap_module.h
│ │ └── splitflap_module_data.h
│ └── test/
│ └── README
├── platformio.ini
├── proto/
│ ├── generate_protobuf.py
│ └── splitflap.proto
├── scripts/
│ ├── annotate_image.sh
│ └── video_thumb/
│ └── generate_thumbnail.sh
├── software/
│ ├── chainlink/
│ │ ├── README.md
│ │ ├── demo.py
│ │ ├── js/
│ │ │ ├── .gitignore
│ │ │ ├── .npmrc
│ │ │ ├── README.md
│ │ │ ├── package.json
│ │ │ └── packages/
│ │ │ ├── example-node-cli/
│ │ │ │ ├── .eslintrc
│ │ │ │ ├── .prettierrc
│ │ │ │ ├── package.json
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ └── tsconfig.json
│ │ │ ├── example-webserial-basic/
│ │ │ │ ├── .eslintrc
│ │ │ │ ├── .gitignore
│ │ │ │ ├── .prettierrc
│ │ │ │ ├── README.md
│ │ │ │ ├── package.json
│ │ │ │ ├── public/
│ │ │ │ │ ├── 3d_viewer/
│ │ │ │ │ │ ├── css/
│ │ │ │ │ │ │ └── style.css
│ │ │ │ │ │ ├── index.html
│ │ │ │ │ │ └── js/
│ │ │ │ │ │ ├── OrbitControls.js
│ │ │ │ │ │ ├── STLLoader.js
│ │ │ │ │ │ ├── WebGL.js
│ │ │ │ │ │ ├── three.js
│ │ │ │ │ │ ├── url-search-params/
│ │ │ │ │ │ │ ├── LICENSE
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ └── viewer.js
│ │ │ │ │ ├── embed.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── robots.txt
│ │ │ │ ├── src/
│ │ │ │ │ ├── App.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── react-app-env.d.ts
│ │ │ │ │ └── util.ts
│ │ │ │ └── tsconfig.json
│ │ │ ├── splitflapjs-core/
│ │ │ │ ├── .eslintrc
│ │ │ │ ├── .prettierrc
│ │ │ │ ├── package.json
│ │ │ │ ├── src/
│ │ │ │ │ ├── cobs.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── util.ts
│ │ │ │ └── tsconfig.json
│ │ │ ├── splitflapjs-node/
│ │ │ │ ├── .eslintrc
│ │ │ │ ├── .prettierrc
│ │ │ │ ├── package.json
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ └── tsconfig.json
│ │ │ ├── splitflapjs-proto/
│ │ │ │ └── package.json
│ │ │ └── splitflapjs-webserial/
│ │ │ ├── .eslintrc
│ │ │ ├── .prettierrc
│ │ │ ├── package.json
│ │ │ ├── src/
│ │ │ │ └── index.ts
│ │ │ └── tsconfig.json
│ │ ├── proto_gen/
│ │ │ ├── nanopb_pb2.py
│ │ │ └── splitflap_pb2.py
│ │ ├── requirements.txt
│ │ └── splitflap_proto.py
│ └── classic/
│ ├── demo.py
│ ├── flap_test.py
│ ├── splitflap.py
│ └── terminal.py
├── thirdparty/
│ ├── README
│ ├── __init__.py
│ ├── panelize.py
│ └── xvfbwrapper/
│ ├── LICENSE.txt
│ ├── __init__.py
│ └── xvfbwrapper.py
└── util/
├── __init__.py
├── app_paths.py
├── file_util.py
├── inkscape.py
└── rev_info.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: scottbez1
================================================
FILE: .github/workflows/3d.yml
================================================
name: Render 3D Designs
on:
push:
pull_request:
jobs:
render-3d:
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up outputs directory
run: mkdir -p 3d/build/outputs
- name: Install dependencies
run: ./3d/scripts/dependencies.sh
- name: 'Generate front panel example :: 52 flap :: 6x1 :: Elecrow acrylic kerf'
run: |
xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" python3 -u 3d/scripts/generate_combined_front_panel.py --kerf-preset elecrow-3mm-acrylic --rows 1 --cols 6 --spacing-x 0 --spacing-y 0 --frame-margin-x 0 --frame-margin-y 0 --center-mode module --num-flaps 52 --render-raster
cp 3d/build/front_panel/combined.svg 3d/build/outputs/3d_front_panel-52-elecrow-3mm-acrylic-6x1.svg
cp 3d/build/front_panel/raster.png 3d/build/outputs/3d_front_panel_raster-52-elecrow-3mm-acrylic-6x1.png
./scripts/annotate_image.sh 3d/build/outputs/3d_front_panel_raster-52-elecrow-3mm-acrylic-6x1.png
- name: 'Generate front panel example :: 52 flap :: 12x2 :: 1/8 in tool diameter :: frame margin=20mm, 4mm'
run: |
xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" python3 -u 3d/scripts/generate_combined_front_panel.py --tool-diameter 3.175 --rows 2 --cols 12 --spacing-x 0 --spacing-y 0 --frame-margin-x 20 --frame-margin-y 4 --center-mode module --num-flaps 52 --render-raster
cp 3d/build/front_panel/combined.svg 3d/build/outputs/3d_front_panel-52-3.175-20x4margin-12x2.svg
cp 3d/build/front_panel/raster.png 3d/build/outputs/3d_front_panel_raster-52-3.175-20x4margin-12x2.png
./scripts/annotate_image.sh 3d/build/outputs/3d_front_panel_raster-52-3.175-20x4margin-12x2.png
- name: 'Generate font example :: 52 flap :: Epilogue :: 1mm bleed'
run: |
xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" python3 -u 3d/scripts/generate_fonts.py --mode side-by-side --font Epilogue --columns 4 --bleed 1 --fill --render-raster
cp 3d/build/fonts/combined.svg 3d/build/outputs/font_example-Epilogue-1mmBleed.svg
cp 3d/build/fonts/raster.png 3d/build/outputs/font_example-Epilogue-1mmBleed.png
./scripts/annotate_image.sh 3d/build/outputs/font_example-Epilogue-1mmBleed.png
- name: 'Generate 2d output :: 52 flap :: generic'
run: |
xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" python -u 3d/scripts/generate_2d.py --calculate-dimensions --render-raster
cp 3d/build/laser_parts/combined.svg 3d/build/outputs/3d_laser_vector-52.svg
cp 3d/build/laser_parts/raster.png 3d/build/outputs/3d_laser_raster-52.png
./scripts/annotate_image.sh 3d/build/outputs/3d_laser_raster-52.png
cp 3d/build/laser_parts/combined_module_dimensions.svg 3d/build/outputs/3d_laser_vector-52-module_dimensions.svg
- name: 'Generate 2d output :: 52 flap :: Ponoko 3mm MDF'
run: |
xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" python3 -u 3d/scripts/generate_2d.py --calculate-dimensions --kerf-preset ponoko-3mm-mdf
cp 3d/build/laser_parts/combined.svg 3d/build/outputs/3d_laser_vector-52-ponoko-3mm-mdf_1x.svg
cp 3d/build/laser_parts/combined_panel_dimensions.svg 3d/build/outputs/3d_laser_vector-52-ponoko-3mm-mdf_1x_dimensions.svg
- name: 'Generate 2d output :: 52 flap :: Ponoko 3mm Acrylic'
run: |
xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" python3 -u 3d/scripts/generate_2d.py --calculate-dimensions --kerf-preset ponoko-3mm-acrylic
cp 3d/build/laser_parts/combined.svg 3d/build/outputs/3d_laser_vector-52-ponoko-3mm-acrylic_1x.svg
cp 3d/build/laser_parts/combined_panel_dimensions.svg 3d/build/outputs/3d_laser_vector-52-ponoko-3mm-acrylic_1x_dimensions.svg
- name: 'Generate 2d output :: 52 flap :: Elecrow 3mm Wood'
run: |
xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" python3 -u 3d/scripts/generate_2d.py --kerf-preset elecrow-3mm-wood --render-elecrow
cp 3d/build/laser_parts/combined.svg 3d/build/outputs/3d_laser_vector-52-elecrow-3mm-wood_1x.svg
cp 3d/build/laser_parts/combined_panel_dimensions.svg 3d/build/outputs/3d_laser_vector-52-elecrow-3mm-wood_1x_dimensions.svg
cp 3d/build/laser_parts/elecrow.zip 3d/build/outputs/3d_laser_vector-52-elecrow-3mm-wood_1x.zip
- name: 'Generate 2d output :: 52 flap :: Elecrow 3mm Acrylic'
run: |
xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" python3 -u 3d/scripts/generate_2d.py --kerf-preset elecrow-3mm-acrylic --render-elecrow
cp 3d/build/laser_parts/combined.svg 3d/build/outputs/3d_laser_vector-52-elecrow-3mm-acrylic_1x.svg
cp 3d/build/laser_parts/combined_panel_dimensions.svg 3d/build/outputs/3d_laser_vector-52-elecrow-3mm-acrylic_1x_dimensions.svg
cp 3d/build/laser_parts/elecrow.zip 3d/build/outputs/3d_laser_vector-52-elecrow-3mm-acrylic_1x.zip
- name: 'Generate 2d output :: 40 flap :: generic'
run: |
xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" python -u 3d/scripts/generate_2d.py --calculate-dimensions --render-raster --num-flaps 40
cp 3d/build/laser_parts/combined.svg 3d/build/outputs/3d_laser_vector-40.svg
cp 3d/build/laser_parts/raster.png 3d/build/outputs/3d_laser_raster-40.png
./scripts/annotate_image.sh 3d/build/outputs/3d_laser_raster-40.png
cp 3d/build/laser_parts/combined_module_dimensions.svg 3d/build/outputs/3d_laser_vector-40-module_dimensions.svg
- name: 'Generate 2d output :: 40 flap :: Ponoko 3mm MDF)'
run: |
xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" python3 -u 3d/scripts/generate_2d.py --calculate-dimensions --kerf-preset ponoko-3mm-mdf --num-flaps 40
cp 3d/build/laser_parts/combined.svg 3d/build/outputs/3d_laser_vector-40-ponoko-3mm-mdf_1x.svg
cp 3d/build/laser_parts/combined_panel_dimensions.svg 3d/build/outputs/3d_laser_vector-40-ponoko-3mm-mdf_1x_dimensions.svg
- name: 'Generate 2d output :: 40 flap :: Ponoko 3mm Acrylic'
run: |
xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" python3 -u 3d/scripts/generate_2d.py --calculate-dimensions --kerf-preset ponoko-3mm-acrylic --num-flaps 40
cp 3d/build/laser_parts/combined.svg 3d/build/outputs/3d_laser_vector-40-ponoko-3mm-acrylic_1x.svg
cp 3d/build/laser_parts/combined_panel_dimensions.svg 3d/build/outputs/3d_laser_vector-40-ponoko-3mm-acrylic_1x_dimensions.svg
- name: 'Generate 2d output :: 40 flap :: Elecrow 3mm Wood'
run: |
xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" python3 -u 3d/scripts/generate_2d.py --kerf-preset elecrow-3mm-wood --render-elecrow --num-flaps 40
cp 3d/build/laser_parts/combined.svg 3d/build/outputs/3d_laser_vector-40-elecrow-3mm-wood_1x.svg
cp 3d/build/laser_parts/combined_panel_dimensions.svg 3d/build/outputs/3d_laser_vector-40-elecrow-3mm-wood_1x_dimensions.svg
cp 3d/build/laser_parts/elecrow.zip 3d/build/outputs/3d_laser_vector-40-elecrow-3mm-wood_1x.zip
- name: 'Generate 2d output :: 40 flap :: Elecrow 3mm Acrylic'
run: |
xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" python3 -u 3d/scripts/generate_2d.py --kerf-preset elecrow-3mm-acrylic --render-elecrow --num-flaps 40
cp 3d/build/laser_parts/combined.svg 3d/build/outputs/3d_laser_vector-40-elecrow-3mm-acrylic_1x.svg
cp 3d/build/laser_parts/combined_panel_dimensions.svg 3d/build/outputs/3d_laser_vector-40-elecrow-3mm-acrylic_1x_dimensions.svg
cp 3d/build/laser_parts/elecrow.zip 3d/build/outputs/3d_laser_vector-40-elecrow-3mm-acrylic_1x.zip
- name: Generate animated gif
run: |
xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" python3 -u 3d/scripts/generate_gif.py
cp 3d/build/animation/animation.gif 3d/build/outputs/3d_animation.gif
cp 3d/build/animation/all_flaps.gif 3d/build/outputs/all_flaps.gif
- name: Generate STLs for web viewer
run: |
python -u 3d/scripts/generate_stl.py
cp -r 3d/build/colored_stl 3d/build/outputs/3d_colored_stl
gzip 3d/build/outputs/3d_colored_stl/*.stl
- name: Archive artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: 3d
path: |
3d/build
- name: Configure AWS Credentials
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Sync artifacts to S3
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
run: |
aws s3 sync 3d/build/outputs s3://splitflap-artifacts/${GITHUB_REF#refs/heads/}/3d --exclude '3d_colored_stl/*.stl.gz' --delete --acl public-read --cache-control max-age=0,no-cache
aws s3 sync 3d/build/outputs s3://splitflap-artifacts/${GITHUB_REF#refs/heads/}/3d --exclude '*' --include '3d_colored_stl/*.stl.gz' --delete --content-encoding gzip --acl public-read --cache-control max-age=0,no-cache
================================================
FILE: .github/workflows/electronics.yml
================================================
name: Export Electronics
on:
push:
pull_request:
jobs:
export-electronics-classic:
name: Export Electronics (Sensor)
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up outputs directory
run: mkdir -p electronics/build/outputs
- name: Install dependencies
run: ./electronics/scripts/dependencies.sh
# Sensor board:
- name: Export sensor schematic
run: |
./electronics/scripts/export_schematic.py electronics/sensor/sensor.sch
cp electronics/build/sensor.pdf electronics/build/outputs/sensor-schematic.pdf
cp electronics/build/sensor.png electronics/build/outputs/sensor-schematic.png
env:
PYTHONUNBUFFERED: 1
- name: Export sensor PCB fabrication files
run: |
./electronics/scripts/export_jlcpcb.py electronics/sensor/sensor.kicad_pcb
cp -r electronics/build/sensor-jlc electronics/build/outputs/sensor-jlc
- name: Export sensor PCB overview PDF
run: |
./electronics/scripts/generate_pdf.py electronics/sensor/sensor.kicad_pcb
cp electronics/build/sensor-pcb-packet.pdf electronics/build/outputs/sensor-pcb-packet.pdf
env:
PYTHONUNBUFFERED: 1
- name: Panelize sensor PCB
run: |
kikit panelize -p electronics/scripts/config/kikit_panelize_sensor.json electronics/sensor/sensor.kicad_pcb electronics/build/panelized_sensor.kicad_pcb
- name: Export panelized sensor PCB SVG
run: |
./electronics/scripts/generate_svg.py electronics/build/panelized_sensor.kicad_pcb
cp electronics/build/panelized_sensor_merged.png electronics/build/outputs/sensor-panelized-pcb-raster.png
./scripts/annotate_image.sh electronics/build/outputs/sensor-panelized-pcb-raster.png
env:
PYTHONUNBUFFERED: 1
- name: Export panelized sensor PCB fabrication files
run: |
./electronics/scripts/export_jlcpcb.py electronics/build/panelized_sensor.kicad_pcb
cp -r electronics/build/panelized_sensor-jlc electronics/build/outputs/sensor-panelized-jlc
- name: Export panelized sensor PCB overview PDF
run: |
./electronics/scripts/generate_pdf.py electronics/build/panelized_sensor.kicad_pcb
cp electronics/build/panelized_sensor-pcb-packet.pdf electronics/build/outputs/sensor-panelized-pcb-packet.pdf
env:
PYTHONUNBUFFERED: 1
- name: Archive artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: electronics-classic
path: |
electronics/build
- name: Configure AWS Credentials
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Sync artifacts to S3
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
run: |
aws s3 sync electronics/build/outputs s3://splitflap-artifacts/${GITHUB_REF#refs/heads/}/electronics-classic --delete --acl public-read --cache-control max-age=0,no-cache
export-electronics-chainlink:
name: Export Electronics (Chainlink Driver)
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up outputs directory
run: mkdir -p electronics/build/outputs
- name: Install dependencies
run: ./electronics/scripts/dependencies.sh
# Chainlink Driver:
- name: Export chainlinkDriver schematic
run: |
./electronics/scripts/export_schematic.py electronics/chainlinkDriver/chainlinkDriver.sch
cp electronics/build/chainlinkDriver.pdf electronics/build/outputs/chainlinkDriver-schematic.pdf
cp electronics/build/chainlinkDriver.png electronics/build/outputs/chainlinkDriver-schematic.png
env:
PYTHONUNBUFFERED: 1
- name: Render chainlinkDriver PCB 3D
run: |
./electronics/scripts/export_3d.py electronics/chainlinkDriver/chainlinkDriver.kicad_pcb --width 2000 --height 800 transform z+ z+ z+ z+ z+ z+ z+ z+ mr rx+ rx+ ry- ry- rz-
cp electronics/build/chainlinkDriver-3d.png electronics/build/outputs/chainlinkDriver-3d.png
./scripts/annotate_image.sh electronics/build/outputs/chainlinkDriver-3d.png
env:
PYTHONUNBUFFERED: 1
- name: Export chainlinkDriver JLCPCB fabrication files
run: |
./electronics/scripts/export_jlcpcb.py electronics/chainlinkDriver/chainlinkDriver.kicad_pcb --assembly-schematic electronics/chainlinkDriver/chainlinkDriver.sch --alt-fields LCSC_ALT_ULN2003A
cp -r electronics/build/chainlinkDriver-jlc electronics/build/outputs
- name: Export chainlinkDriver PCB overview PDF
run: |
./electronics/scripts/generate_pdf.py electronics/chainlinkDriver/chainlinkDriver.kicad_pcb
cp electronics/build/chainlinkDriver-pcb-packet.pdf electronics/build/outputs/chainlinkDriver-pcb-packet.pdf
env:
PYTHONUNBUFFERED: 1
- name: Export chainlinkDriver PCB SVG
run: |
./electronics/scripts/generate_svg.py electronics/chainlinkDriver/chainlinkDriver.kicad_pcb
cp electronics/build/chainlinkDriver_merged.png electronics/build/outputs/chainlinkDriver-pcb-raster.png
./scripts/annotate_image.sh electronics/build/outputs/chainlinkDriver-pcb-raster.png
env:
PYTHONUNBUFFERED: 1
# Note: These kibot scripts are run last because they mess with file permissions which breaks other scripts, not sure why
- name: Export BOM [chainlinkDriver]
uses: INTI-CMNB/KiBot@c723b51327c2d9b7863672698ae5fd3da58b386e
with:
config: electronics/chainlinkDriver/chainlinkDriver.kibot.yml
dir: electronics/build
schema: 'electronics/chainlinkDriver/chainlinkDriver.sch'
board: 'electronics/chainlinkDriver/chainlinkDriver.kicad_pcb'
- name: Copy BOM outputs
run: cp -r electronics/build/bom electronics/build/outputs
- name: Archive artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: electronics-chainlink
path: |
electronics/build
- name: Configure AWS Credentials
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Sync artifacts to S3
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
run: |
aws s3 sync electronics/build/outputs s3://splitflap-artifacts/${GITHUB_REF#refs/heads/}/electronics-chainlink --delete --acl public-read --cache-control max-age=0,no-cache
export-electronics-chainlink-tester:
name: Export Electronics (Chainlink Driver Tester)
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up outputs directory
run: mkdir -p electronics/build/outputs
- name: Install dependencies
run: ./electronics/scripts/dependencies.sh
# Chainlink Driver Tester:
- name: Export schematic [chainlinkDriverTester]
run: |
./electronics/scripts/export_schematic.py electronics/chainlinkDriverTester/chainlinkDriverTester.sch
cp electronics/build/chainlinkDriverTester.pdf electronics/build/outputs/chainlinkDriverTester-schematic.pdf
cp electronics/build/chainlinkDriverTester.png electronics/build/outputs/chainlinkDriverTester-schematic.png
env:
PYTHONUNBUFFERED: 1
- name: Render PCB 3D [chainlinkDriverTester]
run: |
./electronics/scripts/export_3d.py electronics/chainlinkDriverTester/chainlinkDriverTester.kicad_pcb --width 1600 --height 1100 transform z+ z+ z+ z+ z+ mu mr mr rx+ rx+ ry- ry- rz-
cp electronics/build/chainlinkDriverTester-3d.png electronics/build/outputs/chainlinkDriverTester-3d.png
./scripts/annotate_image.sh electronics/build/outputs/chainlinkDriverTester-3d.png
env:
PYTHONUNBUFFERED: 1
- name: Export JLCPCB fabrication files [chainlinkDriverTester]
run: |
./electronics/scripts/export_jlcpcb.py electronics/chainlinkDriverTester/chainlinkDriverTester.kicad_pcb
cp -r electronics/build/chainlinkDriverTester-jlc electronics/build/outputs
- name: Export PCB overview PDF [chainlinkDriverTester]
run: |
./electronics/scripts/generate_pdf.py electronics/chainlinkDriverTester/chainlinkDriverTester.kicad_pcb
cp electronics/build/chainlinkDriverTester-pcb-packet.pdf electronics/build/outputs/chainlinkDriverTester-pcb-packet.pdf
env:
PYTHONUNBUFFERED: 1
- name: Export PCB SVG [chainlinkDriverTester]
run: |
./electronics/scripts/generate_svg.py electronics/chainlinkDriverTester/chainlinkDriverTester.kicad_pcb
cp electronics/build/chainlinkDriverTester_merged.png electronics/build/outputs/chainlinkDriverTester-pcb-raster.png
./scripts/annotate_image.sh electronics/build/outputs/chainlinkDriverTester-pcb-raster.png
env:
PYTHONUNBUFFERED: 1
# Note: These kibot scripts are run last because they mess with file permissions which breaks other scripts, not sure why
- name: Export BOM [chainlinkDriverTester]
uses: INTI-CMNB/KiBot@c723b51327c2d9b7863672698ae5fd3da58b386e
with:
config: electronics/chainlinkDriverTester/chainlinkDriverTester.kibot.yml
dir: electronics/build
schema: 'electronics/chainlinkDriverTester/chainlinkDriverTester.sch'
board: 'electronics/chainlinkDriverTester/chainlinkDriverTester.kicad_pcb'
- name: Copy BOM outputs
run: cp -r electronics/build/bom electronics/build/outputs
- name: Archive artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: electronics-chainlink-tester
path: |
electronics/build
- name: Configure AWS Credentials
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Sync artifacts to S3
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
run: |
aws s3 sync electronics/build/outputs s3://splitflap-artifacts/${GITHUB_REF#refs/heads/}/electronics-chainlink-tester --delete --acl public-read --cache-control max-age=0,no-cache
export-electronics-chainlink-base:
name: Export Electronics (Chainlink Base)
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up outputs directory
run: mkdir -p electronics/build/outputs
- name: Install dependencies
run: ./electronics/scripts/dependencies.sh
# Chainlink Base:
- name: Export schematic [chainlinkBase]
run: |
./electronics/scripts/export_schematic.py electronics/chainlinkBase/chainlinkBase.sch
cp electronics/build/chainlinkBase.pdf electronics/build/outputs/chainlinkBase-schematic.pdf
cp electronics/build/chainlinkBase-0.png electronics/build/outputs/chainlinkBase-schematic.png
env:
PYTHONUNBUFFERED: 1
- name: Render PCB 3D [chainlinkBase]
run: |
./electronics/scripts/export_3d.py electronics/chainlinkBase/chainlinkBase.kicad_pcb --width 1000 --height 1500 transform z+ z+ mu rx+ rx+ ry- ry- rz-
cp electronics/build/chainlinkBase-3d.png electronics/build/outputs/chainlinkBase-3d.png
./scripts/annotate_image.sh electronics/build/outputs/chainlinkBase-3d.png
env:
PYTHONUNBUFFERED: 1
- name: Export JLCPCB fabrication files [chainlinkBase]
run: |
./electronics/scripts/export_jlcpcb.py electronics/chainlinkBase/chainlinkBase.kicad_pcb
cp -r electronics/build/chainlinkBase-jlc electronics/build/outputs
- name: Export PCB overview PDF [chainlinkBase]
run: |
./electronics/scripts/generate_pdf.py electronics/chainlinkBase/chainlinkBase.kicad_pcb
cp electronics/build/chainlinkBase-pcb-packet.pdf electronics/build/outputs/chainlinkBase-pcb-packet.pdf
env:
PYTHONUNBUFFERED: 1
- name: Export PCB SVG [chainlinkBase]
run: |
./electronics/scripts/generate_svg.py electronics/chainlinkBase/chainlinkBase.kicad_pcb
cp electronics/build/chainlinkBase_merged.png electronics/build/outputs/chainlinkBase-pcb-raster.png
./scripts/annotate_image.sh electronics/build/outputs/chainlinkBase-pcb-raster.png
env:
PYTHONUNBUFFERED: 1
# Note: These kibot scripts are run last because they mess with file permissions which breaks other scripts, not sure why
- name: Export BOM [chainlinkBase]
uses: INTI-CMNB/KiBot@c723b51327c2d9b7863672698ae5fd3da58b386e
with:
config: electronics/chainlinkBase/chainlinkBase.kibot.yml
dir: electronics/build
schema: 'electronics/chainlinkBase/chainlinkBase.sch'
board: 'electronics/chainlinkBase/chainlinkBase.kicad_pcb'
- name: Copy BOM outputs
run: cp -r electronics/build/bom electronics/build/outputs
- name: Archive artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: electronics-chainlink-base
path: |
electronics/build
- name: Configure AWS Credentials
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Sync artifacts to S3
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
run: |
aws s3 sync electronics/build/outputs s3://splitflap-artifacts/${GITHUB_REF#refs/heads/}/electronics-chainlink-base --delete --acl public-read --cache-control max-age=0,no-cache
export-electronics-chainlink-buddy-t-display:
name: Export Electronics (Chainlink Buddy T-Display)
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up outputs directory
run: mkdir -p electronics/build/outputs
- name: Install dependencies
run: ./electronics/scripts/dependencies.sh
- name: Export schematic [chainlinkBuddyTDisplay]
run: |
./electronics/scripts/export_schematic.py electronics/chainlinkBuddyTDisplay/chainlinkBuddyTDisplay.sch
cp electronics/build/chainlinkBuddyTDisplay.pdf electronics/build/outputs/chainlinkBuddyTDisplay-schematic.pdf
cp electronics/build/chainlinkBuddyTDisplay.png electronics/build/outputs/chainlinkBuddyTDisplay-schematic.png
env:
PYTHONUNBUFFERED: 1
- name: Render PCB 3D [chainlinkBuddyTDisplay]
run: |
./electronics/scripts/export_3d.py electronics/chainlinkBuddyTDisplay/chainlinkBuddyTDisplay.kicad_pcb --width 1600 --height 1100 transform z+ z+ z+ z+ mr mr mr rx+ rx+ ry- ry- rz-
cp electronics/build/chainlinkBuddyTDisplay-3d.png electronics/build/outputs/chainlinkBuddyTDisplay-3d.png
./scripts/annotate_image.sh electronics/build/outputs/chainlinkBuddyTDisplay-3d.png
env:
PYTHONUNBUFFERED: 1
- name: Export JLCPCB fabrication files [chainlinkBuddyTDisplay]
run: |
./electronics/scripts/export_jlcpcb.py electronics/chainlinkBuddyTDisplay/chainlinkBuddyTDisplay.kicad_pcb
cp -r electronics/build/chainlinkBuddyTDisplay-jlc electronics/build/outputs
- name: Export PCB overview PDF [chainlinkBuddyTDisplay]
run: |
./electronics/scripts/generate_pdf.py electronics/chainlinkBuddyTDisplay/chainlinkBuddyTDisplay.kicad_pcb
cp electronics/build/chainlinkBuddyTDisplay-pcb-packet.pdf electronics/build/outputs/chainlinkBuddyTDisplay-pcb-packet.pdf
env:
PYTHONUNBUFFERED: 1
- name: Export PCB SVG [chainlinkBuddyTDisplay]
run: |
./electronics/scripts/generate_svg.py electronics/chainlinkBuddyTDisplay/chainlinkBuddyTDisplay.kicad_pcb
cp electronics/build/chainlinkBuddyTDisplay_merged.png electronics/build/outputs/chainlinkBuddyTDisplay-pcb-raster.png
./scripts/annotate_image.sh electronics/build/outputs/chainlinkBuddyTDisplay-pcb-raster.png
env:
PYTHONUNBUFFERED: 1
- name: Panelize
run: |
kikit panelize -p electronics/chainlinkBuddyTDisplay/kikit_panelize.json electronics/chainlinkBuddyTDisplay/chainlinkBuddyTDisplay.kicad_pcb electronics/build/panelized_chainlinkBuddyTDisplay.kicad_pcb
- name: Export panelized PCB SVG
run: |
./electronics/scripts/generate_svg.py electronics/build/panelized_chainlinkBuddyTDisplay.kicad_pcb
cp electronics/build/panelized_chainlinkBuddyTDisplay_merged.png electronics/build/outputs/chainlinkBuddyTDisplay-panelized-pcb-raster.png
./scripts/annotate_image.sh electronics/build/outputs/chainlinkBuddyTDisplay-panelized-pcb-raster.png
env:
PYTHONUNBUFFERED: 1
- name: Export panelized PCB fabrication files
run: |
./electronics/scripts/export_jlcpcb.py electronics/build/panelized_chainlinkBuddyTDisplay.kicad_pcb
cp -r electronics/build/panelized_chainlinkBuddyTDisplay-jlc electronics/build/outputs/chainlinkBuddyTDisplay-panelized-jlc
- name: Export panelized PCB overview PDF
run: |
./electronics/scripts/generate_pdf.py electronics/build/panelized_chainlinkBuddyTDisplay.kicad_pcb
cp electronics/build/panelized_chainlinkBuddyTDisplay-pcb-packet.pdf electronics/build/outputs/chainlinkBuddyTDisplay-panelized-pcb-packet.pdf
env:
PYTHONUNBUFFERED: 1
# Note: These kibot scripts are run last because they mess with file permissions which breaks other scripts, not sure why
- name: Export BOM [chainlinkBuddyTDisplay]
uses: INTI-CMNB/KiBot@c723b51327c2d9b7863672698ae5fd3da58b386e
with:
config: electronics/chainlinkBuddyTDisplay/chainlinkBuddyTDisplay.kibot.yml
dir: electronics/build
schema: 'electronics/chainlinkBuddyTDisplay/chainlinkBuddyTDisplay.sch'
board: 'electronics/chainlinkBuddyTDisplay/chainlinkBuddyTDisplay.kicad_pcb'
- name: Copy BOM outputs
run: cp -r electronics/build/bom electronics/build/outputs
- name: Archive artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: electronics-chainlink-buddy-t-display
path: |
electronics/build
- name: Configure AWS Credentials
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Sync artifacts to S3
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
run: |
aws s3 sync electronics/build/outputs s3://splitflap-artifacts/${GITHUB_REF#refs/heads/}/electronics-chainlink-buddy-t-display --delete --acl public-read --cache-control max-age=0,no-cache
export-electronics-chainlink-buddy-breadboard:
name: Export Electronics (Chainlink Buddy Breadboard)
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up outputs directory
run: mkdir -p electronics/build/outputs
- name: Install dependencies
run: ./electronics/scripts/dependencies.sh
- name: Export schematic [chainlinkBuddyBreadboard]
run: |
./electronics/scripts/export_schematic.py electronics/chainlinkBuddyBreadboard/chainlinkBuddyBreadboard.sch
cp electronics/build/chainlinkBuddyBreadboard.pdf electronics/build/outputs/chainlinkBuddyBreadboard-schematic.pdf
cp electronics/build/chainlinkBuddyBreadboard.png electronics/build/outputs/chainlinkBuddyBreadboard-schematic.png
env:
PYTHONUNBUFFERED: 1
- name: Render PCB 3D [chainlinkBuddyBreadboard]
run: |
./electronics/scripts/export_3d.py electronics/chainlinkBuddyBreadboard/chainlinkBuddyBreadboard.kicad_pcb --width 1600 --height 1100 transform z+ z+ z+ z+ mu rx+ rx+ rx+ rx+ ry+ ry+ ry+ rz+ rz+
cp electronics/build/chainlinkBuddyBreadboard-3d.png electronics/build/outputs/chainlinkBuddyBreadboard-3d.png
./scripts/annotate_image.sh electronics/build/outputs/chainlinkBuddyBreadboard-3d.png
env:
PYTHONUNBUFFERED: 1
- name: Export JLCPCB fabrication files [chainlinkBuddyBreadboard]
run: |
./electronics/scripts/export_jlcpcb.py electronics/chainlinkBuddyBreadboard/chainlinkBuddyBreadboard.kicad_pcb
cp -r electronics/build/chainlinkBuddyBreadboard-jlc electronics/build/outputs
- name: Export PCB overview PDF [chainlinkBuddyBreadboard]
run: |
./electronics/scripts/generate_pdf.py electronics/chainlinkBuddyBreadboard/chainlinkBuddyBreadboard.kicad_pcb
cp electronics/build/chainlinkBuddyBreadboard-pcb-packet.pdf electronics/build/outputs/chainlinkBuddyBreadboard-pcb-packet.pdf
env:
PYTHONUNBUFFERED: 1
- name: Export PCB SVG [chainlinkBuddyBreadboard]
run: |
./electronics/scripts/generate_svg.py electronics/chainlinkBuddyBreadboard/chainlinkBuddyBreadboard.kicad_pcb
cp electronics/build/chainlinkBuddyBreadboard_merged.png electronics/build/outputs/chainlinkBuddyBreadboard-pcb-raster.png
./scripts/annotate_image.sh electronics/build/outputs/chainlinkBuddyBreadboard-pcb-raster.png
env:
PYTHONUNBUFFERED: 1
- name: Panelize
run: |
kikit panelize -p electronics/chainlinkBuddyBreadboard/kikit_panelize.json electronics/chainlinkBuddyBreadboard/chainlinkBuddyBreadboard.kicad_pcb electronics/build/panelized_chainlinkBuddyBreadboard.kicad_pcb
- name: Export panelized PCB SVG
run: |
./electronics/scripts/generate_svg.py electronics/build/panelized_chainlinkBuddyBreadboard.kicad_pcb
cp electronics/build/panelized_chainlinkBuddyBreadboard_merged.png electronics/build/outputs/chainlinkBuddyBreadboard-panelized-pcb-raster.png
./scripts/annotate_image.sh electronics/build/outputs/chainlinkBuddyBreadboard-panelized-pcb-raster.png
env:
PYTHONUNBUFFERED: 1
- name: Export panelized PCB fabrication files
run: |
./electronics/scripts/export_jlcpcb.py electronics/build/panelized_chainlinkBuddyBreadboard.kicad_pcb
cp -r electronics/build/panelized_chainlinkBuddyBreadboard-jlc electronics/build/outputs/chainlinkBuddyBreadboard-panelized-jlc
- name: Export panelized PCB overview PDF
run: |
./electronics/scripts/generate_pdf.py electronics/build/panelized_chainlinkBuddyBreadboard.kicad_pcb
cp electronics/build/panelized_chainlinkBuddyBreadboard-pcb-packet.pdf electronics/build/outputs/chainlinkBuddyBreadboard-panelized-pcb-packet.pdf
env:
PYTHONUNBUFFERED: 1
# Note: These kibot scripts are run last because they mess with file permissions which breaks other scripts, not sure why
- name: Export BOM [chainlinkBuddyBreadboard]
uses: INTI-CMNB/KiBot@c723b51327c2d9b7863672698ae5fd3da58b386e
with:
config: electronics/chainlinkBuddyBreadboard/chainlinkBuddyBreadboard.kibot.yml
dir: electronics/build
schema: 'electronics/chainlinkBuddyBreadboard/chainlinkBuddyBreadboard.sch'
board: 'electronics/chainlinkBuddyBreadboard/chainlinkBuddyBreadboard.kicad_pcb'
- name: Copy BOM outputs
run: cp -r electronics/build/bom electronics/build/outputs
- name: Archive artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: electronics-chainlink-buddy-breadboard
path: |
electronics/build
- name: Configure AWS Credentials
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Sync artifacts to S3
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
run: |
aws s3 sync electronics/build/outputs s3://splitflap-artifacts/${GITHUB_REF#refs/heads/}/electronics-chainlink-buddy-breadboard --delete --acl public-read --cache-control max-age=0,no-cache
================================================
FILE: .github/workflows/electronics_v2.yml
================================================
name: Export Electronics (v2)
on:
push:
pull_request:
jobs:
export-electronics-sensor-smd:
name: Export Electronics (Sensor SMD)
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up outputs directory
run: mkdir -p electronics/build/outputs
- name: Install dependencies
run: ./electronics/scripts/dependencies_v2.sh
# Sensor board:
- name: Export PCB fabrication files [sensor_smd, single]
run: |
./electronics/scripts/export_jlcpcb.py electronics/sensor_smd/sensor_smd.kicad_pcb --assembly-schematic electronics/sensor_smd/sensor_smd.kicad_sch
cp -r electronics/build/sensor_smd-jlc electronics/build/outputs/sensor_smd-jlc
- name: Panelize sensor PCB
run: |
kikit panelize -p electronics/sensor_smd/kikit_panelize.json electronics/sensor_smd/sensor_smd.kicad_pcb electronics/build/panelized_sensor_smd.kicad_pcb
- name: Export PCB fabrication files [sensor_smd, panelized]
run: |
./electronics/scripts/export_jlcpcb.py electronics/build/panelized_sensor_smd.kicad_pcb --assembly-schematic electronics/sensor_smd/sensor_smd.kicad_sch
cp -r electronics/build/panelized_sensor_smd-jlc electronics/build/outputs/sensor_smd-panelized-jlc
# Note: These kibot scripts are run last because they mess with file permissions which breaks other scripts, not sure why
- name: Exports [sensor_smd, single]
uses: INTI-CMNB/KiBot@v2_k6_1_6_3
with:
config: electronics/sensor_smd/sensor_smd.kibot.yml
dir: electronics/build/outputs
schema: 'electronics/sensor_smd/sensor_smd.kicad_sch'
board: 'electronics/sensor_smd/sensor_smd.kicad_pcb'
- name: Exports [sensor_smd, panelized]
uses: INTI-CMNB/KiBot@v2_k6_1_6_3
with:
config: electronics/sensor_smd/sensor_smd-panelized.kibot.yml
dir: electronics/build/outputs
schema: 'electronics/sensor_smd/sensor_smd.kicad_sch'
board: 'electronics/build/panelized_sensor_smd.kicad_pcb'
- name: Fix permissions
# Need to chown files back to runner, as ownership gets set to root by the KiBot Actions
run: |
sudo chown runner:docker electronics/build/outputs/*
sudo chown runner:docker electronics/build/*
- name: Update 3D artifacts [sensor_smd, single]
run: |
mv electronics/build/outputs/sensor_smd-3D_top.png electronics/build/outputs/sensor_smd-front-3d.png
./scripts/annotate_image.sh electronics/build/outputs/sensor_smd-front-3d.png
mv electronics/build/outputs/sensor_smd-3D_bottom.png electronics/build/outputs/sensor_smd-back-3d.png
./scripts/annotate_image.sh electronics/build/outputs/sensor_smd-back-3d.png
env:
PYTHONUNBUFFERED: 1
- name: Update 3D artifacts [sensor_smd, panelized]
run: |
mv electronics/build/outputs/panelized_sensor_smd-3D_top.png electronics/build/outputs/panelized_sensor_smd-front-3d.png
./scripts/annotate_image.sh electronics/build/outputs/panelized_sensor_smd-front-3d.png
mv electronics/build/outputs/panelized_sensor_smd-3D_bottom.png electronics/build/outputs/panelized_sensor_smd-back-3d.png
./scripts/annotate_image.sh electronics/build/outputs/panelized_sensor_smd-back-3d.png
env:
PYTHONUNBUFFERED: 1
- name: Rasterise schematic [sensor_smd]
run: |
convert -density 96 electronics/build/outputs/sensor_smd-schematic.pdf -background white -alpha remove electronics/build/outputs/sensor_smd-schematic.png
./scripts/annotate_image.sh electronics/build/outputs/sensor_smd-schematic.png
- name: Show artifacts
if: always()
run: |
ls -lah electronics/build
ls -lah electronics/build/outputs
- name: Archive artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: electronics-v2
path: |
electronics/build
- name: Configure AWS Credentials
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Sync artifacts to S3
if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
run: |
aws s3 sync electronics/build/outputs s3://splitflap-artifacts/${GITHUB_REF#refs/heads/}/electronics-v2 --delete --acl public-read --cache-control max-age=0,no-cache
================================================
FILE: .github/workflows/js.yml
================================================
name: JS
on:
push:
pull_request:
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
working-directory: software/chainlink/js
run: npm ci
- name: Build
working-directory: software/chainlink/js
run: PUBLIC_URL="/splitflap" npm run build
- name: Setup Pages
uses: actions/configure-pages@v3
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: './software/chainlink/js/packages/example-webserial-basic/build'
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
if: github.repository == 'scottbez1/splitflap' && github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
================================================
FILE: .github/workflows/pio.yml
================================================
name: PlatformIO CI
on:
push:
pull_request:
jobs:
pio-build:
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Cache PlatformIO
uses: actions/cache@v2
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python
uses: actions/setup-python@v2
- name: Install PlatformIO
id: pio_install
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
# - name: Build Firmware (uno-shift-register)
# # Run regardless of other build step failures, as long as setup steps completed
# if: always() && steps.pio_install.outcome == 'success'
# run: |
# pio run \
# -e uno-shift-register \
# - name: Build Firmware (uno-direct)
# # Run regardless of other build step failures, as long as setup steps completed
# if: always() && steps.pio_install.outcome == 'success'
# run: |
# pio run \
# -e uno-direct \
- name: Build Firmware (esp32CustomAdvanced)
# Run regardless of other build step failures, as long as setup steps completed
if: always() && steps.pio_install.outcome == 'success'
run: |
pio run \
-e esp32CustomAdvanced
- name: Build Firmware (chainlink)
# Run regardless of other build step failures, as long as setup steps completed
if: always() && steps.pio_install.outcome == 'success'
run: |
pio run \
-e chainlink
- name: Build Firmware (chainlinkBase)
# Run regardless of other build step failures, as long as setup steps completed
if: always() && steps.pio_install.outcome == 'success'
run: |
pio run \
-e chainlinkBase
# Temporarily disabled due to proto issue
# - name: Build Firmware (chainlinkDriverTester)
# # Run regardless of other build step failures, as long as setup steps completed
# if: always() && steps.pio_install.outcome == 'success'
# run: |
# cp firmware/esp32/tester/secrets.h.example firmware/esp32/tester/secrets.h &&
# pio run \
# -e chainlinkDriverTester
================================================
FILE: .gitignore
================================================
*.swp
*.pyc
build/
# KiCAD files
*.000
*.bak
*.bck
*.kicad_pcb-bak
*.sch-bak
*.net
*.dsn
fp-info-cache
*-backups
\#auto_saved_files\#
# KiCAD bom
splitflap.xml
sensor.xml
# Arduino compiled output
*.hex
.pio
.vscode
.nvmrc
.python_version
================================================
FILE: .gitmodules
================================================
[submodule "thirdparty/nanopb"]
path = thirdparty/nanopb
url = git@github.com:nanopb/nanopb.git
================================================
FILE: 3d/28byj-48.scad
================================================
/*
Copyright 2015-2018 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
28byj48_chassis_radius = 28/2;
28byj48_chassis_height = 19;
28byj48_shaft_offset = 8;
28byj48_mount_outer_radius = 3.5;
28byj48_mount_hole_radius = 4.2/2;
28byj48_mount_center_offset = 35/2;
28byj48_mount_bracket_height = 0.8;
28byj48_shaft_collar_radius = 9.4/2;
28byj48_shaft_collar_height = 1.5;
28byj48_shaft_radius = 5/2;
28byj48_shaft_height = 10;
28byj48_shaft_slotted_width = 3;
28byj48_shaft_slotted_height = 6;
28byj48_backpack_width = 14.6;
28byj48_backpack_extent = 18; // seen values from 17-18
28byj48_backpack_height = 16;
// Exports for use in other files:
function 28byj48_mount_bracket_height() = 28byj48_mount_bracket_height;
function 28byj48_shaft_radius() = 28byj48_shaft_radius;
function 28byj48_shaft_collar_radius() = 28byj48_shaft_collar_radius;
function 28byj48_mount_center_offset() = 28byj48_mount_center_offset;
function 28byj48_chassis_radius() = 28byj48_chassis_radius;
function 28byj48_shaft_offset() = 28byj48_shaft_offset;
function 28byj48_chassis_height() = 28byj48_chassis_height;
function 28byj48_backpack_extent() = 28byj48_backpack_extent;
// Basic 28BYJ-48 model based on various dimensioned drawings.
// Origin is centered on the shaft, on the front face of the motor, with the shaft in the +z direction
module Stepper28BYJ48() {
$fn = 60;
eps = 0.01;
gray = [0.7, 0.7, 0.7];
gold = [1, 0.843, 0];
blue = [0, 0, 1];
module mounting_holes() {
linear_extrude(height=28byj48_mount_bracket_height) {
difference() {
hull() {
translate([28byj48_mount_center_offset, 0]) {
circle(r=28byj48_mount_outer_radius);
}
translate([-28byj48_mount_center_offset, 0]) {
circle(r=28byj48_mount_outer_radius);
}
}
translate([28byj48_mount_center_offset, 0]) {
circle(r=28byj48_mount_hole_radius);
}
translate([-28byj48_mount_center_offset, 0]) {
circle(r=28byj48_mount_hole_radius);
}
}
}
}
module shaft_collar() {
cylinder(r=28byj48_shaft_collar_radius, h=28byj48_shaft_collar_height + eps);
}
module shaft() {
color(gold) {
difference() {
cylinder(r=28byj48_shaft_radius, h=28byj48_shaft_height);
translate([-(28byj48_shaft_radius + eps), 28byj48_shaft_slotted_width / 2, 28byj48_shaft_height - 28byj48_shaft_slotted_height]) {
cube([(28byj48_shaft_radius+eps)*2, 28byj48_shaft_radius, 28byj48_shaft_slotted_height+eps]);
}
translate([-(28byj48_shaft_radius + eps), - 28byj48_shaft_radius - (28byj48_shaft_slotted_width / 2), 28byj48_shaft_height - 28byj48_shaft_slotted_height]) {
cube([(28byj48_shaft_radius+eps)*2, 28byj48_shaft_radius, 28byj48_shaft_slotted_height+eps]);
}
}
}
}
module chassis() {
color(gray) {
translate([0, 0, -28byj48_chassis_height]) {
cylinder(r=28byj48_chassis_radius, h=28byj48_chassis_height);
}
translate([0, 0, -28byj48_mount_bracket_height - eps]) {
mounting_holes();
}
translate([0, 28byj48_shaft_offset, -eps]) {
shaft_collar();
}
}
}
module backpack() {
color(blue) {
translate([-28byj48_backpack_width/2, 0, -28byj48_backpack_height - eps]) {
cube([28byj48_backpack_width, 28byj48_backpack_extent, 28byj48_backpack_height]);
}
}
}
translate([0, -28byj48_shaft_offset, 0]) {
chassis();
translate([0, 28byj48_shaft_offset, 0]) {
shaft();
}
translate([0, -28byj48_backpack_extent, 0]) {
backpack();
}
}
}
// Example usage:
Stepper28BYJ48();
================================================
FILE: 3d/assert.scad
================================================
/*
Copyright 2015-2020 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
module legacyAssert(truth, msg="(no messsage provided)") {
if (!truth) {
// Intentionally don't close the <div> tag to make the entire console red
echo(str(
"<div style='background-color:#FF0000'>",
"<br><br>",
"####################",
"<br><br>",
"<span style='font-size: x-large'>ASSERTION ERROR!</span>",
"<br><br>",
msg,
"<br><br>",
"####################",
"<br><br>"));
color("red") {
cube([1000, 1000, 1000], center=true);
}
}
}
required_widgets = 24;
widgets = 12 + 3;
legacyAssert(widgets >= required_widgets, str("Not enough widgets! Required ", required_widgets, " but found ", widgets));
================================================
FILE: 3d/color_util.scad
================================================
/*
Copyright 2015-2021 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// multiply two equal matricies by each element, limiting to a max of 1.0
function color_multiply(x, y) =
[ for(j=[0:len(x) - 1]) min(x[j] * y[j], 1.0) ];
// inverts a color matrix by subtracting the input channel values from 1.0
function color_invert(x) =
[ for(j=[0:len(x) - 1]) (1.0 - x[j]) ];
================================================
FILE: 3d/combined_front_panel.scad
================================================
/*
Copyright 2021 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
include<flap_dimensions.scad>;
use<color_util.scad>;
use<flap.scad>;
use<projection_renderer.scad>;
use<splitflap.scad>;
// -----------------------
// Configurable parameters
// -----------------------
rows = 4;
cols = 13;
// Set either frame_width/height, or frame_margin_x/y to set the overall frame dimensions. margin_x/y will calculate the necessary frame width
// based on the number of modules and spacing and add the margin onto both sides.
frame_width = 48*25.4;
frame_height = 30*25.4;
frame_margin_x = undef;
frame_margin_y = undef;
// Set either the center_center_* or gap_* values to distribute modules based on center-to-center distance or a gap between modules.
// You can set gap_x and gap_y to 0 to get the closest possible spacing of modules.
center_center_x = undef;
center_center_y = undef;
gap_x = 0;
gap_y = 0;
// Vertical centering mode
// 0 = center based on letter/flap center
// 1 = center based on window cutout center
// 2 = center based on module overall center
center_mode = 0;
// Use tool_diameter for rotary/milling cutters, or kerf_width for laser.
// kerf_width applies a naive offset that is only valid for very small kerf from a laser cutter (e.g. 0.2mm)
// tool_diameter will modify a few parts of the design for milling bits, but will not handle all sizes well; use caution and review the design carefully.
tool_diameter = 0; // e.g. set to "1/8*25.4 * 1.1" for a 1/8 inch bit, plus 10%
kerf_width = 0;
// Preview-only paramters:
display_text = [
" ABCDEFGHIJKL",
"MNOPQRSTUVWXY",
"Zg0123456789r",
".?-$'#yp,!@&w",
];
// Number of full modules to render in 3d preview
render_full_modules_count = 4;
// ---------------------------
// End configurable parameters
// ---------------------------
render_index = -1;
render_etch = false;
assert(is_undef(center_center_x) || center_center_x >= get_enclosure_width(), "Horizontal center-to-center value must be at least the enclosure width");
assert(is_undef(center_center_y) || center_center_y >= get_enclosure_height(), "Vertical center-to-center value must be at least the enclosure height");
assert(is_undef(gap_x) || gap_x >= 0);
assert(is_undef(gap_y) || gap_y >= 0);
layout_center_center_x = is_undef(gap_x) ? center_center_x : get_enclosure_width() + gap_x;
layout_center_center_y = is_undef(gap_y) ? center_center_y : get_enclosure_height() + gap_y;
echo(debug_gap_x = layout_center_center_x - get_enclosure_width());
echo(debug_gap_y = layout_center_center_y - get_enclosure_height());
layout_frame_width = is_undef(frame_margin_x) ? frame_width : layout_center_center_x * (cols - 1) + get_enclosure_width() + frame_margin_x*2;
layout_frame_height = is_undef(frame_margin_y) ? frame_height : layout_center_center_y * (rows - 1) + get_enclosure_height() + frame_margin_y*2;
y_offset = center_mode == 0 ? 0 :
center_mode == 1 ? get_front_window_lower() - (get_front_window_upper() + get_front_window_lower())/2 :
center_mode == 2 ? get_enclosure_height_lower() - get_enclosure_height()/2:
0;
echo(debug_frame_margin_top = (layout_frame_height - layout_center_center_y * (rows - 1))/2 - get_enclosure_height_upper() - y_offset);
echo(debug_frame_margin_bottom = (layout_frame_height - layout_center_center_y * (rows - 1))/2 - get_enclosure_height_lower() + y_offset);
module centered_front() {
translate([-get_front_window_right_inset() - get_front_window_width()/2, -get_enclosure_height_lower() + y_offset]) {
children();
}
}
flap_color = get_flap_color();
letter_color = get_letter_color();
assembly_colors = get_assembly_colors();
frame_color = assembly_colors[0];
projection_renderer(render_index = render_index, render_etch = render_etch, kerf_width = kerf_width, panel_height = 0, panel_horizontal = 0, panel_vertical = 0) {
color(frame_color) {
linear_extrude(height=get_thickness()) {
difference() {
translate([(cols-1)/2 * layout_center_center_x, -(rows-1)/2 * layout_center_center_y]) {
square([layout_frame_width, layout_frame_height], center=true);
}
for (i=[0:cols-1]) {
for (j=[0:rows-1]) {
translate([i * layout_center_center_x, -j * layout_center_center_y]) {
centered_front() {
enclosure_front_cutouts_2d(tool_diameter=tool_diameter);
}
}
}
}
}
}
}
}
if (render_index == -1 && !is_projection_rendering()) {
for (i=[0:cols-1]) {
for (j=[0:rows-1]) {
index = i + j*cols;
translate([i * layout_center_center_x, -j * layout_center_center_y]) {
if (index < render_full_modules_count) {
translate([get_front_window_right_inset() + get_front_window_width()/2, y_offset, -get_front_forward_offset()]) {
rotate([90, 0, 180]) {
split_flap_3d(get_flap_index_for_letter(display_text[j][i]), false, false);
}
}
} else {
// Render just a flap
translate([-flap_width/2, 0]) {
flap_with_letters(flap_color, letter_color, get_flap_index_for_letter(display_text[j][i]), get_flap_gap());
}
translate([-flap_width/2, -(get_flap_gap() + get_flap_pin_width())]) {
rotate([-180, 0, 0]) {
flap_with_letters(flap_color, letter_color, get_flap_index_for_letter(display_text[j][i])-1, get_flap_gap());
}
}
}
}
}
}
}
================================================
FILE: 3d/flap.scad
================================================
/*
Copyright 2015-2021 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
include<flap_dimensions.scad>;
include<global_constants.scad>;
use<flap_fonts.scad>;
use<flap_characters.scad>;
// TODO: extract core flap spool dimensions used for vertical_keepout_size instead of using the full splitflap file
use<splitflap.scad>;
character_list = get_character_list();
color_list = [
["p", [122, 40, 203] / 255], // #7a28cb
["r", [230, 57, 70] / 255], // "#e63946
["y", [255, 214, 57] / 255], // "#ffd639
["g", [102, 215, 209] / 255], // "#66d7d1
["w", [255, 255, 255] / 255], // "#ffffff
];
// Facet number variable refers to a special variable "$fn" that is used to control the number of facets to generate an arc.
// Higher than 100 is not recommended according to the docs as it demands a lot resources
letter_facet_number = 100;
// Vertical keepout refers to the portion of bottom flaps that are visible stacked behind the frontmost flap.
vertical_keepout_mode = 1; // 0=ignore; 1=highlight; 2=cut
vertical_keepout_size_factor = 1.1; // Expand calculated keepout region by this factor. 1=no expansion, 1.5=50% expansion, etc
vertical_keepout_size = get_flap_arc_separation() * vertical_keepout_size_factor;
font_extrusion_3d = 0.3;
font_extrusion = 0.1;
function get_flap_index_for_letter(letter) = search(letter, character_list)[0];
function get_letter_for_front(flap_index) =
flap_index >= len(character_list) ? get_letter_for_front(flap_index - len(character_list)) :
flap_index < 0 ? get_letter_for_front(flap_index + len(character_list)) :
character_list[flap_index];
// The letter (bottom half) on the back of a flap is the same as the letter (top half) on the front of the next flap
function get_letter_for_back(flap_index) = get_letter_for_front(flap_index + 1);
module flap_2d(cut_tabs = true) {
translate([0, -flap_pin_width/2, 0])
difference() {
union() {
square([flap_width, flap_height - flap_corner_radius]);
// rounded corners
hull() {
translate([flap_corner_radius, flap_height - flap_corner_radius])
circle(r=flap_corner_radius, $fn=40);
translate([flap_width - flap_corner_radius, flap_height - flap_corner_radius])
circle(r=flap_corner_radius, $fn=40);
}
}
// spool tabs
if(cut_tabs) {
translate([-eps, flap_pin_width])
square([eps + flap_notch_depth, flap_notch_height]);
translate([flap_width - flap_notch_depth, flap_pin_width])
square([eps + flap_notch_depth, flap_notch_height]);
}
}
}
module _flap(flap_color) {
color(flap_color) {
translate([0, 0, -flap_thickness/2]) {
linear_extrude(height=flap_thickness) {
flap_2d();
}
}
}
}
module _apply_special_color(letter, standard_letter_color) {
color_index = search([letter], color_list);
if (len(color_index) > 0 && is_num(color_index[0])) {
color(color_list[color_index[0]][1]) {
children();
}
} else {
color(standard_letter_color) {
children();
}
}
}
module _draw_letter(letter, flap_gap) {
overrides = get_letter_overrides(letter);
height = is_undef(overrides[3]) ? get_font_setting("height") : overrides[3];
width = is_undef(overrides[4]) ? get_font_setting("width") : overrides[4];
offset_x = is_undef(overrides[1]) ? get_font_setting("offset_x") : get_font_setting("offset_x") + overrides[1];
offset_y = is_undef(overrides[2]) ? get_font_setting("offset_y") : get_font_setting("offset_y") + overrides[2];
thickness_offset = is_undef(overrides[5]) ? (is_undef(get_font_setting("thickness_offset")) ? 0 : get_font_setting("thickness_offset")) : overrides[5];
color_index = search([letter], color_list);
if (len(color_index) > 0 && is_num(color_index[0])) {
color_height = get_font_setting("color_height");
color_height_with_default = is_undef(color_height) ? height : color_height;
color_offset_y = get_font_setting("color_offset_y");
color_offset_y_with_default = is_undef(color_offset_y) ? offset_y : color_offset_y;
translate([0, color_offset_y_with_default]) {
square([flap_width - flap_notch_depth * 2 - 4, (flap_height * 2 + flap_gap) * color_height_with_default], center=true);
}
} else {
translate([0, -flap_height * height, 0]) { // valign compensation
scale([width, 1, 1]) {
translate([offset_x, offset_y]) {
offset(delta=thickness_offset) {
text(text=letter, size=flap_height * height * 2, font=get_font_setting("font"), halign="center", $fn=letter_facet_number);
}
}
}
}
}
}
module _flap_letter(letter, letter_color, flap_gap, front = true, bleed = 0, print_3d = false) {
_apply_special_color(letter, letter_color) {
translate([0, 0, front ? (flap_thickness/2 + eps - (print_3d ? font_extrusion_3d : 0)) : (-flap_thickness/2 - eps)]) {
linear_extrude(height=print_3d ? font_extrusion_3d : font_extrusion, center=print_3d ? false : true) {
intersection() {
difference() {
offset(r=bleed) {
flap_2d();
}
// Note that the verticle keepout (if applicable) is subtracted *after* the bleed is applied; otherwise
// text would bleed into the keepout zone.
if (vertical_keepout_mode > 0) {
vertical_keepout_width = flap_width + bleed*2; // keepout must expand by "bleed" on the left and right
translate([-bleed, flap_height - flap_pin_width/2 - vertical_keepout_size, 0]) {
square([vertical_keepout_width, vertical_keepout_size]);
}
}
// TODO: consider adding horizontal keepout; it may be undesirable for text to meet the sides of the flaps
// (or possibly extend beyond the sides if bleed > 0).
}
translate([flap_width/2, -flap_pin_width/2, 0]) {
rotation = front ? 0 : -180; // flip upside-down for back
gap_comp = (use_letter_gap_compensation() == true) ? -flap_gap/2 : 0;
translate([0, gap_comp, 0]) {
rotate([rotation, 0, 0]) {
_draw_letter(letter, flap_gap);
}
}
}
}
}
}
}
// Highlight any portion of the letter within the keepout region in red, if desired.
if (vertical_keepout_mode == 1) {
color([1,0,0]) {
translate([0, 0, front ? flap_thickness/2 + eps : -flap_thickness/2 - eps]) {
linear_extrude(height=1, center=true) {
intersection() {
offset(r=bleed) {
flap_2d();
}
translate([0, flap_height - flap_pin_width/2 - vertical_keepout_size, 0]) {
square([flap_width, vertical_keepout_size]);
}
translate([flap_width/2, -flap_pin_width/2, 0]) {
rotation = front ? 0 : -180; // flip upside-down for back
gap_comp = (use_letter_gap_compensation() == true) ? -flap_gap/2 : 0;
translate([0, gap_comp, 0]) {
rotate([rotation, 0, 0]) {
_draw_letter(letter, flap_gap);
}
}
}
}
}
}
}
}
}
module flap_with_letters(flap_color, letter_color, flap_index, flap_gap, flap=true, front_letter=true, back_letter=true, bleed=0, print_3d=false) {
if (len(character_list) != get_num_flaps()) {
echo("Warning: character_list and num_flaps mismatch!");
}
if (flap) {
_flap(flap_color);
}
if (front_letter) {
_flap_letter(get_letter_for_front(flap_index), letter_color, flap_gap, front=true, bleed=bleed, print_3d=print_3d);
}
if (back_letter) {
_flap_letter(get_letter_for_back(flap_index), letter_color, flap_gap, front=false, bleed=bleed, print_3d=print_3d);
}
}
// Example:
i = 1;
gap = 5;
flap_with_letters([1,0,0], [1,1,0], flap_index=i, flap_gap=gap, bleed=2);
translate([0, -flap_pin_width-gap, 0])
rotate([180, 0, 0]) {
flap_with_letters([1,0,0], [1,1,0], flap_index=i - 1, flap_gap=gap, bleed=2);
}
================================================
FILE: 3d/flap_3dp.scad
================================================
/*
Copyright 2025 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
use<flap.scad>;
// TODO: extract core flap spool dimensions used for flap gap instead of using the full splitflap file
use<splitflap.scad>;
generateFullFlap = false; // If true, generates the full flap STL
generateText = false; // If generateFullFlap is false, this controls what part of the STL to generate
generateFrontText = true; // If generateText is true, this controls generation of the front text
generateBackText = true; // If generateText is true, this controls generation of the back text
flap_number = 0;
flap_gap = get_flap_gap();
if (generateFullFlap) {
// Generate the full flap using the standard function
flap_with_letters([0,0,0], [1,1,1], flap_index=flap_number, flap_gap=flap_gap, bleed=0, print_3d=true);
} else {
if (generateText == false) {
// Generate just the flap STL with the letters removed
difference() {
flap_with_letters([0,0,0], [1,1,1], front_letter=false, back_letter=false, flap_index=flap_number, flap_gap=flap_gap, bleed=0, print_3d=true);
flap_with_letters([0,0,0], [1,1,1], flap=false, flap_index=flap_number, flap_gap=flap_gap, bleed=0, print_3d=true);
}
} else {
// Generate just the letters STL
flap_with_letters([0,0,0], [1,1,1], front_letter=generateFrontText, back_letter=generateBackText, flap=false, flap_index=flap_number, flap_gap=flap_gap, bleed=0, print_3d=true);
}
}
================================================
FILE: 3d/flap_characters.scad
================================================
character_list = " ABCDEFGHIJKLMNOPQRSTUVWXYZg0123456789r.?-$'#yp,!@&w";
function get_character_list() = character_list;
================================================
FILE: 3d/flap_dimensions.scad
================================================
/*
Copyright 2018 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
flap_width = 54;
flap_height = 43;
flap_thickness = 30 / 1000 * 25.4; // 30 mil
flap_corner_radius = 3.1; // 2.88-3.48mm (used just for display)
flap_notch_height_auto = false;
flap_notch_height_default = 15;
flap_notch_height = (flap_notch_height_auto == true) ? sqrt(spool_outer_radius*spool_outer_radius - flap_pitch_radius*flap_pitch_radius) : flap_notch_height_default;
flap_notch_depth = 3.2;
flap_pin_width = 1.4;
================================================
FILE: 3d/flap_fonts.scad
================================================
/*
Copyright 2020-2021 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
include<flap_dimensions.scad>;
use<fonts/roboto/RobotoCondensed-Regular.ttf>;
use<fonts/Epilogue/Epilogue-VariableFont_wght.ttf>;
// To try other experimental fonts, download fonts from Google Fonts to these locations and add `use` statements
// fonts/PoiretOne/PoiretOne-Regular.ttf
// fonts/Voltaire/Voltaire-Regular.otf
// fonts/PTSansNarrow/PTSansNarrow-Regular.ttf
// -----------------------
// Configurable parameters
// -----------------------
font_preset = "Epilogue"; // See available presets below
letter_gap_comp = true; // Shifts letter positions to compensate for gap between flaps
// ---------------------------
// End configurable parameters
// ---------------------------
// Configure font presets below. Each font has the following settings:
// 'font'
// Font name - see https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text#Using_Fonts_&_Styles
// 'height'
// Font size (height) relative to flaps. i.e. a value of 1 sets the font size equal to the height of the top and bottom flaps.
// 'width'
// Width scale factor. 1 = use default font width.
// 'offset_x'
// Horizontal offset, in mm, of letters within flaps. A value of 0 uses default font centering.
// 'offset_y'
// Vertical offset, in mm, of letters within flaps. A value of 0 uses default font centering.
// 'overrides'
// Array of position/size overrides for specific letters. Each entry is a set of overrides for a single letter,
// specified as an array with the following entries:
// - Letter to override (e.g. "M"). Case must match for the override to apply.
// - Additional X position offset, in mm (e.g. -5). Can be undef or 0 to omit.
// - Additional Y position offset, in mm (e.g. 2.5). Can be undef or 0 to omit.
// - Height override, as a value relative to flap height (e.g. 0.7). Replaces letter_height for this letter. Can be undef to omit.
// - Width override, as a value relative to default font width (e.g. 0.7). Replaces letter_width for this letter. Can be undef to omit.
// - Thickness offset override.
_font_settings = [
"Roboto", [
"font", "RobotoCondensed",
"height", 0.6,
"width", 1,
"offset_x", -0.78,
"offset_y", 0.7,
"overrides", [
["@", 0, 1],
],
],
// https://fonts.google.com/specimen/Bangers
"Bangers", [
"font", "Bangers",
"height", 0.85,
"width", 0.7,
"offset_x", -5.5,
"offset_y", -1,
"overrides", [
["M", 2.5, 0],
["Q", 0, 1, 0.82],
["W", -2, 0],
[",", 0, -4, 0.6, .8],
["'", 0, 6, 0.65, .8],
],
],
"OCR-A", [
"font", "OCRAStd",
"height", 0.7,
"width", 1,
"offset_x", -0.78,
"offset_y", 0,
"overrides", [],
],
"Epilogue", [
"font", "Epilogue:style=Medium",
"height", 0.7,
"width", 1,
"offset_x", -0.65,
"offset_y", -0.8,
"color_offset_y", 0.4,
"thickness_offset", 0,
"overrides", [
// x, y, height, width, thickness
["@", 1.2, 0, .65, 0.7, 0.4],
["&", 1.2, 0, undef, 0.9, 0],
["C", 0, 0, undef, 0.95, 0],
["G", 0.9, 0, undef, 0.98, 0],
["W", -0.15, undef, undef, 0.72, 1],
["M", -0.16, undef, undef, 0.795, 0.6],
["O", 0, 0, undef, 0.92, 0],
["Q", 0, 3, 0.62, undef, 0.4],
[",", 0, -1.6, 0.6, undef, 0],
],
],
"Poiret", [
"font", "PoiretOne",
"height", 0.65,
"width", 0.75,
"offset_x", -0.6,
"offset_y", 0.65,
"overrides", [
["W", 0, 0, 0.65, 0.7]
],
"thickness_offset", 2,
"color_height", 0.455,
"color_offset_y", 1.4,
],
"Voltaire", [
"font", "Voltaire:style=Regular",
"height", 0.75,
"width", 1,
"offset_x", -0.6,
"offset_y", -0.65,
"overrides", [],
// "color_height", 0.455,
// "color_offset_y", 1.4,
],
"PTSansNarrow", [
"font", "PTSansNarrow",
"height", 0.7,
"width", 1,
"offset_x", -0.6,
"offset_y", 2,
"overrides", [
["@", 0, 4, 0.65, 0.8],
["Q", 0, 4, 0.6],
],
// "color_height", 0.455,
// "color_offset_y", 1.4,
],
"Righteous", [
"font", "Righteous",
"height", 0.6,
"width", 1,
"offset_x", 0,
"offset_y", 0,
"overrides", [
// ["@", 0, 4, 0.65, 0.8],
// ["Q", 0, 4, 0.6],
],
// "color_height", 0.455,
// "color_offset_y", 1.4,
],
];
// Private functions
function _get_entry_in_dict_array(arr, key) = search([key], arr) != [[]] ? arr[search([key], arr)[0] + 1] : undef;
function _get_font_settings() = _get_entry_in_dict_array(_font_settings, font_preset);
// Public functions
function use_letter_gap_compensation() = letter_gap_comp;
function get_font_setting(key) = _get_entry_in_dict_array(_get_font_settings(), key);
function get_letter_overrides(letter) =
get_font_setting("overrides")[search([letter], get_font_setting("overrides"))[0]];
================================================
FILE: 3d/font_generator.scad
================================================
/*
Copyright 2020 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
include<flap_dimensions.scad>;
use<flap.scad>;
use<flap_characters.scad>;
use<projection_renderer.scad>;
use<splitflap.scad>;
// -----------------------
// Configurable parameters
// -----------------------
num_columns = 5; // Number of columns for layout; 0 for infinite
start_row = 0; // First row to render
row_count = 1000; // Number of rows to render
only_side = 0; // 0=both, 1=top only, 2=bottom only
// "Standard" layout, of all flaps front side or back side, which can be rendered to 2 separate files
// for double-sided (duplex) printing on a single substrate. Note that the flap order goes
// right-to-left on the back side, corresponding to the flipped order when printing on the back side
// of the substrate. See MODE_FRONT_BACK for a better alternative when applying lettering to flaps
// one-by-one.
MODE_DOUBLE_SIDED = 0;
// Similar to MODE_DOUBLE_SIDED, this generates separate front and back layouts, but both the front
// and back sides are ordered left-to-right. This cannot be used for duplex printing on a single sheet
// substrate, but is ideal for things like adhesive vinyl where each letter will be applied individually.
MODE_FRONT_BACK = 1;
// If you want to view the full font with each letter as it appears when at the front of the display,
// it requires rendering twice as many flaps and a special layout with the bottom flaps flipped over.
// You also probably want to set only_side to 1 when using this layout.
MODE_FULL_FONT = 2;
// Each flap's front side is laid out side-by-side with its back side.
// You probably want to set only_side to 1 when using this layout.
MODE_SIDE_BY_SIDE = 3;
layout_mode = MODE_DOUBLE_SIDED;
flip_individual_flaps = false;
flip_entire_layout = false; // Flip the entire layout of flaps over (e.g. when exporting the bottom sides)
// Gap between flaps
spacing_x = 5;
spacing_y = 5;
bleed = 0; // Amount of bleed (in mm) for text to expand beyond the flap boundary
flap_color = [1,1,1];
letter_color = [0,0,0];
print_3d = false;
render_alignment_marks = false; // Whether to render markings to help with alignment/registration (e.g. for screen printing)
// ---------------------------
// End configurable parameters
// ---------------------------
character_list = get_character_list();
cols = (num_columns == 0) ? len(character_list) : num_columns;
total_rows = floor((len(character_list)-1+cols) / cols);
visible_rows = min(row_count, total_rows - start_row);
kerf_width = 0;
render_fill = false;
flap_gap = get_flap_gap();
assert(!(layout_mode != MODE_DOUBLE_SIDED && render_alignment_marks), "Alignment marks are only supported with duplex double-sided layout mode");
if (!is_projection_rendering()) {
echo("Info: this model is intended for use via generate_fonts.py in order to export flap font files.");
}
module flap_transform(row, col) {
x_pos = layout_mode == MODE_SIDE_BY_SIDE ?
(flap_width*2 + spacing_x*3) * col :
(flap_width + spacing_x) * col;
y_pos = layout_mode == MODE_FULL_FONT ?
(flap_height*2 + flap_gap + spacing_y) * row:
(flap_height + spacing_y) * row;
translate([x_pos, -y_pos, 0]) {
children();
}
}
module flap_pos(i) {
col = i % cols;
row = floor(i / cols);
offsetted_row = row - start_row;
if (offsetted_row >= 0 && offsetted_row < row_count) {
flap_transform(offsetted_row, col) {
children();
}
}
}
module configured_flap(index, flap, front_letter, back_letter) {
flap_with_letters(flap_color, letter_color, index, flap_gap, flap=flap, front_letter=front_letter, back_letter=back_letter, bleed=bleed, print_3d=print_3d);
if (layout_mode == MODE_FULL_FONT) {
translate([0, -flap_pin_width -flap_gap, 0]) {
rotate([180,0,0]) {
flap_with_letters(flap_color, letter_color, index - 1, flap_gap, flap=flap, front_letter=back_letter, back_letter=front_letter, bleed=bleed, print_3d=print_3d);
}
}
}
if (layout_mode == MODE_SIDE_BY_SIDE) {
translate([2*flap_width + spacing_x, 0]) {
rotate([0, 180,0]) {
flap_with_letters(flap_color, letter_color, index, flap_gap, flap=flap, front_letter=back_letter, back_letter=front_letter, bleed=bleed, print_3d=print_3d);
}
}
}
}
module fill_text() {
// If 'render_fill' is 'true', only continue if 'render_etch' is also set by the script
if (!render_fill || render_fill && render_etch) {
children();
}
}
module alignment_marks() {
alignment_offset = 5;
secondary_alignment_offset = 15;
alignment_diameter = 5;
// Holes in each corner, intended for alignment pins when building a flap tray, which register the flap tray to the printing platen for repeatability
color([1,0,0]) {
linear_extrude(height=5) {
translate([0, flap_height + alignment_offset - flap_pin_width/2]) {
circle(d=alignment_diameter, $fn=30);
}
translate([0, -(flap_height + spacing_y)*(visible_rows - 1) - alignment_offset - flap_pin_width/2]) {
circle(d=alignment_diameter, $fn=30);
}
translate([cols * flap_width + (cols - 1) * spacing_x, flap_height + alignment_offset - flap_pin_width/2]) {
circle(d=alignment_diameter, $fn=30);
}
translate([cols * flap_width + (cols - 1) * spacing_x, -(flap_height + spacing_y)*(visible_rows - 1) - alignment_offset - flap_pin_width/2]) {
circle(d=alignment_diameter, $fn=30);
}
}
}
// Additional alignment holes in each corner, intended as registration marks for aligning the screen/image to the flap tray during initial setup
color([0,0,1]) {
linear_extrude(height=5) {
translate([secondary_alignment_offset, flap_height + alignment_offset - flap_pin_width/2]) {
circle(d=alignment_diameter, $fn=30);
}
translate([secondary_alignment_offset, -(flap_height + spacing_y)*(visible_rows - 1) - alignment_offset - flap_pin_width/2]) {
circle(d=alignment_diameter, $fn=30);
}
translate([cols * flap_width + (cols - 1) * spacing_x - secondary_alignment_offset, flap_height + alignment_offset - flap_pin_width/2]) {
circle(d=alignment_diameter, $fn=30);
}
translate([cols * flap_width + (cols - 1) * spacing_x - secondary_alignment_offset, -(flap_height + spacing_y)*(visible_rows - 1) - alignment_offset - flap_pin_width/2]) {
circle(d=alignment_diameter, $fn=30);
}
}
}
}
module _flip() {
translate([flip_entire_layout ? cols*flap_width + (cols-1)*spacing_x : 0, 0, 0]) {
rotate([0, flip_entire_layout ? 180 : 0, 0]) {
children();
}
}
}
module _flip_flap() {
translate([flip_individual_flaps ? flap_width : 0, 0, 0]) {
rotate([0, flip_individual_flaps ? 180 : 0, 0]) {
children();
}
}
}
render_index = -1;
render_etch = false;
projection_renderer(render_index = render_index, render_etch = render_etch, kerf_width = kerf_width, panel_height = 0, panel_horizontal = 0, panel_vertical = 0) {
_flip() {
for(i = [0 : len(character_list) - 1]) {
flap_pos(i) {
_flip_flap() {
configured_flap(i, flap=true, front_letter=false, back_letter=false);
}
}
}
}
fill_text() {
_flip() {
for(i = [0 : len(character_list) - 1]) {
show_front = only_side == 0 || only_side == 1;
show_back = only_side == 0 || only_side == 2;
flap_pos(i) {
_flip_flap() {
configured_flap(i, flap=false, front_letter=show_front, back_letter=show_back);
}
}
}
}
}
if (render_alignment_marks) {
fill_text() {
_flip() {
alignment_marks();
}
}
}
}
================================================
FILE: 3d/fonts/Epilogue/OFL.txt
================================================
Copyright 2020 The Epilogue Project Authors (https://github.com/Etcetera-Type-Co/Epilogue)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
================================================
FILE: 3d/fonts/roboto/LICENSE.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: 3d/global_constants.scad
================================================
/*
Copyright 2015-2021 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Almost nothing should be a global constant. Think hard before adding to this file.
// "Epsilon" - a small error tolerance value, used when designing 3d parts to avoid exact complanar faces that cause OpenSCAD rendering artifacts
// See https://3dprinting.stackexchange.com/a/9795
eps=0.01;
================================================
FILE: 3d/label.scad
================================================
/*
Copyright 2015-2016 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
use<fonts/roboto/Roboto-Bold.ttf>;
module roboto(text, size) {
text(text=text, size=size, font="Roboto:style=Bold");
}
module text_label(lines) {
text_height=2;
module text_lines(lines, text_height, spacing=1.5) {
for (i = [0 : len(lines)-1]) {
translate([0, text_height * spacing * (len(lines)-1-i), 0]) {
roboto(lines[i], text_height);
}
}
}
text_lines(lines, text_height);
}
================================================
FILE: 3d/m4_dimensions.scad
================================================
/*
Copyright 2015-2018 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// M4 bolts
m4_hole_diameter = 4.5;
m4_bolt_length = 10;
m4_button_head_diameter = 7.6 + .2;
m4_button_head_length = 2.2 + .2;
m4_nut_width_flats = 7 + .2;
m4_nut_width_corners = 7/cos(180/6);
m4_nut_width_corners_padded = m4_nut_width_corners + .2;
m4_nut_length = 3.2;
m4_nut_length_padded = m4_nut_length + .2;
================================================
FILE: 3d/pcb.scad
================================================
/*
Copyright 2015-2018 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
include <m4_dimensions.scad>;
include <sensor_pcb_dimensions.scad>;
module pcb_outline_2d() {
difference() {
translate([-pcb_edge_to_hole_x, -pcb_height + pcb_edge_to_hole_y]) {
square([pcb_length, pcb_height]);
}
circle(r=pcb_hole_radius, $fn=30);
translate([pcb_hole_to_bolt_hole_x, -pcb_hole_to_bolt_hole_y]) {
circle(r=pcb_bolt_hole_radius, $fn=30);
}
}
}
// 3D PCB module, origin at the center of the mounting hole on the bottom surface of the PCB
module pcb(pcb_to_spool) {
color([0, 0.5, 0]) {
linear_extrude(height=pcb_thickness) {
pcb_outline_2d();
}
}
mirror([0, 0, 1]) {
// Connector
color([0, 0, 0]) {
translate([pcb_hole_to_connector_pin_2_x - pcb_connector_width/2, -pcb_hole_to_connector_pin_2_y - pcb_connector_length, 0]) {
cube([pcb_connector_width, pcb_connector_length, pcb_connector_height]);
}
}
// Connector pins
color([0.5, 0.5, 0.5]) {
translate([pcb_hole_to_connector_pin_2_x - pcb_connector_pin_width/2, -pcb_hole_to_connector_pin_2_y - pcb_connector_pin_width/2, -pcb_connector_pin_tail_length + 2.5/2]) {
cube([pcb_connector_pin_width, pcb_connector_pin_width, pcb_connector_pin_tail_length]);
translate([-connector_pin_pitch, 0, 0]) {
cube([pcb_connector_pin_width, pcb_connector_pin_width, pcb_connector_pin_tail_length]);
}
translate([connector_pin_pitch, 0, 0]) {
cube([pcb_connector_pin_width, pcb_connector_pin_width, pcb_connector_pin_tail_length]);
}
}
}
}
// Sensor body
color([0, 0, 0]) {
translate([pcb_hole_to_sensor_x - hall_effect_width/2, pcb_hole_to_sensor_y - hall_effect_height/2, pcb_thickness]) {
cube([hall_effect_width, hall_effect_height, hall_effect_thickness]);
}
}
}
function pcb_hole_to_sensor_x() = pcb_hole_to_sensor_x;
function pcb_hole_to_sensor_y() = pcb_hole_to_sensor_y;
function pcb_thickness() = pcb_thickness;
// Example usage:
pcb(10);
================================================
FILE: 3d/projection_renderer.scad
================================================
/*
Copyright 2015-2021 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
_is_projection_rendering = false; // DO NOT MODIFY - this is set by the projection_renderer.py script
module projection_renderer(render_index = -1, render_etch = false, kerf_width = 0, panel_height, panel_horizontal, panel_vertical) {
if (is_projection_rendering()) {
echo(num_components=$children);
}
if (render_index == -1) {
children();
} else {
translate([(panel_horizontal == 1 ? -kerf_width : 0), (panel_horizontal == 1 ? panel_height - kerf_width : 0) - panel_vertical * panel_height]) {
rotate([0, 0, panel_horizontal == 1 ? 180 : 0]) {
offset_size = (render_etch ? -kerf_width : kerf_width)/2;
offset(delta=offset_size) {
projection() {
children(render_index);
}
}
}
}
}
}
function is_projection_rendering() = _is_projection_rendering;
================================================
FILE: 3d/rough7380.scad
================================================
/*
Copyright 2018 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
module roughM4_7380(length) {
eps = 0.1;
thread_diameter = 4;
head_diameter = 7.6;
head_length = 2.2;
driving_depth = 1.3;
driving_apothem = 2.5/2;
driving_radius = driving_apothem / cos(180/6);
sphere_offset = 1.2; // Just a guess
sphere_radius = sqrt(sphere_offset*sphere_offset + (head_diameter/2)*(head_diameter/2));
difference() {
union() {
translate([0, 0, -eps]) {
cylinder(h=length+eps, d=thread_diameter, $fn=30);
}
difference() {
translate([0, 0, sphere_offset]) {
sphere(sphere_radius, $fn=30);
}
translate([0, 0, sphere_radius]) {
cube([sphere_radius*2 + eps, sphere_radius*2 + eps, sphere_radius*2 + eps], center=true);
}
translate([0, 0, -sphere_radius - head_length]) {
cube([sphere_radius*2 + eps, sphere_radius*2 + eps, sphere_radius*2 + eps], center=true);
}
}
}
translate([0, 0, -head_length-eps]) {
cylinder(h=driving_depth+eps, r=driving_radius, $fn=6);
}
}
}
roughM4_7380(12);
================================================
FILE: 3d/scripts/colored_stl_exporter.py
================================================
# Copyright 2015-2016 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Exports each color of a given OpenSCAD model as a separate .stl file.
First, determines the set of unique colors used in the model by replacing usage of the 'color()' statement with a
custom module that echos the color value when compiled (must be done at compilation time rather than simply parsing the
.scad file since colors may be expressed as variables or other complex expressions). Note: only colors in the rgb array
format ('[0.2, 0.1, 0.8]') are supported.
Then, for each color, generates a model that replaces usage of 'color()' with a custom module that conditionally renders
its children if the color argument matches the current color we're exporting. This model is run through OpenSCAD to
generate an .stl file. Once all single-color .stl files have been exported, a manifest json file is generated that maps
each .stl filename to its rgb color info."""
from __future__ import division
from __future__ import print_function
import errno
import hashlib
import json
import logging
import os
import re
try:
import webcolors
except ImportError:
webcolors = None
from multiprocessing.dummy import Pool
import openscad
USE_INCLUDE_REGEX = re.compile(r'\b(?P<statement>use|include)\s*<\s*(?P<filename>.*?)\s*>\s*;')
COLOR_REGEX = re.compile(r'\bcolor\s*\(')
EXTRACTED_COLOR_REGEX = re.compile(r'ECHO: extracted_color = (?P<color>.*)')
# RGB_COLOR_REGEX = re.compile(r'\[(?P<r>.*?),(?P<g>.*?),(?P<b>.*?)\]')
RGBA_COLOR_REGEX = re.compile(r'\[(?P<r>[0-9.]*), *?(?P<g>[0-9.]*), *?(?P<b>[0-9.]*)(?:, *)?(?P<a>[0-9.]*)?\]')
class ColoredStlExporter(object):
def __init__(self, input_file, build_folder, openscad_variables = None):
self.logger = logging.getLogger(__name__)
self.input_file = input_file
self.intermediate_folder = os.path.join(build_folder, 'intermediate')
self.output_folder = os.path.join(build_folder, 'colored_stl')
if openscad_variables is None:
openscad_variables = {}
self.openscad_variables = openscad_variables
def run(self):
mkdir_p(self.intermediate_folder)
mkdir_p(self.output_folder)
color_values = self._extract_colors()
self.logger.debug('Found {} unique colors: {}'.format(len(color_values), color_values))
manifest = {}
def render_color(color):
file_name = self._export_stl(color)
if file_name is not None:
manifest[file_name + '.gz'] = ColoredStlExporter.parse_openscad_color(color)
pool = Pool()
for _ in pool.imap_unordered(render_color, color_values):
# Consume results as they occur so any exception is rethrown
pass
pool.close()
pool.join()
with open(os.path.join(self.output_folder, 'manifest.json'), 'w') as f:
f.write(json.dumps(manifest, indent=4))
def _extract_colors(self):
"""Returns a list of color expressions used within the input_file or any dependencies thereof"""
self.logger.info("Extracting color information...")
# Create a mutator that defines a color_extractor() module that prints the passed color argument and replaces
# all color(xyz) declarations with color_extractor(xyz) modules instances.
def replace_with_color_collector(contents):
contents = COLOR_REGEX.sub(' color_extractor(', contents)
return contents + '''
module color_extractor(c) {
echo(extracted_color=c);
children();
}'''
intermediate_subfolder = os.path.join(self.intermediate_folder, 'color_extraction')
self.walk_and_mutate_scad_files(replace_with_color_collector, intermediate_subfolder)
# Compile the mutated scad model and collect the echo output for processing
echo_file = os.path.join(self.intermediate_folder, 'color_extractor.echo')
openscad.run(
os.path.join(intermediate_subfolder, ColoredStlExporter.get_transformed_file_path(self.input_file)),
echo_file,
variables=self.openscad_variables,
capture_output=True
)
# Parse the color values from the output
color_values = set()
with open(echo_file, 'r') as f:
for line in f:
match = EXTRACTED_COLOR_REGEX.search(line)
if match:
color_values.add(match.group('color'))
return color_values
def _export_stl(self, color):
"""Exports an .stl file containing only objects of the specified color from the input model"""
# Create a mutator that defines a color_selector() module that conditionally includes its children only if the
# passed color argument is the color we're currently trying to export.
def replace_with_color_selector(contents):
contents = COLOR_REGEX.sub(' color_selector(', contents)
return contents + '''
module color_selector(c) {{
precision = 0.0001; // arbitrary
function compare_floats(x, y, i=0) =
(len(x) != len(y)) ? false // if arrays differ in length, they can't be equal
: (i >= len(x)) ? true // if we've hit the end of the arrays without issue, we're equal
: (x[i] - precision <= y[i]) && x[i] + precision >= y[i]
? compare_floats(x, y, i+1)
: false; // not equal, short circuit
if (c == {0} || compare_floats(c, {0}))
children();
}}
'''.format(color)
color_hash = hashlib.sha256(color.encode('utf-8')).hexdigest()
intermediate_subfolder = os.path.join(self.intermediate_folder, 'color_' + color_hash)
self.walk_and_mutate_scad_files(replace_with_color_selector, intermediate_subfolder)
# Name the stl model after its color (but use a hash function to make sure it's a valid filename)
file_name = color_hash + '.stl'
self.logger.info('Exporting STL for color {} as {}...'.format(color, file_name))
try:
openscad.run(
os.path.join(intermediate_subfolder, ColoredStlExporter.get_transformed_file_path(self.input_file)),
os.path.join(self.output_folder, file_name),
variables=self.openscad_variables,
capture_output=True
)
except openscad.OpenSCADException as e:
if b'Current top level object is empty.' in e.stderr:
# No geometry in this color
self.logger.debug(f'Ignoring color due to empty top level object. {color} {color_hash}')
return None
else:
raise
return file_name
def walk_and_mutate_scad_files(self, mutate_function, intermediate_subfolder):
mkdir_p(intermediate_subfolder)
visited = set()
to_process = [self.input_file]
while len(to_process):
current_file = to_process.pop(0)
self.logger.debug('Processing {}'.format(current_file))
with open(current_file, 'rb') as f:
contents = f.read()
# Only process .scad files; copy any other file types (e.g. fonts) over as-is
if current_file.lower().endswith('.scad'):
contents = contents.decode('utf-8')
current_folder = os.path.dirname(current_file)
for include in USE_INCLUDE_REGEX.finditer(contents):
next_filename = os.path.realpath(
os.path.join(current_folder, include.group('filename')))
if next_filename not in visited:
to_process.append(next_filename)
visited.add(next_filename)
def replace(match):
return '{} <{}>;'.format(match.group('statement'),
ColoredStlExporter.get_transformed_file_path(
os.path.join(current_folder, match.group('filename'))))
contents = mutate_function(USE_INCLUDE_REGEX.sub(replace, contents)).encode('utf-8')
with open(os.path.join(intermediate_subfolder,
ColoredStlExporter.get_transformed_file_path(current_file)), 'wb') as out_file:
out_file.write(contents)
@staticmethod
def get_transformed_file_path(original_path):
extension = os.path.splitext(original_path)[1]
return hashlib.sha256(os.path.realpath(original_path).encode('utf-8')).hexdigest() + extension
@staticmethod
def parse_openscad_color(color):
match = RGBA_COLOR_REGEX.search(color)
if match:
color_out = [
float(match.group('r')),
float(match.group('g')),
float(match.group('b')),
]
if(match.group('a')):
color_out.append(float(match.group('a')))
return color_out
if '"' in color and webcolors:
try:
c = webcolors.name_to_rgb(color[1:-1]) # skip the ""
return c.red/255., c.green/255., c.blue/255.
except ValueError:
pass
raise ValueError('Failed to parse color. Must be named webcolor or in [<r>, <g>, <b>] format. {}'.format(color))
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
raise
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('input_file', type=str, help='OpenSCAD file to parse into STLs')
parser.add_argument('output_folder', type=str, help='directory to place the colored STL files')
args = parser.parse_args()
ColoredStlExporter(args.input_file, args.output_folder).run()
================================================
FILE: 3d/scripts/dependencies.sh
================================================
#!/bin/bash
set -e
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
set -v
sudo apt-get update -qq
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y openscad inkscape imagemagick xvfb texlive-extra-utils
sudo pip install -r "$DIR/requirements.txt"
================================================
FILE: 3d/scripts/generate_2d.py
================================================
#!/usr/bin/env python3
# Copyright 2015-2021 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import json
import logging
import os
import subprocess
import sys
import zipfile
from kerf_presets import KERF_PRESETS
from svg_processor import SvgProcessor
from projection_renderer import Renderer
script_dir = os.path.dirname(os.path.abspath(__file__))
source_parts_dir = os.path.dirname(script_dir)
repo_root = os.path.dirname(source_parts_dir)
sys.path.append(repo_root)
from util import (
app_paths,
inkscape,
rev_info,
)
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
parser = argparse.ArgumentParser()
parser.add_argument('--panelize', type=int, default=1, help='Quantity to panelize - must be 1 or an even number')
parser.add_argument('--skip-optimize', action='store_true', help='Don\'t remove redundant/overlapping cut lines')
kerf_group = parser.add_mutually_exclusive_group()
kerf_group.add_argument('--kerf', type=float, help='Override kerf_width value')
kerf_group.add_argument('--kerf-preset', choices=KERF_PRESETS, help='Override kerf_width using a defined preset')
parser.add_argument('--render-raster', action='store_true', help='Render raster PNG from the output SVG (requires '
'Inkscape)')
parser.add_argument('--calculate-dimensions', action='store_true', help='Calculate overall cut file dimensions ('
'requires Inkscape)')
parser.add_argument('--num-flaps', type=int, help='Override NUM_FLAPS')
parser.add_argument('--thickness', type=float, help='Override panel thickness value')
parser.add_argument('--no-etch', action='store_true', help='Do not render laser-etched features')
parser.add_argument('--mirror', action='store_true', help='Mirror the assembly so the outside faces are facing up. '
'Note that this will remove all etched features.')
parser.add_argument('--render-elecrow', action='store_true', help='Render an additional zipped pdf with labeled '
'dimensions and only cut lines, for Elecrow. '
'Requires Inkscape and pdfjam. Implies '
'--no-etch, --calculate-dimensions, '
'--skip-optimize, and --mirror')
parser.add_argument('--no-alignment-bar', action='store_true', help='Do not include features for the alignment bar')
parser.add_argument('--no-front-panel', action='store_true', help='Do not include the front face of the enclosure, '
'e.g. if you will use '
'generate_combined_front_panel.py instead.')
parser.add_argument('--no-mounting-holes', action='store_true', help='Do not include mounting hole on top/bottom '
'enclosure pieces.')
parser.add_argument('--no-connectors', action='store_true', help='Do not include inter-module connector pieces.')
parser.add_argument('--no-source-info', action='store_true', help='Do not include the source info: revision, date, url.')
parser.add_argument('--cut-home-indicator', action='store_true', help='Cut, instead of etch, the home position '
'indicator on the spool.')
args = parser.parse_args()
if args.panelize > 1:
raise RuntimeError('The --panelize option is no longer supported for v2 hardware')
if args.render_elecrow:
args.no_etch = True
args.calculate_dimensions = True
args.skip_optimize = True
args.mirror = True
laser_parts_directory = os.path.join(source_parts_dir, 'build', 'laser_parts')
extra_variables = {
'render_revision': rev_info.git_short_rev(),
'render_date': rev_info.current_date(),
'render_etch': not args.no_etch,
'render_2d_mirror': args.mirror,
'enable_alignment_bar': not args.no_alignment_bar,
'render_front_panel': not args.no_front_panel,
'enable_mounting_holes': not args.no_mounting_holes,
'enable_connectors': not args.no_connectors,
'enable_source_info': not args.no_source_info,
'render_home_indicator_as_cut': args.cut_home_indicator,
}
if args.kerf is not None:
extra_variables['kerf_width'] = args.kerf
elif args.kerf_preset is not None:
extra_variables['kerf_width'] = KERF_PRESETS[args.kerf_preset]
if args.thickness is not None:
extra_variables['thickness'] = args.thickness
if args.num_flaps is not None:
extra_variables['num_flaps'] = args.num_flaps
print('Variables:\n' + json.dumps(extra_variables, indent=4))
renderer = Renderer(os.path.join(source_parts_dir, 'splitflap.scad'), laser_parts_directory, extra_variables)
renderer.clean()
svg_output, output_data = renderer.render_svgs(panelize_quantity=args.panelize)
logging.debug(f'Output data: {output_data}')
processor = SvgProcessor(svg_output)
redundant_lines, merged_lines = None, None
if not args.skip_optimize:
logging.info('Removing redundant lines')
redundant_lines, merged_lines = processor.remove_redundant_lines()
processor.write(svg_output)
logging.info(f'\n\n\nDone rendering to SVG: {svg_output}')
if args.calculate_dimensions:
def getDimensionSvgContents(text, width):
return f'<svg height="40" width="{width}" xmlns="http://www.w3.org/2000/svg" style="background-color:#eeeeee;"><text x="4" y="32" fill="black" style="font-style:normal;font-variant:normal;font-weight:normal;font-size:28pt;font-family:sans-serif;">{text}</text></svg>'
module_dimensions_file = os.path.join(laser_parts_directory, os.path.splitext(os.path.basename(svg_output))[0] + '_module_dimensions.svg')
with open(module_dimensions_file, 'w') as f:
f.write(getDimensionSvgContents(f'{output_data["enclosure_width"]}mm width, {output_data["enclosure_height"]}mm height, {output_data["enclosure_length"]}mm depth', 1100))
svg_for_dimensions = os.path.join(laser_parts_directory, os.path.splitext(os.path.basename(svg_output))[0] + '_dimensions.svg')
processor.apply_dimension_calculation_style()
processor.write(svg_for_dimensions)
logging.info('Read dimensions')
width_px = float(subprocess.check_output([
app_paths.get('inkscape'),
] + inkscape.without_gui() + [
'--query-width',
svg_for_dimensions,
]))
width_mm = width_px * 25.4 / 96 # Assuming 96dpi...
height_px = float(subprocess.check_output([
app_paths.get('inkscape'),
] + inkscape.without_gui() + [
'--query-height',
svg_for_dimensions,
]))
height_mm = height_px * 25.4 / 96 # Assuming 96dpi...
panel_dimensions_file = os.path.join(laser_parts_directory, os.path.splitext(os.path.basename(svg_output))[0] + '_panel_dimensions.svg')
with open(panel_dimensions_file, 'w') as f:
f.write(getDimensionSvgContents(f'{width_mm:.2f}mm x {height_mm:.2f}mm', 500))
if args.render_elecrow:
elecrow_svg = os.path.join(laser_parts_directory, 'elecrow.svg')
elecrow_intermediate_pdf = os.path.join(laser_parts_directory, 'elecrow_intermediate.pdf')
elecrow_pdf = os.path.join(laser_parts_directory, 'elecrow.pdf')
elecrow_zip = os.path.join(laser_parts_directory, 'elecrow.zip')
processor.apply_elecrow_style()
processor.add_dimensions(width_mm, height_mm, args.mirror)
processor.write(elecrow_svg)
logging.info('Resize SVG canvas')
# since version 1.2, inkscape has replaced 'verbs' with 'actions'
# see: https://wiki.inkscape.org/wiki/Using_the_Command_Line
if inkscape._version() < 1.2:
subprocess.check_call([
app_paths.get('inkscape'),
'--verb=FitCanvasToDrawing',
'--verb=FileSave',
'--verb=FileClose',
'--verb=FileQuit',
elecrow_svg,
])
else:
subprocess.check_call([
app_paths.get('inkscape'),
"--actions=select-all;fit-canvas-to-selection;export-type:svg;export-plain-svg;export-do",
elecrow_svg,
"-o", elecrow_svg,
])
logging.info('Output PDF')
subprocess.check_call([
app_paths.get('inkscape'),
elecrow_svg,
] + inkscape.export_pdf(elecrow_intermediate_pdf))
logging.info('Resize PDF')
subprocess.check_call([
app_paths.get('pdfjam'),
'--papersize',
f'{{{width_mm + 80}mm,{height_mm + 80}mm}}',
'--noautoscale',
'true',
'--outfile',
elecrow_pdf,
elecrow_intermediate_pdf,
])
logging.info('Zip')
with zipfile.ZipFile(elecrow_zip, 'w', zipfile.ZIP_DEFLATED) as zip:
zip.write(elecrow_pdf, 'elecrow.pdf')
if args.render_raster:
# Export to png
logging.info('Generating raster preview')
raster_svg = os.path.join(laser_parts_directory, 'raster.svg')
raster_png = os.path.join(laser_parts_directory, 'raster.png')
processor.apply_raster_render_style()
if not args.skip_optimize:
# Show which redundant lines were removed and lines merged
processor.add_highlight_lines(redundant_lines, '#ff0000')
processor.add_highlight_lines(merged_lines, '#0000ff')
processor.write(raster_svg)
logging.info('Resize SVG canvas')
subprocess.check_call([
app_paths.get('inkscape'),
'--verb=FitCanvasToDrawing',
'--verb=FileSave',
'--verb=FileClose',
'--verb=FileQuit',
raster_svg,
])
logging.info('Export PNG')
subprocess.check_call([
app_paths.get('inkscape'),
'--export-width=320',
'--export-background=white',
] + inkscape.export_png(raster_png) + [
raster_svg,
])
================================================
FILE: 3d/scripts/generate_3d_print_flaps.py
================================================
#!/usr/bin/env python3
# Copyright 2025 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import division
from __future__ import print_function
import argparse
import logging
import os
import sys
import time
import openscad
from multiprocessing.dummy import Pool
from shutil import rmtree
script_dir = os.path.dirname(os.path.abspath(__file__))
source_parts_dir = os.path.dirname(script_dir)
repo_root = os.path.dirname(source_parts_dir)
sys.path.append(repo_root)
from util import file_util
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
parser = argparse.ArgumentParser()
parser.add_argument(
"-f",
"--font",
type=str,
help="Name of font preset to use - see flap_fonts.scad",
)
parser.add_argument("-t", "--text", type=str, help="String of text to generate")
parser.add_argument(
"--vertical-keepout-clip",
action="store_true",
help="Clip letters that violate vertical keepout zones",
)
parser.add_argument(
"--single-stl",
action="store_true",
default=False,
help="Generate a single STL file for each flap",
)
parser.add_argument(
"--single-letter-stl",
action="store_true",
default=False,
help="Generate a single STL file for the front and back letters (only used if --single-stl is not set)",
)
parser.add_argument(
"--parallelism",
type=int,
help="How many OpenSCAD exports to run in parallel. Defaults to CPU count if unspecified.",
default=None,
)
args = parser.parse_args()
extra_variables = {}
if args.font is not None:
extra_variables["font_preset"] = args.font
if args.text is not None:
extra_variables["character_list"] = args.text
if args.vertical_keepout_clip:
extra_variables["vertical_keepout_mode"] = 2
output_directory = os.path.join(source_parts_dir, "build", "flap_3dp")
rmtree(output_directory, ignore_errors=True)
def render_flap(flap_number):
flap_path = os.path.join(output_directory, f"flap_{flap_number:02}")
file_util.mkdir_p(flap_path)
openscad_variables = {
"flap_number": flap_number,
"generateFullFlap": args.single_stl,
"generateText": False,
}
openscad_variables.update(extra_variables)
openscad.run(
os.path.join(source_parts_dir, "flap_3dp.scad"),
os.path.join(flap_path, f"{flap_number:02}_flap.stl"),
variables=openscad_variables,
capture_output=True,
)
if not args.single_stl:
if args.single_letter_stl:
openscad_variables["generateText"] = True
openscad.run(
os.path.join(source_parts_dir, "flap_3dp.scad"),
os.path.join(flap_path, f"{flap_number:02}_letters.stl"),
variables=openscad_variables,
capture_output=True,
ignore_empty_top_level_object=True,
)
else:
openscad_variables["generateText"] = True
openscad_variables["generateFrontText"] = True
openscad_variables["generateBackText"] = False
openscad.run(
os.path.join(source_parts_dir, "flap_3dp.scad"),
os.path.join(flap_path, f"{flap_number:02}_letter_front.stl"),
variables=openscad_variables,
capture_output=True,
ignore_empty_top_level_object=True,
)
openscad_variables["generateText"] = True
openscad_variables["generateFrontText"] = False
openscad_variables["generateBackText"] = True
openscad.run(
os.path.join(source_parts_dir, "flap_3dp.scad"),
os.path.join(flap_path, f"{flap_number:02}_letter_back.stl"),
variables=openscad_variables,
capture_output=True,
ignore_empty_top_level_object=True,
)
pool = Pool(args.parallelism)
num_flaps = len(args.text) if args.text is not None else 52
for _ in pool.imap_unordered(render_flap, range(num_flaps)):
# Consume results as they occur so any exception is rethrown
pass
pool.close()
pool.join()
================================================
FILE: 3d/scripts/generate_combined_front_panel.py
================================================
#!/usr/bin/env python3
# Copyright 2021 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import division
from __future__ import print_function
import argparse
import logging
import os
import subprocess
import sys
from kerf_presets import KERF_PRESETS
from svg_processor import SvgProcessor
from projection_renderer import Renderer
script_dir = os.path.dirname(os.path.abspath(__file__))
source_parts_dir = os.path.dirname(script_dir)
repo_root = os.path.dirname(source_parts_dir)
sys.path.append(repo_root)
from util import (
app_paths,
inkscape,
)
CENTER_MODES = {
'letter': 0,
'window': 1,
'module': 2,
}
def render(extra_variables, output_directory):
renderer = Renderer(os.path.join(source_parts_dir, 'combined_front_panel.scad'), output_directory, extra_variables)
renderer.clean()
svg_output, _ = renderer.render_svgs(panelize_quantity = 1)
logging.info('\n\n\nDone rendering to SVG: ' + svg_output)
return svg_output
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
parser = argparse.ArgumentParser("")
kerf_group = parser.add_mutually_exclusive_group(required=True)
kerf_group.add_argument('--kerf', type=float, help='Set kerf_width value')
kerf_group.add_argument('--kerf-preset', choices=KERF_PRESETS, help='Set kerf_width using a defined preset')
kerf_group.add_argument('--tool-diameter', type=float, help='Diameter of cutting tool')
parser.add_argument('--render-raster', action='store_true', help='Render raster PNG from the output SVG (requires '
'Inkscape)')
parser.add_argument('--rows', type=int, required=True, help='Number of rows')
parser.add_argument('--cols', type=int, required=True, help='Number of columns')
parser.add_argument('--num-flaps', type=int, help='Override NUM_FLAPS')
x_group = parser.add_mutually_exclusive_group(required=True)
x_group.add_argument('--spacing-x', type=float, help='Horizontal gap between modules')
x_group.add_argument('--center-center-x', type=float, help='Horizontal center-to-center distance between modules')
y_group = parser.add_mutually_exclusive_group(required=True)
y_group.add_argument('--spacing-y', type=float, help='Vertical gap between modules')
y_group.add_argument('--center-center-y', type=float, help='Vertical center-to-center distance between modules')
width_group = parser.add_mutually_exclusive_group(required=True)
width_group.add_argument('--width', type=float, help='Width of the panel')
width_group.add_argument('--frame-margin-x', type=float, help='Margin to add to the left and right sides')
height_group = parser.add_mutually_exclusive_group(required=True)
height_group.add_argument('--height', type=float, help='Height of the panel')
height_group.add_argument('--frame-margin-y', type=float, help='Margin to add to the top and bottom')
parser.add_argument('--center-mode', choices=CENTER_MODES, required=True, help='Specify how modules should be centered')
args = parser.parse_args()
extra_variables = {
'render_etch': False,
}
if args.kerf is not None:
extra_variables['kerf_width'] = args.kerf
elif args.kerf_preset is not None:
extra_variables['kerf_width'] = KERF_PRESETS[args.kerf_preset]
elif args.tool_diameter is not None:
extra_variables['tool_diameter'] = args.tool_diameter
extra_variables['rows'] = args.rows
extra_variables['cols'] = args.cols
if args.num_flaps is not None:
extra_variables['num_flaps'] = args.num_flaps
if args.spacing_x is not None:
extra_variables['gap_x'] = args.spacing_x
if args.spacing_y is not None:
extra_variables['gap_y'] = args.spacing_y
if args.center_center_x is not None:
extra_variables['center_center_x'] = args.center_center_x
if args.center_center_y is not None:
extra_variables['center_center_y'] = args.center_center_y
if args.width is not None:
extra_variables['frame_width'] = args.width
if args.height is not None:
extra_variables['frame_height'] = args.height
if args.frame_margin_x is not None:
extra_variables['frame_margin_x'] = args.frame_margin_x
if args.frame_margin_y is not None:
extra_variables['frame_margin_y'] = args.frame_margin_y
extra_variables['center_mode'] = CENTER_MODES[args.center_mode]
output_dir = os.path.join(source_parts_dir, 'build', 'front_panel')
svg_output = render(extra_variables, output_dir)
if args.render_raster:
# Export to png
logging.info('Generating raster preview')
processor = SvgProcessor(svg_output)
raster_svg = os.path.join(output_dir, 'raster.svg')
raster_png = os.path.join(output_dir, 'raster.png')
processor.apply_raster_render_style()
processor.write(raster_svg)
# logging.info('Resize SVG canvas')
# subprocess.check_call([
# app_paths.get('inkscape'),
# '--verb=FitCanvasToDrawing',
# '--verb=FileSave',
# '--verb=FileClose',
# '--verb=FileQuit',
# raster_svg,
# ])
logging.info('Export PNG')
subprocess.check_call([
app_paths.get('inkscape'),
'--export-width=1080',
'--export-background=white',
] + inkscape.export_png(raster_png) + [
raster_svg,
])
================================================
FILE: 3d/scripts/generate_fonts.py
================================================
#!/usr/bin/env python3
# Copyright 2020 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import division
from __future__ import print_function
import argparse
import logging
import os
import subprocess
import sys
from svg_processor import SvgProcessor
from projection_renderer import Renderer
script_dir = os.path.dirname(os.path.abspath(__file__))
source_parts_dir = os.path.dirname(script_dir)
repo_root = os.path.dirname(source_parts_dir)
sys.path.append(repo_root)
from util import (
app_paths,
inkscape,
rev_info,
)
_MODES = {
'double-sided': 0,
'front-back': 1,
'full-font': 2,
'side-by-side': 3,
}
def render(extra_variables, skip_optimize, output_directory, render_raster):
renderer = Renderer(os.path.join(source_parts_dir, 'font_generator.scad'), output_directory, extra_variables)
renderer.clean()
svg_output, _ = renderer.render_svgs(panelize_quantity = 1)
processor = SvgProcessor(svg_output)
if not skip_optimize:
logging.info('Removing redundant lines')
processor.remove_redundant_lines()
processor.write(svg_output)
logging.info('\n\n\nDone rendering to SVG: ' + svg_output)
if render_raster:
# Export to png
logging.info('Generating raster preview')
raster_png = os.path.join(output_directory, 'raster.png')
logging.info('Export PNG')
subprocess.check_call([
app_paths.get('inkscape'),
'--export-width=1080',
'--export-background=white',
] + inkscape.export_png(raster_png) + [
svg_output,
])
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
parser = argparse.ArgumentParser()
parser.add_argument('--mode', choices=_MODES.keys(), required=True)
parser.add_argument('--render-raster', action='store_true', help='Render raster PNG from the output SVG (requires '
'Inkscape)')
parser.add_argument('-f', '--font', type=str, help='Name of font preset to use - see flap_fonts.scad')
parser.add_argument('-t', '--text', type=str, help='String of text to generate')
parser.add_argument('--columns', type=int, help='Number of columns')
parser.add_argument('--start-row', type=int, help='The starting row to render, 0-indexed')
parser.add_argument('--row-count', type=int, help='Number of rows to render')
parser.add_argument('--spacing-x', type=float, help='Horizontal gap between flaps')
parser.add_argument('--spacing-y', type=float, help='Vertical gap between flaps')
parser.add_argument('--no-comp', action='store_true', default=False, help='Don\'t compensate for the gap between top and bottom flaps')
parser.add_argument('--alignment', action='store_true', help='Render alignment markers for registration when printing onto flaps')
parser.add_argument('--bleed', type=float, help='Bleed amount for letters, in mm')
parser.add_argument('--kerf', type=float, help='Override kerf_width value')
parser.add_argument('--fill', action='store_true', help='Fill the text solid (disables optimization)')
parser.add_argument('--skip-optimize', action='store_true', help='Don\'t remove redundant/overlapping cut lines')
parser.add_argument('--vertical-keepout-clip', action='store_true', help='Clip letters that violate vertical keepout zones')
args = parser.parse_args()
extra_variables = {
'render_revision': rev_info.git_short_rev(),
'render_date': rev_info.current_date(),
'letter_gap_comp' : not args.no_comp,
}
if args.font is not None:
extra_variables['font_preset'] = args.font
if args.text is not None:
extra_variables['character_list'] = args.text
if args.start_row is not None:
extra_variables['start_row'] = args.start_row
if args.row_count is not None:
extra_variables['row_count'] = args.row_count
if args.columns is not None:
extra_variables['num_columns'] = args.columns
if args.spacing_x is not None:
extra_variables['spacing_x'] = args.spacing_x
if args.spacing_y is not None:
extra_variables['spacing_y'] = args.spacing_y
if args.kerf is not None:
extra_variables['kerf_width'] = args.kerf
if args.fill:
extra_variables['render_fill'] = True
args.skip_optimize = True
if args.bleed is not None:
extra_variables['bleed'] = args.bleed
if args.vertical_keepout_clip:
extra_variables['vertical_keepout_mode'] = 2
fonts_directory = os.path.join(source_parts_dir, 'build', 'fonts')
extra_variables['layout_mode'] = _MODES[args.mode]
if args.mode in ['double-sided', 'front-back']:
extra_variables['render_alignment_marks'] = args.alignment
extra_variables['only_side'] = 1
extra_variables['flip_entire_layout'] = False
extra_variables['flip_individual_flaps'] = False
render(extra_variables, args.skip_optimize, os.path.join(fonts_directory, 'front'), args.render_raster)
extra_variables['only_side'] = 2
if args.mode == 'double-sided':
extra_variables['flip_entire_layout'] = True
extra_variables['flip_individual_flaps'] = False
elif args.mode == 'front-back':
extra_variables['flip_entire_layout'] = False
extra_variables['flip_individual_flaps'] = True
render(extra_variables, args.skip_optimize, os.path.join(fonts_directory, 'back'), args.render_raster)
else:
if args.alignment:
raise RuntimeError('Alignment markers are only compatible with double-sided rendering')
extra_variables['only_side'] = 1
extra_variables['flip_entire_layout'] = False
render(extra_variables, args.skip_optimize, fonts_directory, args.render_raster)
================================================
FILE: 3d/scripts/generate_gif.py
================================================
#!/usr/bin/env python3
# Copyright 2015-2021 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import errno
import functools
import logging
import os
import shutil
import subprocess
from multiprocessing.dummy import Pool
import openscad
logging.basicConfig(level=logging.DEBUG)
NUM_FLAPS = 52 # TODO: read from source?
def generate_gif(output_folder, delay, filename):
command = [
'convert',
os.path.join(output_folder, 'frames', 'frame*.png'),
'-set', 'delay', str(delay),
os.path.join(output_folder, filename),
]
logging.debug(command)
subprocess.check_call(command)
def render_rotation(input_file, output_folder, num_frames, start_frame, variables):
def render_frame(i):
angle = 135 + i * 360 / num_frames
openscad.run(
input_file,
os.path.join(output_folder, 'frames', 'frame_%05d.png' % (start_frame + i)),
output_size = [320, 320],
camera_translation = [0, 0, -15],
camera_rotation = [60, 0, angle],
camera_distance = 600,
variables = variables,
colorscheme = 'DeepOcean',
)
pool = Pool()
for _ in pool.imap_unordered(render_frame, range(num_frames)):
# Consume results as they occur so any exception is rethrown
pass
pool.close()
pool.join()
def render_flaps(input_file, output_folder, variables):
def render_flap(i):
merged_variables = {}
merged_variables.update(variables)
merged_variables.update({'render_flap_index': i})
openscad.run(
input_file,
os.path.join(output_folder, 'frames', 'frame_%05d.png' % (i)),
output_size = [600,800],
camera_translation = [-5, 0, -9],
camera_rotation = [90, 0, 180],
camera_distance = 500,
variables = merged_variables,
colorscheme = 'Starnight',
)
pool = Pool()
for _ in pool.imap_unordered(render_flap, range(NUM_FLAPS)):
# Consume results as they occur so any exception is rethrown
pass
pool.close()
pool.join()
script_dir = os.path.dirname(os.path.abspath(__file__))
source_parts_dir = os.path.dirname(script_dir)
input_file = os.path.join(source_parts_dir, 'splitflap.scad')
output_folder = os.path.join(source_parts_dir, 'build', 'animation')
shutil.rmtree(output_folder, ignore_errors=True)
frames_folder = os.path.join(output_folder, 'frames')
os.makedirs(frames_folder)
num_frames = 50
render_rotation(input_file, output_folder, num_frames, 0, {
'render_enclosure': 2,
'render_flaps': 2,
})
render_rotation(input_file, output_folder, num_frames, num_frames, {
'render_enclosure': 1,
'render_flaps': 2,
})
generate_gif(output_folder, '1x15', 'animation.gif')
shutil.rmtree(frames_folder, ignore_errors=True)
os.makedirs(frames_folder)
render_flaps(input_file, output_folder, {
'render_enclosure': 2,
'render_flaps': 2,
})
generate_gif(output_folder, '20', 'all_flaps.gif')
================================================
FILE: 3d/scripts/generate_snapshot.py
================================================
#!/usr/bin/env python
# Copyright 2015-2016 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import division
from __future__ import print_function
import errno
import logging
import os
import openscad
logging.basicConfig(level=logging.DEBUG)
output_folder = os.path.join('build')
try:
os.makedirs(output_folder)
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(output_folder):
pass
else:
raise
openscad.run(
'splitflap.scad',
os.path.join(output_folder, 'snapshot.png'),
output_size = [1280, 1024],
camera_translation = [0, 0, 0],
camera_rotation = [60, 0, 135],
camera_distance = 600,
colorscheme = 'Nature',
)
================================================
FILE: 3d/scripts/generate_stl.py
================================================
#!/usr/bin/env python3
# Copyright 2015-2021 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import os
import sys
from colored_stl_exporter import ColoredStlExporter
script_dir = os.path.dirname(os.path.abspath(__file__))
source_parts_dir = os.path.dirname(script_dir)
repo_root = os.path.dirname(source_parts_dir)
sys.path.append(repo_root)
from util import rev_info
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
openscad_variables = {
'render_3d': True,
'render_enclosure': 2,
'render_flaps': 2,
'render_message': 'Ag',
'render_pcb': True,
'render_revision': rev_info.git_short_rev(),
'render_date': rev_info.current_date(),
}
exporter = ColoredStlExporter(
os.path.join(source_parts_dir, 'splitflap.scad'),
os.path.join(source_parts_dir, 'build'),
openscad_variables)
exporter.run()
================================================
FILE: 3d/scripts/kerf_presets.py
================================================
KERF_PRESETS = {
'ponoko-3mm-mdf': 0.18,
'ponoko-3mm-acrylic': 0.1,
'elecrow-3mm-wood': 0.185,
'elecrow-3mm-acrylic': 0.1,
}
================================================
FILE: 3d/scripts/openscad.py
================================================
# Copyright 2015-2020 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import division
from __future__ import print_function
import logging
import numbers
import os
import re
import subprocess
import sys
repo_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
sys.path.append(repo_root)
from util import app_paths
logger = logging.getLogger(__name__)
class OpenSCADException(Exception):
def __init__(self, message, returncode, stdout=None, stderr=None):
truncated_stdout = '\n'.join(stdout.decode('utf-8').splitlines()[-20:]) if stdout is not None else None
truncated_stderr = '\n'.join(stderr.decode('utf-8').splitlines()[-20:]) if stderr is not None else None
super(OpenSCADException, self).__init__('%s\n\nRETURN CODE:%d\n\nSTDOUT:\n%s\n\nSTDERR:\n%s' % (
message, returncode, truncated_stdout, truncated_stderr))
self.returncode = returncode
self.stdout = stdout
self.stderr = stderr
def run(
input_file,
output_file,
output_size=None,
camera_translation=None,
camera_rotation=None,
camera_distance=None,
variables=None,
capture_output=False,
colorscheme=None,
ignore_empty_top_level_object=False,
):
command = [
app_paths.get('openscad'),
'-o', output_file,
]
if output_size is not None:
command += ['--imgsize=%d,%d' % tuple(output_size)]
if camera_translation is not None or camera_rotation is not None or camera_distance is not None:
if camera_translation is None:
camera_translation = [0, 0, 0]
if camera_rotation is None:
camera_rotation = [60, 0, 135]
if camera_distance is None:
camera_distance = 600
command += ['--camera=%f,%f,%f,%f,%f,%f,%f' % tuple(camera_translation + camera_rotation + [camera_distance])]
if colorscheme is not None:
command += ['--colorscheme=%s' % colorscheme]
if variables is not None:
for k, v in variables.items():
if isinstance(v, str) or isinstance(v, bytes):
try:
v = v.decode('utf-8')
except (UnicodeDecodeError, AttributeError):
pass
value = '"%s"' % v.replace('"', '\\"')
elif v is True:
value = 'true'
elif v is False:
value = 'false'
elif isinstance(v, numbers.Integral):
value = '%d' % v
elif isinstance(v, numbers.Real):
value = '%f' % v
else:
raise ValueError("Unknown value type", type(v), v)
command += ['-D', '%s=%s' % (k, value)]
command += [input_file]
logger.info(command)
if ignore_empty_top_level_object:
# In order to ignore empty top level object errors, we need to be able to look at stderr
capture_output = True
stdout_type = subprocess.PIPE if capture_output else None
stderr_type = subprocess.PIPE if capture_output else None
try:
proc = subprocess.Popen(command, stdout=stdout_type, stderr=stderr_type)
stdout, stderr = proc.communicate()
returncode = proc.returncode
except OSError as e:
if 'No such file or directory' in e.strerror:
raise RuntimeError('Could not find openscad! Make sure you\'ve installed OpenSCAD and it\'s on your PATH '
'environment variable')
else:
raise
logger.debug('returncode:%d', returncode)
if returncode != 0:
if ignore_empty_top_level_object and b'Current top level object is empty.' in stderr:
logger.info('Ignoring empty top level object!')
else:
raise OpenSCADException('openscad returned non-zero!', returncode, stdout=stdout, stderr=stderr)
return stdout, stderr
def extract_values(stderr):
"""
Extracts values from ECHO statements as a dictionary. Values are strings.
"""
result = {}
pattern = re.compile(r'^ECHO: (.+) = (.+)$')
for line in stderr.splitlines():
m = pattern.search(line.decode('utf-8'))
if m:
result[m.group(1)] = m.group(2)
return result
================================================
FILE: 3d/scripts/projection_renderer.py
================================================
# Copyright 2015-2021 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import division
from __future__ import print_function
import logging
import os
import shutil
import openscad
from svg_processor import SvgProcessor
class Renderer(object):
def __init__(self, input_file, output_folder, extra_variables=None):
self.input_file = input_file
self.output_folder = output_folder
if extra_variables is None:
extra_variables = {}
self.extra_variables = extra_variables
try:
self.etch_enabled = self.extra_variables['render_etch']
except KeyError:
self.etch_enabled = True
def clean(self):
shutil.rmtree(self.output_folder, ignore_errors=True)
os.makedirs(self.output_folder)
def _get_variables(self, variables):
v = self.extra_variables.copy()
v.update(variables)
return v
def _get_extracted_outputs(self):
stdout, stderr = openscad.run(
self.input_file,
os.path.join(self.output_folder, 'dummy.png'),
output_size=[1,1],
variables = self._get_variables({
'_is_projection_rendering': True,
'render_3d': False,
'render_index': 0,
}),
capture_output=True,
)
outputs = openscad.extract_values(stderr)
for key, value in outputs.items():
if key.startswith('debug_'):
logging.debug('DEBUG VALUE %r = %r', key, value)
return outputs
def _get_component_file(self, i):
return os.path.join(self.output_folder, 'component_%05d.svg' % i)
def _render_component(self, i, panel_horizontal, panel_vertical):
output_file = self._get_component_file(i)
style_options = ['cut']
if self.etch_enabled:
style_options.append('etch')
for style in (style_options):
logging.debug('Rendering component %d, %s', i, style)
try:
_ = openscad.run(
self.input_file,
output_file,
variables=self._get_variables({
'_is_projection_rendering': True,
'render_3d': False,
'render_index': i,
'render_etch': style == 'etch',
'panel_horizontal': panel_horizontal,
'panel_vertical': panel_vertical,
}),
capture_output=True,
)
except openscad.OpenSCADException as e:
if b'Current top level object is not a 2D object.' in e.stderr:
# This is expected if we try rendering an etch layer as a
# cut, since there will be nothing to export
continue
else:
raise
processor = SvgProcessor(output_file)
if style == 'cut':
processor.apply_laser_cut_style()
elif style == 'etch':
processor.apply_laser_etch_style()
return processor
logging.debug('Component %d has no geometry', i)
return None
def render_svgs(self, panelize_quantity):
assert panelize_quantity == 1 or panelize_quantity % 2 == 0, 'Panelize quantity must be 1 or an even number'
outputs = self._get_extracted_outputs()
num_components = int(outputs['num_components'])
logging.info('Found %d components to render', num_components)
svg_output = None
horizontal_range = 1 if panelize_quantity == 1 else 2
vertical_range = (panelize_quantity + 1) // 2
for panel_horizontal in range(0, horizontal_range):
for panel_vertical in range(0, vertical_range):
for i in range(num_components):
svg_processor = self._render_component(i, panel_horizontal, panel_vertical)
if svg_output is None:
svg_output = svg_processor
elif svg_processor is not None:
svg_output.import_paths(svg_processor)
output_file_path = os.path.join(self.output_folder, 'combined.svg')
svg_output.write(output_file_path)
return output_file_path, outputs
================================================
FILE: 3d/scripts/requirements.txt
================================================
svg.path==6.3
================================================
FILE: 3d/scripts/svg_processor.py
================================================
# Copyright 2015-2016 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function
from collections import defaultdict
import os
svg_path_install = f'python3 -m pip install -r {os.path.join(os.path.dirname(__file__), "requirements.txt")}'
try:
from svg.path import (
Path,
Line,
Move,
Close,
parse_path,
)
except:
raise RuntimeError(f'Error loading svg.path library. Run "{svg_path_install}" to install it')
from importlib.metadata import version
assert int(version('svg.path').split('.')[0]) == 6, f'svg.path library is not new enough (found {version("svg.path")}). Run "{svg_path_install}" to install it'
from xml.dom import minidom
eps = 0.001
def _get_slope_intersect(p1, p2):
if abs(p1.real - p2.real) < eps:
# Vertical; no slope and return x-intercept
return None, p1.real
m = (p2.imag - p1.imag) / (p2.real - p1.real)
b1 = p1.imag - m * p1.real
b2 = p2.imag - m * p2.real
assert abs(b1 - b2) < eps
return m, b1
def _lines_are_collinear(line1, line2):
eq1 = _get_slope_intersect(line1.start, line1.end)
eq2 = _get_slope_intersect(line2.start, line2.end)
same_slope = ((eq1[0] is None and eq2[0] is None)
or (eq1[0] is not None and eq2[0] is not None and abs(eq1[0] - eq2[0]) < eps))
same_intercept = abs(eq1[1] - eq2[1]) < eps
return same_slope and same_intercept
class SvgProcessor(object):
"""
Processes SVG files generated by OpenSCAD to prepare for laser cutting
"""
def __init__(self, input_file):
self.dom = minidom.parse(input_file)
self.svg_node = self.dom.documentElement
def set_dimensions(self, width, height):
self.svg_node.attributes['width'].value = width
self.svg_node.attributes['height'].value = height
def set_viewbox(self, min_x, min_y, width, height):
view_str = "{:.0f} {:.0f} {:.0f} {:.0f}".format(min_x, min_y, width, height)
self.svg_node.attributes['viewBox'].value = view_str
def get_viewbox(self):
vb = self.svg_node.attributes['viewBox'].value.replace(',', '').split(' ')
vb = [float(x) for x in vb]
return tuple(vb)
def apply_laser_cut_style(self):
# Set fill and stroke for laser cutting
for path in self.svg_node.getElementsByTagName('path'):
SvgProcessor._apply_attributes(path, {
'fill': 'none',
'stroke': '#0000ff',
'stroke-width': '0.1',
})
def apply_laser_etch_style(self):
# Set fill and stroke for laser etching
for path in self.svg_node.getElementsByTagName('path'):
SvgProcessor._apply_attributes(path, {
'fill': '#000000',
'stroke': 'none',
})
def apply_raster_render_style(self):
# Set fill and stroke for rasterized rendering
for path in self.svg_node.getElementsByTagName('path'):
SvgProcessor._apply_attributes(path, {
'fill': 'none',
'stroke': '#000000',
'stroke-width': '0.2',
})
def apply_dimension_calculation_style(self):
# Remove stroke for calculating overall dimensions in Inkscape (which includes stroke)
for path in self.svg_node.getElementsByTagName('path'):
SvgProcessor._apply_attributes(path, {
'fill': 'none',
'stroke': 'none',
})
def apply_elecrow_style(self):
# Set fill and stroke for Elecrow ordering
for path in self.svg_node.getElementsByTagName('path'):
SvgProcessor._apply_attributes(path, {
'fill': 'none',
'stroke': '#000000',
'stroke-width': '0.1',
})
def import_paths(self, from_svg_processor):
for path in from_svg_processor.svg_node.getElementsByTagName('path'):
output_node = self.dom.importNode(path, True)
self.svg_node.appendChild(output_node)
vb = self.merge_viewbox(from_svg_processor.get_viewbox())
self.set_viewbox(*vb)
dimm = "{:.0f}mm"
self.set_dimensions(dimm.format(vb[2]), dimm.format(vb[3]))
def merge_viewbox(self, vb1):
"""
Takes a new SVG viewbox and combines it with the existing viewbox
to create a new viewbox enclosing both of them.
Returns a tuple with the min-x, min-y, width, and height
"""
def get_max(vb, ax):
return vb[ax] + vb[ax+2] # min + size
vb2 = self.get_viewbox()
mins, maxes = [], []
for xy in range(2):
ax_max = [get_max(vb1, xy), get_max(vb2, xy)]
mins.append(vb1[xy] if vb1[xy] < vb2[xy] else vb2[xy])
maxes.append(ax_max[0] if ax_max[0] > ax_max[1] else ax_max[1])
X, Y = 0, 1
return (mins[X], mins[Y], maxes[X] - mins[X], maxes[Y] - mins[Y])
def remove_redundant_lines(self):
lines_bucketed_by_slope_intersect = defaultdict(list)
paths = self.svg_node.getElementsByTagName('path')
overall_index = 0
for path_index, path in enumerate(paths):
path_text = path.attributes['d'].value
path_obj = parse_path(path_text)
for line_index, line in enumerate(path_obj):
if isinstance(line, Close):
# Treat Close as a Line
line = Line(line.start, line.end)
if isinstance(line, Line):
slope, intersect = _get_slope_intersect(line.start, line.end)
# TODO: float inaccuracy and rounding may cause collinear lines to end up in separate buckets in rare
# cases, so this is not quite correct. Would be better to put lines into *2* nearest buckets in each
# dimension to avoid edge cases.
if slope is not None:
slope = round(slope, ndigits=3)
intersect = round(intersect, ndigits=3)
lines_bucketed_by_slope_intersect[(slope, intersect)].append({
'overall_index': overall_index,
'path_index': path_index,
'line_index': line_index,
'line': line,
})
overall_index += 1
to_remove = {}
to_update = {}
for slope_intersect, lines in lines_bucketed_by_slope_intersect.items():
for i in range(20):
if SvgProcessor._pairwise_overlap_check(lines, to_update, to_remove):
print('Re-running pairwise overlap check because of updated/merged line')
continue
break
else:
raise Exception(
'Exceeded the max number of pairwise overlap check passes. Something is probably wrong.'
)
# Reconstruct the paths, but excluding/updating the lines we just identified
i = 0
removed = 0
removed_length = 0
kept = 0
kept_length = 0
for path_index, path in enumerate(paths):
path_text = path.attributes['d'].value
path_obj = parse_path(path_text)
filtered_path = Path()
for line_index, line in enumerate(path_obj):
if isinstance(line, Close):
# Treat Close as a Line
line = Line(line.start, line.end)
if i in to_remove:
assert isinstance(line, Line)
assert path_index == to_remove[i][0]
assert line_index == to_remove[i][1]
removed += 1
removed_length += line.length()
elif i in to_update:
assert isinstance(line, Line)
assert path_index == to_update[i][0]
assert line_index == to_update[i][1]
replacement_line = to_update[i][2]
filtered_path.append(replacement_line)
kept += 1
kept_length += replacement_line.length()
elif isinstance(line, Line):
filtered_path.append(line)
kept += 1
kept_length += line.length()
else:
print(f'Omitting non-line in reconstructed path at index {line_index}: {line!r}')
i += 1
# Update the path data with the filtered path data
last_line = None
new_path = Path()
for line in filtered_path:
# Newer versions of svg.path keep Moves but don't ensure consistency with line start/ends. We've stripped non-Lines above, so
# we need to add back Moves where Line segments are not connected
if last_line is None or abs(last_line.end.real - line.start.real) > 0.001 or abs(last_line.end.imag - line.start.imag) > 0.001:
new_path.append(Move(line.start))
new_path.append(line)
last_line = line
path.attributes['d'] = new_path.d()
print(f'Optimized line path from\n {path_text!r}\n to\n {path.attributes["d"].value!r}')
print('Removed {} lines ({} length) and kept {} lines ({} length)'.format(
removed,
removed_length,
kept,
kept_length,
))
return [to_remove[k][2] for k in to_remove], [to_update[k][2] for k in to_update]
@staticmethod
def _pairwise_overlap_check(lines, to_update, to_remove):
"""Naive N^2 search for overlapping lines within a slope-intersect bucket.
Adds lines to the to_remove dictionary when they are fully redundant, and adds updated line info to the
to_update dictionary if a line needs to be lengthened to simplify partially overlapping lines.
Returns True if a line update was produced (which means another pass of overlap-checking is required with the
updated line info.
"""
for i in range(len(lines)):
if lines[i]['overall_index'] in to_remove:
continue
line1 = lines[i]['line']
for j in range(i + 1, len(lines)):
if lines[j]['overall_index'] in to_remove:
continue
line2 = lines[j]['line']
if _lines_are_collinear(line1, line2):
# Check for overlap using the min/max x and y values of the lines
l1x1 = min(line1.start.real, line1.end.real)
l1x2 = max(line1.start.real, line1.end.real)
l1y1 = min(line1.start.imag, line1.end.imag)
l1y2 = max(line1.start.imag, line1.end.imag)
l2x1 = min(line2.start.real, line2.end.real)
l2x2 = max(line2.start.real, line2.end.real)
l2y1 = min(line2.start.imag, line2.end.imag)
l2y2 = max(line2.start.imag, line2.end.imag)
if l1x1 <= l2x1 + eps and l1x2 + eps >= l2x2 and l1y1 <= l2y1 + eps and l1y2 + eps >= l2y2:
# Line 1 is bigger, fully contains line 2
assert line1.length() + eps >= line2.length()
to_remove[lines[j]['overall_index']] = (lines[j]['path_index'], lines[j]['line_index'], line2)
elif l1x1 + eps >= l2x1 and l1x2 <= l2x2 + eps and l1y1 + eps >= l2y1 and l1y2 <= l2y2 + eps:
# Line 2 is bigger, fully contains line 1
assert line2.length() + eps >= line1.length()
to_remove[lines[i]['overall_index']] = (lines[i]['path_index'], lines[i]['line_index'], line1)
# Check for partial overlap, i.e. one point of line 2 is between points of line 1 or vice versa
# To avoid cases with 2 line segments end-to-end, we check for point containment with either
# X inclusive OR Y inclusive, but not both (which would mean they share an endpoint and therefore
# must be end-to-end rather than overlapping since we already covered the fully-contained cases
# above)
elif (
(l1x1 <= l2x1 + eps and l2x1 <= l1x2 + eps and l1y1 + eps < l2y1 and l2y1 + eps < l1y2) or
(l1x1 + eps < l2x1 and l2x1 + eps < l1x2 and l1y1 <= l2y1 + eps and l2y1 <= l1y2 + eps) or
(l1x1 <= l2x2 + eps and l2x2 <= l1x2 + eps and l1y1 + eps < l2y2 and l2y2 + eps < l1y2) or
(l1x1 + eps < l2x2 and l2x2 + eps < l1x2 and l1y1 <= l2y2 + eps and l2y2 <= l1y2 + eps)
):
print('Partial overlap of these lines:\n {!r}\n {!r}'.format(line1, line2))
# Arbitrarily pick line1 to remove, and update line2 to cover the full length
to_remove[lines[i]['overall_index']] = (
lines[i]['path_index'],
lines[i]['line_index'],
line1,
)
if lines[i]['overall_index'] in to_update:
# In case we're now deleting a line that was previously updated, remove it from
# to_update to be safe
del to_update[lines[i]['overall_index']]
# To form a line that covers the full length, try all pairs of points and select the pair
# that produces the longest length.
#
# Simply sorting the points as x,y tuples and choosing the first/last wouldn't work because
# of possible floating point error: if the 2 lines have the exact same x coordinate then the
# sort will fall back to sorting on y and work as expected, but if the 2 lines have the
# "same" x coordinate but one is actually a miniscule amount smaller, that x difference will
# take precedence in the sort, potentially resulting in the wrong endpoints being selected.
points = [
line1.start,
line1.end,
line2.start,
line2.end,
]
longest_line = line1
for x in range(len(points)):
for y in range(x + 1, len(points)):
new_line = Line(points[x], points[y])
if new_line.length() > longest_line.length():
longest_line = new_line
# Update the original line's values (needed for subsequent comparisons of collinear lines)
# and log an entry in to_update for the final SVG path generation.
line2.start = longest_line.start
line2.end = longest_line.end
to_update[lines[j]['overall_index']] = (
lines[j]['path_index'],
lines[j]['line_index'],
line2,
)
print(' -- merged into a single line: {!r}'.format(line2))
return True
return False
def add_highlight_lines(self, lines, color):
for line in lines:
new_path_node = self.dom.createElement("path")
new_path_node.setAttribute('d', Path(line).d())
new_path_node.setAttribute('fill', 'none')
new_path_node.setAttribute('stroke', color)
new_path_node.setAttribute('stroke-width', '1')
new_path_node.setAttribute('stroke-opacity', '.45')
self.svg_node.appendChild(new_path_node)
def add_dimensions(self, width_mm, height_mm, mirror=False):
width_node = self.dom.createElement("path")
mirror_dir = -1 if mirror else 1
width_node.setAttribute('d', f'M 0 10 l 0 5 l {mirror_dir * width_mm} 0 l 0 -5')
width_node.setAttribute('fill', 'none')
width_node.setAttribute('stroke', '#ff00ff')
width_node.setAttribute('stroke-width', '1')
self.svg_node.appendChild(width_node)
width_label_node = self.dom.createElement('text')
width_label_node.setAttribute('x', f'{mirror_dir * width_mm / 2}')
width_label_node.setAttribute('y', '25')
width_label_node.setAttribute('style', 'font: 5px sans-serif; fill: #ff00ff; text-anchor: middle;')
width_label_node.appendChild(self.dom.createTextNode(f'{width_mm:.2f} mm'))
self.svg_node.appendChild(width_label_node)
height_node = self.dom.createElement("path")
height_node.setAttribute('d', f'M {-width_mm - 10 if mirror else -10} 0 l -5 0 l 0 -{height_mm} l 5 0')
height_node.setAttribute('fill', 'none')
height_node.setAttribute('stroke', '#ff00ff')
height_node.setAttribute('stroke-width', '1')
self.svg_node.appendChild(height_node)
height_label_node = self.dom.createElement('text')
height_label_node.setAttribute('x', f'{-width_mm - 20 if mirror else -20}')
height_label_node.setAttribute('y', f'{-height_mm / 2}')
height_label_node.setAttribute('style', 'font: 5px sans-serif; fill: #ff00ff; text-anchor: end;')
height_label_node.appendChild(self.dom.createTextNode(f'{height_mm:.2f} mm'))
self.svg_node.appendChild(height_label_node)
def write(self, filename):
with open(filename, 'w') as output_file:
self.svg_node.writexml(output_file)
@staticmethod
def _apply_attributes(node, values):
for (k, v) in values.items():
node.attributes[k] = v
================================================
FILE: 3d/sensor_pcb_dimensions.scad
================================================
/*
Copyright 2018 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
include <m4_dimensions.scad>;
pcb_thickness = 0.8;
// From datasheet:
hall_effect_height = 1.7;
hall_effect_width = 3;
hall_effect_thickness = 1.22;
// From sensor.kicad_pcb:
pcb_height = 20;
pcb_length = 40;
pcb_hole_to_sensor_x = 17.5;
pcb_hole_to_sensor_y = 0;
pcb_hole_to_connector_pin_2_x = 28;
pcb_hole_to_connector_pin_2_y = 10;
pcb_hole_to_bolt_hole_x = 17.5; // 28byj48_mount_center_offset
pcb_hole_to_bolt_hole_y = 8; // 28byj48_shaft_offset
connector_pin_pitch = 2.54;
pcb_edge_to_hole_x = 7;
pcb_edge_to_hole_y = 7;
pcb_hole_radius = 9.4/2; // 28byj48_shaft_collar_radius
pcb_bolt_hole_radius = 4.3/2; // M4
// Rough numbers for 3d rendering only (non-critical dimensions)
pcb_connector_height = 3.2;
pcb_connector_width = 8.2;
pcb_connector_length = 18;
pcb_connector_pin_width = 0.64;
pcb_connector_pin_slop = 0.4;
pcb_connector_pin_tail_length = 3.05 + 2.5/2;
================================================
FILE: 3d/shapes.scad
================================================
/*
Copyright 2020 Scott Bezek and the splitflap contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
module rounded_square(size, center=false, r=0.0, corners=[0,1,2,3], $fn=$fn) {
translation_matrix = [
[-1, -1],
[-1, 1],
[ 1, 1],
[ 1, -1],
];
module add_corner(index, center, r) {
// this is tiny because the geometry is 'hull'ed and we need to get out
// of the way of the rounds at larger radii
square_size = 0.001;
function circle_offset(axis) =
center[axis] * translation_matrix[index][axis];
function square_offset(axis) =
center[axis] * translation_matrix[index][axis] + r * translation_matrix[index][axis] + (translation_matrix[index][axis] >= 1 ? -square_size : 0);
if(len(search(index, corners)) > 0)
translate([circle_offset(0), circle_offset(1)])
circle(r=r, $fn=$fn);
else {
translate([square_offset(0), square_offset(1)])
square(square_size);
}
}
width = size[0] == undef ? size : size[0]; // unpack vector if present
height = size[1] == undef ? size : size[1];
if(r <= 0.0) {
square([width, height], center=center);
} else {
radius = min(min(r, width/2), height/2); // radius cannot be larger than rectangle
center_x = center ? 0 : width/2;
center_y = center ? 0 : height/2;
translate([center_x, center_y]) {
hull() {
x = width/2 - radius;
y = height/2 - radius;
for(i = [0:3])
add_corner(i, [x, y], radius);
}
}
}
}
module triangle(size, center=false) {
width = size[0] == undef ? size : size[0]; // unpack vector if present
height = size[1] == undef ? size : size[1];
pts = [
[ 0, 0],
[width/2, height],
[ width, 0],
];
x_offset = center ? -width/2 : 0;
y_offset = center ? -height/2 : 0;
translate([x_offset, y_offset])
polygon(points=pts, convexity=1);
}
module arrow(size, aspect=[0.5, 0.3], center=false) {
function unpack(val, pos) = val[pos] == undef ? val : val[pos]; // use vector if possible, value otherwise
function recenter(vect, pos) = center ? -vect[pos]/2 : 0; // if 'center', return negative vector position/2
size = [ unpack(size, 0), unpack(size, 1) ]; // overall bounding box
ratio = [ unpack(aspect, 0), unpack(aspect, 1) ]; // ratio between head and body, 1.0 max
head = [ size[0], size[1] * ratio[1] ]; // head bounding box
base = [ size[0] * (1 - ratio[0]), size[1] - head[1] ]; // base bounding box
translate([recenter(size, 0), recenter(size, 1)]) {
translate([(size[0] - base[0]) / 2, 0])
square(base);
translate([0, base[1]])
triangle(head);
}
}
// positioned where origin is at the point to be rounded (first quadrant)
module fillet_tool(r, overlap=0.01, $fn=$fn) {
tool_size = r + overlap;
translate([r, r, 0])
mirror([1, 1, 0])
difference() {
square([tool_size, tool_size]);
circle(r=r, $fn=$fn);
}
}
module fillet_tool_3d(r, r_corner=undef, overlap=0.01, additive=false, $fn=$fn) {
corner_radius = (r_corner == undef) ? r : r_corner; // use fillet radius if corner radius not provided
translate([corner_radius, corner_radius, 0])
rotate([0, 180, 90])
rotate_extrude(angle=90, convexity=10, $fn=$fn)
translate([corner_radius, 0, 0])
rotate([0, 0, additive ? -90 : 90])
fillet_tool(r, overlap=overlap, $fn=$fn);
}
module square_fillet_3d(size, r, r_corner=0.0, overlap=0.01, center=false, additive=false, corners=[0,1,2,3], edges=[0,1,2,3], $fn=$fn) {
function in_list(x, list) = len(search(x, list)) > 0 ? true : false;
width = size[0] == undef ? size : size[0]; // unpack vector if present
length = size[1] == undef ? size : size[1];
max_radius = min(width, length)/2; // largest radius possible along XY
// Both radius and corner radius can be any value between 0 and the shortest
// edge of the rectangle / 2 (maximum radius for full round).
// If radius is zero, no geometry is generated (no radius = no fillet)
// If corner radius is zero, corner rounding geometry is not generated
radius = (r >= max_radius) ? max_radius : r;
// if present, corner radius cannot be less than fillet radius (invalid geometry)
corner_radius_inter = (r_corner != 0 && r_corner < radius) ? radius : r_corner;
// corner radius cannot be greater than the max radius
corner_radius = min(corner_radius_inter, max_radius);
function corner_offset_single(x) = in_list(x % 4, corners) ? corner_radius : -overlap; // calculate the corner offset for a given corner index
function corner_offset(x) = corner_offset_single(x) + corner_offset_single(x + 1); // calculate the corner offset for a given edge
if(radius > 0) {
center_x = center ? -width/2 : 0;
center_y = center ? -length/2 : 0;
translate([center_x, center_y]) {
union() {
// Y Straight, Origin
if(in_list(0, edges)) {
translate([0, length - corner_offset_single(1), 0])
rotate([0, additive ? 180 : 0, 0])
rotate([0, 90, 270])
linear_extrude(height=length - corner_offset(0))
fillet_tool(radius, overlap=overlap, $fn=$fn);
}
// X Straight, At Length
if(in_list(1, edges)) {
translate([width - corner_offset_single(2), length, 0])
rotate([additive ? 180 : 0, 0, 0])
rotate([0, 90, 180])
linear_extrude(height=width - corner_offset(1))
fillet_tool(radius, overlap=overlap, $fn=$fn);
}
// Y Straight, At Width
if(in_list(2, edges)) {
translate([width, corner_offset_single(3), 0])
rotate([0, additive ? 180 : 0, 0])
rotate([0, 90, 90])
linear_extrude(height=length - corner_offset(2))
fillet_tool(radius, overlap=overlap, $fn=$fn);
}
// X Straight, Origin
if(in_list(3, edges)) {
translate([corner_of
gitextract_endh529h/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ ├── 3d.yml
│ ├── electronics.yml
│ ├── electronics_v2.yml
│ ├── js.yml
│ └── pio.yml
├── .gitignore
├── .gitmodules
├── 3d/
│ ├── 28byj-48.scad
│ ├── assert.scad
│ ├── color_util.scad
│ ├── combined_front_panel.scad
│ ├── flap.scad
│ ├── flap_3dp.scad
│ ├── flap_characters.scad
│ ├── flap_dimensions.scad
│ ├── flap_fonts.scad
│ ├── font_generator.scad
│ ├── fonts/
│ │ ├── Epilogue/
│ │ │ └── OFL.txt
│ │ └── roboto/
│ │ └── LICENSE.txt
│ ├── global_constants.scad
│ ├── label.scad
│ ├── m4_dimensions.scad
│ ├── pcb.scad
│ ├── projection_renderer.scad
│ ├── rough7380.scad
│ ├── scripts/
│ │ ├── colored_stl_exporter.py
│ │ ├── dependencies.sh
│ │ ├── generate_2d.py
│ │ ├── generate_3d_print_flaps.py
│ │ ├── generate_combined_front_panel.py
│ │ ├── generate_fonts.py
│ │ ├── generate_gif.py
│ │ ├── generate_snapshot.py
│ │ ├── generate_stl.py
│ │ ├── kerf_presets.py
│ │ ├── openscad.py
│ │ ├── projection_renderer.py
│ │ ├── requirements.txt
│ │ └── svg_processor.py
│ ├── sensor_pcb_dimensions.scad
│ ├── shapes.scad
│ ├── splitflap.scad
│ ├── spool.scad
│ └── tools/
│ ├── classic_pcb_mount.scad
│ ├── connector_case.scad
│ ├── flap_container.scad
│ ├── mounting_bracket.scad
│ ├── punch_jig.scad
│ └── scoring_jig.scad
├── LICENSE.txt
├── README.md
├── __init__.py
├── arduino/
│ └── splitflap/
│ └── MOVED.txt
├── docs/
│ ├── DocumentationIndex.md
│ ├── ElectronicsGuide.md
│ ├── Flaps.md
│ ├── MotorGuide.md
│ ├── OpenSauce2024.md
│ ├── v0/
│ │ ├── Assembly.md
│ │ ├── OrderingComplete.md
│ │ └── OrderingEasy.md
│ └── v2/
│ ├── Assembly.md
│ ├── OrderingComplete.md
│ └── OrderingEasy.md
├── electronics/
│ ├── __init__.py
│ ├── chainlinkBase/
│ │ ├── .gitignore
│ │ ├── chainlinkBase-cache.lib
│ │ ├── chainlinkBase.kibot.yml
│ │ ├── chainlinkBase.kicad_pcb
│ │ ├── chainlinkBase.lib
│ │ ├── chainlinkBase.pro
│ │ ├── chainlinkBase.sch
│ │ ├── fp-lib-table
│ │ ├── powerChannel.sch
│ │ └── sym-lib-table
│ ├── chainlinkBuddyBreadboard/
│ │ ├── chainlinkBuddyBreadboard-cache.lib
│ │ ├── chainlinkBuddyBreadboard.kibot.yml
│ │ ├── chainlinkBuddyBreadboard.kicad_pcb
│ │ ├── chainlinkBuddyBreadboard.pro
│ │ ├── chainlinkBuddyBreadboard.sch
│ │ ├── fp-lib-table
│ │ ├── kikit_panelize.json
│ │ └── sym-lib-table
│ ├── chainlinkBuddyTDisplay/
│ │ ├── chainlinkBuddyTDisplay-cache.lib
│ │ ├── chainlinkBuddyTDisplay.kibot.yml
│ │ ├── chainlinkBuddyTDisplay.kicad_pcb
│ │ ├── chainlinkBuddyTDisplay.pro
│ │ ├── chainlinkBuddyTDisplay.sch
│ │ ├── fp-lib-table
│ │ ├── kikit_panelize.json
│ │ └── sym-lib-table
│ ├── chainlinkDriver/
│ │ ├── .gitignore
│ │ ├── chainlinkDriver-cache.lib
│ │ ├── chainlinkDriver.kibot.yml
│ │ ├── chainlinkDriver.kicad_pcb
│ │ ├── chainlinkDriver.lib
│ │ ├── chainlinkDriver.pro
│ │ ├── chainlinkDriver.sch
│ │ ├── fp-lib-table
│ │ └── sym-lib-table
│ ├── chainlinkDriverTester/
│ │ ├── .gitignore
│ │ ├── chainlinkDriverTester-cache.lib
│ │ ├── chainlinkDriverTester.kibot.yml
│ │ ├── chainlinkDriverTester.kicad_pcb
│ │ ├── chainlinkDriverTester.lib
│ │ ├── chainlinkDriverTester.pro
│ │ ├── chainlinkDriverTester.sch
│ │ ├── fp-lib-table
│ │ └── sym-lib-table
│ ├── lib/
│ │ ├── 54-00131.STEP
│ │ ├── 74HC125.dcm
│ │ ├── 74HC125.lib
│ │ ├── 74HC165-DIP.pretty/
│ │ │ └── 74HC165_DIP-16_W7.62mm.kicad_mod
│ │ ├── 74HC165.lib
│ │ ├── 74HC165_2.dcm
│ │ ├── 74HC165_2.lib
│ │ ├── ArduinoUnoShield.pretty/
│ │ │ └── arduino_uno_shield.kicad_mod
│ │ ├── BK-6013.models/
│ │ │ └── Memory_Protection_Devices_-_BK-6013.step
│ │ ├── BK-6013.pretty/
│ │ │ ├── BK-6013.kicad_mod
│ │ │ └── Memory_Protection_Devices-BK-6013-0-0-0.kicad_mod
│ │ ├── Buck.pretty/
│ │ │ ├── BuckModule.kicad_mod
│ │ │ └── BuckModuleBackSilk.kicad_mod
│ │ ├── BuckModule.dcm
│ │ ├── BuckModule.lib
│ │ ├── CustomPower.dcm
│ │ ├── CustomPower.lib
│ │ ├── CustomSymbols.pretty/
│ │ │ └── PolarityCenterPositive.kicad_mod
│ │ ├── DML3006LFDS.dcm
│ │ ├── DML3006LFDS.lib
│ │ ├── Dummy.pretty/
│ │ │ └── Dummy.kicad_mod
│ │ ├── ESP32.pretty/
│ │ │ ├── D1_32.kicad_mod
│ │ │ ├── T-DISPLAY.kicad_mod
│ │ │ ├── T-DISPLAY_extra_pins.kicad_mod
│ │ │ ├── T-DISPLAY_extra_pins_labeled.kicad_mod
│ │ │ └── T-DISPLAY_extra_pins_labeled_double.kicad_mod
│ │ ├── ESP32Modules.dcm
│ │ ├── ESP32Modules.lib
│ │ ├── GP2S60.pretty/
│ │ │ ├── GP2S60.kicad_mod
│ │ │ └── GP2S60_WITH_MOUNT.kicad_mod
│ │ ├── INA219.pretty/
│ │ │ ├── INA219_LARGE.kicad_mod
│ │ │ └── INA219_SMALL.kicad_mod
│ │ ├── INA219_Breakout.dcm
│ │ ├── INA219_Breakout.lib
│ │ ├── JLCPCB.pretty/
│ │ │ └── AssemblyToolingHole.kicad_mod
│ │ ├── JST_XH_Connectors.pretty/
│ │ │ └── JST_XH_2-5mm_5pin.kicad_mod
│ │ ├── LCD.dcm
│ │ ├── LCD.lib
│ │ ├── LED3mmBetterSilkScreen.pretty/
│ │ │ └── LED_D3.0mm.kicad_mod
│ │ ├── LM339.dcm
│ │ ├── LM339.lib
│ │ ├── LevelShifterModule.dcm
│ │ ├── LevelShifterModule.lib
│ │ ├── LevelShifterModule.pretty/
│ │ │ └── LevelShifterModule.kicad_mod
│ │ ├── MIC5842.dcm
│ │ ├── MIC5842.lib
│ │ ├── Mega2560Shield.dcm
│ │ ├── Mega2560Shield.lib
│ │ ├── Mega2560Shield.pretty/
│ │ │ ├── Mega2560Shield.kicad_mod
│ │ │ └── Mega2560Shield_Modified.kicad_mod
│ │ ├── ModifiedSymbols.pretty/
│ │ │ ├── IDC-Header_2x04_P2.54mm_Vertical.kicad_mod
│ │ │ ├── LED_0603_1608Metric_Silkscreen.kicad_mod
│ │ │ ├── LED_0805_2012Metric_Silkscreen.kicad_mod
│ │ │ ├── LED_WS2812B_PLCC4_5.0x5.0mm_P3.2mm.kicad_mod
│ │ │ ├── PinHeader_1x03_P2.54mm_Vertical_SolderJumper.kicad_mod
│ │ │ ├── PinHeader_1x05_P2.54mm_Vertical_NoSilk.kicad_mod
│ │ │ ├── PinHoles_1x04_P2.54mm_NoSilk.kicad_mod
│ │ │ ├── Pin_Header_Right_Angle_1x03.kicad_mod
│ │ │ ├── SOIC-14_3.9x8.7mm_P1.27mm_silk.kicad_mod
│ │ │ ├── SOIC-16_3.9x9.9mm_P1.27mm_silk.kicad_mod
│ │ │ └── TO-220-3_Vertical.kicad_mod
│ │ ├── MountingHoles.pretty/
│ │ │ ├── M4_mount.kicad_mod
│ │ │ ├── M4_mount_2mm_play.kicad_mod
│ │ │ └── M4_mount_4mm_play.kicad_mod
│ │ ├── NCP45560.dcm
│ │ ├── NCP45560.lib
│ │ ├── PJ-202A.pretty/
│ │ │ └── PJ-202A.kicad_mod
│ │ ├── PinHeaders.pretty/
│ │ │ ├── Pin_Header_Straight_1x03.kicad_mod
│ │ │ ├── Pin_Header_Straight_1x04.kicad_mod
│ │ │ └── Pin_Header_Straight_2x07_Pitch2.54mm_IDC_Shrouded.kicad_mod
│ │ ├── Pogo.models/
│ │ │ ├── pogo1mmPoint.step
│ │ │ └── pogo2mmCup.step
│ │ ├── RS485.pretty/
│ │ │ └── RS485Module.kicad_mod
│ │ ├── RS485Module.dcm
│ │ ├── RS485Module.lib
│ │ ├── Resistor0805ThroughHole.pretty/
│ │ │ └── R_0805_ThroughHole.kicad_mod
│ │ ├── SSD1306.pretty/
│ │ │ └── SSD1306_128x32.kicad_mod
│ │ ├── ST7789.pretty/
│ │ │ ├── ST7789_240x240.kicad_mod
│ │ │ └── ST7789_80x160.kicad_mod
│ │ ├── ScrewTerminals.pretty/
│ │ │ ├── C72334_WJ500V-5.08-3P.kicad_mod
│ │ │ ├── C8465_WJ500V-5.08-2P.kicad_mod
│ │ │ ├── EB147A-02-D.kicad_mod
│ │ │ ├── Generic-5.08-2P.kicad_mod
│ │ │ └── Generic-5.08-3P.kicad_mod
│ │ ├── SolderJumpers.pretty/
│ │ │ └── SolderJumper-2_P1.3mm_Bridged_Pad1.0x1.5mm.kicad_mod
│ │ ├── SwitchESE13.pretty/
│ │ │ └── ESE13V01D.kicad_mod
│ │ ├── TTGO.models/
│ │ │ └── TTGO_TDisplay.step
│ │ ├── ULN2003AModule.dcm
│ │ ├── ULN2003AModule.lib
│ │ ├── ULN2003AModule.pretty/
│ │ │ ├── ULN2003AModule.kicad_mod
│ │ │ ├── ULN2003AModule_No_EN.kicad_mod
│ │ │ └── ULN2003AModule_No_EN_No_FSilk.kicad_mod
│ │ ├── VN7003ALHTR.models/
│ │ │ └── STMicroelectronics_-_VN7003ALHTR.step
│ │ ├── VN7007ALHTR.dcm
│ │ ├── VN7007ALHTR.lib
│ │ ├── VN7007ALHTR.pretty/
│ │ │ └── VN7007ALHTR.kicad_mod
│ │ ├── arduino_uno_shield.dcm
│ │ ├── arduino_uno_shield.lib
│ │ ├── gp2s60.dcm
│ │ ├── gp2s60.lib
│ │ ├── hall_effect.dcm
│ │ ├── hall_effect.lib
│ │ ├── hall_effect.pretty/
│ │ │ ├── hall_effect.kicad_mod
│ │ │ └── hall_effect_wide.kicad_mod
│ │ ├── mount.dcm
│ │ ├── mount.lib
│ │ ├── no_pin.dcm
│ │ ├── no_pin.lib
│ │ ├── oled.dcm
│ │ ├── oled.lib
│ │ ├── pogoPins.pretty/
│ │ │ ├── pogo1mm.kicad_mod
│ │ │ └── pogo2mm.kicad_mod
│ │ ├── screw_terminal_01x03_power_output.dcm
│ │ ├── screw_terminal_01x03_power_output.lib
│ │ ├── sensor_smd_lib.pretty/
│ │ │ ├── SolderJumper-2_P1.3mm_Bridged_Pad1.0x1.5mm.kicad_mod
│ │ │ ├── header_3.kicad_mod
│ │ │ ├── header_3_smd.kicad_mod
│ │ │ └── hole_9.4.kicad_mod
│ │ ├── ws2812b.dcm
│ │ └── ws2812b.lib
│ ├── scripts/
│ │ ├── __init__.py
│ │ ├── config/
│ │ │ ├── eeschema
│ │ │ ├── kikit_panelize_classic.json
│ │ │ ├── kikit_panelize_sensor.json
│ │ │ ├── pcbnew
│ │ │ └── policy.xml
│ │ ├── dependencies.sh
│ │ ├── dependencies_v2.sh
│ │ ├── export_3d.py
│ │ ├── export_jlcpcb.py
│ │ ├── export_schematic.py
│ │ ├── export_util.py
│ │ ├── generate_pdf.py
│ │ ├── generate_svg.py
│ │ ├── pcb_util.py
│ │ └── svg_processor.py
│ ├── sensor/
│ │ ├── fp-lib-table
│ │ ├── sensor-cache.lib
│ │ ├── sensor.kicad_pcb
│ │ ├── sensor.pro
│ │ ├── sensor.sch
│ │ └── sym-lib-table
│ └── sensor_smd/
│ ├── fp-lib-table
│ ├── kikit_panelize.json
│ ├── sensor_smd-panelized.kibot.yml
│ ├── sensor_smd.kibot.yml
│ ├── sensor_smd.kicad_pcb
│ ├── sensor_smd.kicad_prl
│ ├── sensor_smd.kicad_pro
│ └── sensor_smd.kicad_sch
├── firmware/
│ ├── .gitignore
│ ├── buildscript_build_info_macros.py
│ ├── esp32/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── base/
│ │ │ ├── base_config.h
│ │ │ ├── base_supervisor_task.cpp
│ │ │ └── base_supervisor_task.h
│ │ ├── core/
│ │ │ ├── common.h
│ │ │ ├── configuration.cpp
│ │ │ ├── configuration.h
│ │ │ ├── logger.h
│ │ │ ├── recursive_semaphore_guard.h
│ │ │ ├── semaphore_guard.h
│ │ │ ├── splitflap_task.cpp
│ │ │ ├── splitflap_task.h
│ │ │ ├── task.h
│ │ │ ├── uart_stream.cpp
│ │ │ └── uart_stream.h
│ │ ├── proto_gen/
│ │ │ ├── splitflap.pb.c
│ │ │ └── splitflap.pb.h
│ │ ├── splitflap/
│ │ │ ├── crc32.cpp
│ │ │ ├── crc32.h
│ │ │ ├── debug_build_info.cpp
│ │ │ ├── debug_build_info.h
│ │ │ ├── display_layouts.h
│ │ │ ├── display_task.cpp
│ │ │ ├── display_task.h
│ │ │ ├── http_task.cpp
│ │ │ ├── http_task.h
│ │ │ ├── main.cpp
│ │ │ ├── mqtt_task.cpp
│ │ │ ├── mqtt_task.h
│ │ │ ├── secrets.h.example
│ │ │ ├── serial_legacy_json_protocol.cpp
│ │ │ ├── serial_legacy_json_protocol.h
│ │ │ ├── serial_proto_protocol.cpp
│ │ │ ├── serial_proto_protocol.h
│ │ │ ├── serial_protocol.h
│ │ │ ├── serial_task.cpp
│ │ │ └── serial_task.h
│ │ └── tester/
│ │ ├── base64url.cpp
│ │ ├── base64url.h
│ │ ├── firestore.cpp
│ │ ├── firestore.h
│ │ ├── firestore_test_reporter.cpp
│ │ ├── firestore_test_reporter.h
│ │ ├── jwt.cpp
│ │ ├── jwt.h
│ │ ├── main.cpp
│ │ ├── result.h
│ │ ├── secrets.h.example
│ │ ├── tester_task.cpp
│ │ └── tester_task.h
│ ├── include/
│ │ └── README
│ ├── lib/
│ │ ├── README
│ │ └── json11/
│ │ ├── CMakeLists.txt
│ │ ├── LICENSE.txt
│ │ ├── Makefile
│ │ ├── README.md
│ │ ├── json11.cpp
│ │ ├── json11.hpp
│ │ ├── json11.pc.in
│ │ └── test.cpp
│ ├── src/
│ │ ├── Adafruit_INA219.cpp
│ │ ├── Adafruit_INA219.h
│ │ ├── acceleration.h
│ │ ├── basic_io_config.h
│ │ ├── config.h
│ │ ├── generate_acceleration.py
│ │ ├── spi_io_config.h
│ │ ├── splitflap_module.h
│ │ └── splitflap_module_data.h
│ └── test/
│ └── README
├── platformio.ini
├── proto/
│ ├── generate_protobuf.py
│ └── splitflap.proto
├── scripts/
│ ├── annotate_image.sh
│ └── video_thumb/
│ └── generate_thumbnail.sh
├── software/
│ ├── chainlink/
│ │ ├── README.md
│ │ ├── demo.py
│ │ ├── js/
│ │ │ ├── .gitignore
│ │ │ ├── .npmrc
│ │ │ ├── README.md
│ │ │ ├── package.json
│ │ │ └── packages/
│ │ │ ├── example-node-cli/
│ │ │ │ ├── .eslintrc
│ │ │ │ ├── .prettierrc
│ │ │ │ ├── package.json
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ └── tsconfig.json
│ │ │ ├── example-webserial-basic/
│ │ │ │ ├── .eslintrc
│ │ │ │ ├── .gitignore
│ │ │ │ ├── .prettierrc
│ │ │ │ ├── README.md
│ │ │ │ ├── package.json
│ │ │ │ ├── public/
│ │ │ │ │ ├── 3d_viewer/
│ │ │ │ │ │ ├── css/
│ │ │ │ │ │ │ └── style.css
│ │ │ │ │ │ ├── index.html
│ │ │ │ │ │ └── js/
│ │ │ │ │ │ ├── OrbitControls.js
│ │ │ │ │ │ ├── STLLoader.js
│ │ │ │ │ │ ├── WebGL.js
│ │ │ │ │ │ ├── three.js
│ │ │ │ │ │ ├── url-search-params/
│ │ │ │ │ │ │ ├── LICENSE
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ └── viewer.js
│ │ │ │ │ ├── embed.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── robots.txt
│ │ │ │ ├── src/
│ │ │ │ │ ├── App.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── react-app-env.d.ts
│ │ │ │ │ └── util.ts
│ │ │ │ └── tsconfig.json
│ │ │ ├── splitflapjs-core/
│ │ │ │ ├── .eslintrc
│ │ │ │ ├── .prettierrc
│ │ │ │ ├── package.json
│ │ │ │ ├── src/
│ │ │ │ │ ├── cobs.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── util.ts
│ │ │ │ └── tsconfig.json
│ │ │ ├── splitflapjs-node/
│ │ │ │ ├── .eslintrc
│ │ │ │ ├── .prettierrc
│ │ │ │ ├── package.json
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ └── tsconfig.json
│ │ │ ├── splitflapjs-proto/
│ │ │ │ └── package.json
│ │ │ └── splitflapjs-webserial/
│ │ │ ├── .eslintrc
│ │ │ ├── .prettierrc
│ │ │ ├── package.json
│ │ │ ├── src/
│ │ │ │ └── index.ts
│ │ │ └── tsconfig.json
│ │ ├── proto_gen/
│ │ │ ├── nanopb_pb2.py
│ │ │ └── splitflap_pb2.py
│ │ ├── requirements.txt
│ │ └── splitflap_proto.py
│ └── classic/
│ ├── demo.py
│ ├── flap_test.py
│ ├── splitflap.py
│ └── terminal.py
├── thirdparty/
│ ├── README
│ ├── __init__.py
│ ├── panelize.py
│ └── xvfbwrapper/
│ ├── LICENSE.txt
│ ├── __init__.py
│ └── xvfbwrapper.py
└── util/
├── __init__.py
├── app_paths.py
├── file_util.py
├── inkscape.py
└── rev_info.py
SYMBOL INDEX (1013 symbols across 85 files)
FILE: 3d/scripts/colored_stl_exporter.py
class ColoredStlExporter (line 54) | class ColoredStlExporter(object):
method __init__ (line 56) | def __init__(self, input_file, build_folder, openscad_variables = None):
method run (line 65) | def run(self):
method _extract_colors (line 89) | def _extract_colors(self):
method _export_stl (line 124) | def _export_stl(self, color):
method walk_and_mutate_scad_files (line 173) | def walk_and_mutate_scad_files(self, mutate_function, intermediate_sub...
method get_transformed_file_path (line 207) | def get_transformed_file_path(original_path):
method parse_openscad_color (line 212) | def parse_openscad_color(color):
function mkdir_p (line 235) | def mkdir_p(path):
FILE: 3d/scripts/generate_2d.py
function getDimensionSvgContents (line 129) | def getDimensionSvgContents(text, width):
FILE: 3d/scripts/generate_3d_print_flaps.py
function render_flap (line 88) | def render_flap(flap_number):
FILE: 3d/scripts/generate_combined_front_panel.py
function render (line 46) | def render(extra_variables, output_directory):
FILE: 3d/scripts/generate_fonts.py
function render (line 48) | def render(extra_variables, skip_optimize, output_directory, render_rast...
FILE: 3d/scripts/generate_gif.py
function generate_gif (line 32) | def generate_gif(output_folder, delay, filename):
function render_rotation (line 42) | def render_rotation(input_file, output_folder, num_frames, start_frame, ...
function render_flaps (line 62) | def render_flaps(input_file, output_folder, variables):
FILE: 3d/scripts/openscad.py
class OpenSCADException (line 33) | class OpenSCADException(Exception):
method __init__ (line 34) | def __init__(self, message, returncode, stdout=None, stderr=None):
function run (line 44) | def run(
function extract_values (line 126) | def extract_values(stderr):
FILE: 3d/scripts/projection_renderer.py
class Renderer (line 25) | class Renderer(object):
method __init__ (line 26) | def __init__(self, input_file, output_folder, extra_variables=None):
method clean (line 37) | def clean(self):
method _get_variables (line 41) | def _get_variables(self, variables):
method _get_extracted_outputs (line 46) | def _get_extracted_outputs(self):
method _get_component_file (line 64) | def _get_component_file(self, i):
method _render_component (line 67) | def _render_component(self, i, panel_horizontal, panel_vertical):
method render_svgs (line 106) | def render_svgs(self, panelize_quantity):
FILE: 3d/scripts/svg_processor.py
function _get_slope_intersect (line 40) | def _get_slope_intersect(p1, p2):
function _lines_are_collinear (line 51) | def _lines_are_collinear(line1, line2):
class SvgProcessor (line 62) | class SvgProcessor(object):
method __init__ (line 67) | def __init__(self, input_file):
method set_dimensions (line 71) | def set_dimensions(self, width, height):
method set_viewbox (line 75) | def set_viewbox(self, min_x, min_y, width, height):
method get_viewbox (line 79) | def get_viewbox(self):
method apply_laser_cut_style (line 84) | def apply_laser_cut_style(self):
method apply_laser_etch_style (line 93) | def apply_laser_etch_style(self):
method apply_raster_render_style (line 101) | def apply_raster_render_style(self):
method apply_dimension_calculation_style (line 110) | def apply_dimension_calculation_style(self):
method apply_elecrow_style (line 118) | def apply_elecrow_style(self):
method import_paths (line 127) | def import_paths(self, from_svg_processor):
method merge_viewbox (line 138) | def merge_viewbox(self, vb1):
method remove_redundant_lines (line 160) | def remove_redundant_lines(self):
method _pairwise_overlap_check (line 263) | def _pairwise_overlap_check(lines, to_update, to_remove):
method add_highlight_lines (line 361) | def add_highlight_lines(self, lines, color):
method add_dimensions (line 373) | def add_dimensions(self, width_mm, height_mm, mirror=False):
method write (line 403) | def write(self, filename):
method _apply_attributes (line 408) | def _apply_attributes(node, values):
FILE: electronics/scripts/export_3d.py
function _wait_for_pcbnew_idle (line 44) | def _wait_for_pcbnew_idle():
function _zoom_in (line 58) | def _zoom_in():
function _invoke_view_option (line 66) | def _invoke_view_option(index):
function _pcbnew_export_3d (line 87) | def _pcbnew_export_3d(output_file, width, height, transforms):
function export_3d (line 140) | def export_3d(filename, width, height, transforms, raytrace):
FILE: electronics/scripts/export_jlcpcb.py
function export_jlcpcb (line 36) | def export_jlcpcb(pcb, schematic, alt_fields):
FILE: electronics/scripts/export_schematic.py
function eeschema_plot_schematic (line 48) | def eeschema_plot_schematic(output_directory, kicad_4):
function export_schematic (line 99) | def export_schematic(schematic_file, kicad_4):
FILE: electronics/scripts/export_util.py
class PopenContext (line 37) | class PopenContext(subprocess.Popen):
method __enter__ (line 38) | def __enter__(self):
method __exit__ (line 40) | def __exit__(self, type, value, traceback):
function xdotool (line 52) | def xdotool(command):
function wait_for_window (line 55) | def wait_for_window(name, window_regex, additional_commands=None, timeou...
function recorded_xvfb (line 74) | def recorded_xvfb(video_filename, **xvfb_args):
function get_versioned_contents (line 86) | def get_versioned_contents(filename):
function versioned_file (line 105) | def versioned_file(filename):
function patch_config (line 119) | def patch_config(filename, replacements):
FILE: electronics/scripts/generate_pdf.py
function run (line 32) | def run(pcb_file):
function plot_to_directory (line 43) | def plot_to_directory(pcb_file, output_directory, temp_dir):
FILE: electronics/scripts/generate_svg.py
function color_with_alpha (line 32) | def color_with_alpha(base_color, alpha):
function run (line 36) | def run(pcb_file):
function plot_to_directory (line 47) | def plot_to_directory(pcb_file, output_directory, temp_dir):
FILE: electronics/scripts/pcb_util.py
function versioned_board (line 32) | def versioned_board(filename):
function get_plotter (line 44) | def get_plotter(pcb_filename, build_directory):
class Plotter (line 49) | class Plotter(object):
method __init__ (line 50) | def __init__(self, board, build_directory):
method plot (line 64) | def plot(self, layer, plot_format):
method plot_drill (line 74) | def plot_drill(self):
FILE: electronics/scripts/svg_processor.py
class SvgProcessor (line 26) | class SvgProcessor(object):
method __init__ (line 28) | def __init__(self, input_file):
method apply_color_transform (line 32) | def apply_color_transform(self, transform_function):
method apply_group_style_transforms (line 45) | def apply_group_style_transforms(self, transform_dict):
method import_groups (line 49) | def import_groups(self, from_svg_processor):
method write (line 57) | def write(self, filename):
method wrap_with_group (line 61) | def wrap_with_group(self, attrs):
method _apply_transform (line 75) | def _apply_transform(node, values):
FILE: firmware/buildscript_build_info_macros.py
function get_firmware_specifier_build_flag (line 7) | def get_firmware_specifier_build_flag():
function get_build_date_flag (line 14) | def get_build_date_flag():
function get_build_os_flag (line 18) | def get_build_os_flag():
FILE: firmware/esp32/base/base_supervisor_task.h
function class (line 30) | class BaseSupervisorTask : public Task<BaseSupervisorTask> {
FILE: firmware/esp32/core/configuration.cpp
function PB_PersistentConfiguration (line 147) | PB_PersistentConfiguration Configuration::get() {
FILE: firmware/esp32/core/configuration.h
function class (line 27) | class Configuration {
function class (line 49) | class FatGuard {
FILE: firmware/esp32/core/logger.h
function class (line 18) | class Logger {
FILE: firmware/esp32/core/recursive_semaphore_guard.h
function class (line 20) | class RecursiveSemaphoreGuard {
FILE: firmware/esp32/core/semaphore_guard.h
function class (line 20) | class SemaphoreGuard {
FILE: firmware/esp32/core/splitflap_task.cpp
function SplitflapState (line 424) | SplitflapState SplitflapTask::getState() {
FILE: firmware/esp32/core/splitflap_task.h
function SplitflapMode (line 25) | enum class SplitflapMode {
type SplitflapState (line 52) | struct SplitflapState {
type class (line 79) | enum class
type class (line 84) | enum class
type ModuleConfig (line 93) | struct ModuleConfig {
type ModuleConfigs (line 99) | struct ModuleConfigs {
type Command (line 103) | struct Command {
function class (line 123) | class SplitflapTask : public Task<SplitflapTask> {
FILE: firmware/esp32/core/task.h
function TaskHandle_t (line 34) | TaskHandle_t getHandle() {
function begin (line 38) | void begin() {
FILE: firmware/esp32/core/uart_stream.h
function class (line 29) | class UartStream : public Stream {
FILE: firmware/esp32/proto_gen/splitflap.pb.h
type PB_SplitflapState_ModuleState_State (line 13) | typedef enum _PB_SplitflapState_ModuleState_State {
type PB_SupervisorState_State (line 21) | typedef enum _PB_SupervisorState_State {
type PB_SupervisorState_FaultInfo_FaultType (line 30) | typedef enum _PB_SupervisorState_FaultInfo_FaultType {
type PB_SplitflapCommand_ModuleCommand_Action (line 40) | typedef enum _PB_SplitflapCommand_ModuleCommand_Action {
type PB_RequestState (line 50) | typedef struct _PB_RequestState {
type PB_Ack (line 55) | typedef struct _PB_Ack {
type PB_GeneralState_BuildInfo (line 59) | typedef struct _PB_GeneralState_BuildInfo {
type PB_Log (line 65) | typedef struct _PB_Log {
type PB_PersistentConfiguration (line 69) | typedef struct _PB_PersistentConfiguration {
type PB_SplitflapCommand_ModuleCommand (line 76) | typedef struct _PB_SplitflapCommand_ModuleCommand {
type PB_SplitflapConfig_ModuleConfig (line 81) | typedef struct _PB_SplitflapConfig_ModuleConfig {
type PB_SplitflapState_ModuleState (line 87) | typedef struct _PB_SplitflapState_ModuleState {
type PB_SupervisorState_FaultInfo (line 96) | typedef struct _PB_SupervisorState_FaultInfo {
type PB_SupervisorState_PowerChannelState (line 102) | typedef struct _PB_SupervisorState_PowerChannelState {
type PB_GeneralState (line 108) | typedef PB_BYTES_ARRAY_T(80) PB_GeneralState_flap_character_set_t;
type PB_SplitflapCommand (line 118) | typedef struct _PB_SplitflapCommand {
type PB_SplitflapConfig (line 124) | typedef struct _PB_SplitflapConfig {
type PB_SplitflapState (line 129) | typedef struct _PB_SplitflapState {
type PB_SupervisorState (line 136) | typedef struct _PB_SupervisorState {
type PB_FromSplitflap (line 145) | typedef struct _PB_FromSplitflap {
type PB_ToSplitflap (line 156) | typedef struct _PB_ToSplitflap {
FILE: firmware/esp32/splitflap/crc32.cpp
function crc32_for_byte (line 8) | static uint32_t crc32_for_byte(uint32_t r) {
function crc32 (line 14) | void crc32(const void *data, size_t n_bytes, uint32_t* crc) {
FILE: firmware/esp32/splitflap/debug_build_info.cpp
function logDebugBuildInfo (line 5) | void logDebugBuildInfo(Logger& logger) {
FILE: firmware/esp32/splitflap/display_layouts.h
function getLayoutPositionSingleRowZigZag (line 25) | static void getLayoutPositionSingleRowZigZag(const uint8_t module_index,...
function getLayoutPositionDualRowZigZag (line 56) | static void getLayoutPositionDualRowZigZag(const bool flip_first_rows, c...
function getLayoutPosition (line 73) | static void getLayoutPosition(const uint8_t module_index, uint8_t* out_r...
FILE: firmware/esp32/splitflap/display_task.h
function class (line 24) | class DisplayTask : public Task<DisplayTask> {
FILE: firmware/esp32/splitflap/http_task.h
function class (line 28) | class HTTPTask : public Task<HTTPTask> {
FILE: firmware/esp32/splitflap/main.cpp
function setup (line 53) | void setup() {
function loop (line 94) | void loop() {
FILE: firmware/esp32/splitflap/mqtt_task.h
function class (line 30) | class MQTTTask : public Task<MQTTTask> {
FILE: firmware/esp32/splitflap/serial_legacy_json_protocol.h
function class (line 21) | class SerialLegacyJsonProtocol : public SerialProtocol {
FILE: firmware/esp32/splitflap/serial_proto_protocol.h
function class (line 34) | class SerialProtoProtocol : public SerialProtocol {
FILE: firmware/esp32/splitflap/serial_protocol.h
type std (line 27) | typedef std::function<void(uint8_t)> ProtocolChangeCallback;
function class (line 28) | class SerialProtocol : public Logger {
FILE: firmware/esp32/splitflap/serial_task.h
function virtual (line 32) | virtual ~SerialTask() {}
FILE: firmware/esp32/tester/base64url.cpp
function base64url_encode (line 58) | int base64url_encode(const unsigned char *in, unsigned int inlen, char *...
function base64url_decode (line 97) | int base64url_decode(const char *in, unsigned int inlen, unsigned char *...
FILE: firmware/esp32/tester/firestore.cpp
function Json (line 30) | Json Firestore::get(String path, size_t json_capacity=1024) {
function String (line 102) | String Firestore::gen_auto_id() {
function String (line 110) | String Firestore::base_path() {
function String (line 114) | String Firestore::doc_path(String path) {
FILE: firmware/esp32/tester/firestore.h
function class (line 23) | class Firestore {
FILE: firmware/esp32/tester/firestore_test_reporter.h
function class (line 25) | class FirestoreTestReporter {
FILE: firmware/esp32/tester/jwt.cpp
function String (line 11) | String Jwt::get() {
FILE: firmware/esp32/tester/jwt.h
function class (line 5) | class Jwt {
FILE: firmware/esp32/tester/main.cpp
function setup (line 27) | void setup() {
function loop (line 38) | void loop() {
FILE: firmware/esp32/tester/result.h
function class (line 5) | class Result {
FILE: firmware/esp32/tester/tester_task.cpp
function Status (line 95) | Status TesterTask::doMaintenance() {
function Result (line 121) | Result TesterTask::doTestMaintenance() {
function Result (line 139) | Result TesterTask::delayWithMaintenance(uint32_t delay_millis) {
function Status (line 150) | Status TesterTask::waitForBoardInserted() {
function Status (line 174) | Status TesterTask::waitForBoardRemoved() {
function Result (line 196) | Result TesterTask::readSerial() {
function Result (line 236) | Result TesterTask::testLoopbacks() {
function Result (line 247) | Result TesterTask::testLeds() {
function Result (line 291) | Result TesterTask::testPowerPreCheck() {
function Result (line 343) | Result TesterTask::testPower() {
function Result (line 372) | Result TesterTask::testHoming() {
function Result (line 446) | Result TesterTask::testMovements() {
function Status (line 520) | Status TesterTask::runStartupSelfTest() {
function Result (line 625) | Result TesterTask::runTestSuite() {
function Status (line 691) | Status TesterTask::runTestSuitesForever() {
FILE: firmware/esp32/tester/tester_task.h
function class (line 30) | class Status {
function class (line 57) | class TesterTask : public Task<TesterTask> {
FILE: firmware/lib/json11/json11.cpp
type json11 (line 29) | namespace json11 {
type NullStruct (line 44) | struct NullStruct {
function dump (line 53) | static void dump(NullStruct, string &out) {
function dump (line 57) | static void dump(double value, string &out) {
function dump (line 67) | static void dump(int value, string &out) {
function dump (line 73) | static void dump(bool value, string &out) {
function dump (line 77) | static void dump(const string &value, string &out) {
function dump (line 114) | static void dump(const Json::array &values, string &out) {
function dump (line 126) | static void dump(const Json::object &values, string &out) {
class Value (line 149) | class Value : public JsonValue {
method Value (line 153) | explicit Value(const T &value) : m_value(value) {}
method Value (line 154) | explicit Value(T &&value) : m_value(move(value)) {}
method type (line 157) | Json::Type type() const override {
method equals (line 162) | bool equals(const JsonValue * other) const override {
method less (line 165) | bool less(const JsonValue * other) const override {
method dump (line 170) | void dump(string &out) const override { json11::dump(m_value, out); }
class JsonDouble (line 173) | class JsonDouble final : public Value<Json::NUMBER, double> {
method number_value (line 174) | double number_value() const override { return m_value; }
method int_value (line 175) | int int_value() const override { return static_cast<int>(m_value); }
method equals (line 176) | bool equals(const JsonValue * other) const override { return m_value...
method less (line 177) | bool less(const JsonValue * other) const override { return m_value...
method JsonDouble (line 179) | explicit JsonDouble(double value) : Value(value) {}
class JsonInt (line 182) | class JsonInt final : public Value<Json::NUMBER, int> {
method number_value (line 183) | double number_value() const override { return m_value; }
method int_value (line 184) | int int_value() const override { return m_value; }
method equals (line 185) | bool equals(const JsonValue * other) const override { return m_value...
method less (line 186) | bool less(const JsonValue * other) const override { return m_value...
method JsonInt (line 188) | explicit JsonInt(int value) : Value(value) {}
class JsonBoolean (line 191) | class JsonBoolean final : public Value<Json::BOOL, bool> {
method bool_value (line 192) | bool bool_value() const override { return m_value; }
method JsonBoolean (line 194) | explicit JsonBoolean(bool value) : Value(value) {}
class JsonString (line 197) | class JsonString final : public Value<Json::STRING, string> {
method string (line 198) | const string &string_value() const override { return m_value; }
method JsonString (line 200) | explicit JsonString(const string &value) : Value(value) {}
method JsonString (line 201) | explicit JsonString(string &&value) : Value(move(value)) {}
class JsonArray (line 204) | class JsonArray final : public Value<Json::ARRAY, Json::array> {
method JsonArray (line 208) | explicit JsonArray(const Json::array &value) : Value(value) {}
method JsonArray (line 209) | explicit JsonArray(Json::array &&value) : Value(move(value)) {}
class JsonObject (line 212) | class JsonObject final : public Value<Json::OBJECT, Json::object> {
method JsonObject (line 216) | explicit JsonObject(const Json::object &value) : Value(value) {}
method JsonObject (line 217) | explicit JsonObject(Json::object &&value) : Value(move(value)) {}
class JsonNull (line 220) | class JsonNull final : public Value<Json::NUL, NullStruct> {
method JsonNull (line 222) | JsonNull() : Value({}) {}
type Statics (line 228) | struct Statics {
method Statics (line 235) | Statics() {}
function Statics (line 238) | static const Statics & statics() {
method Statics (line 235) | Statics() {}
function Json (line 243) | static const Json & static_null() {
function string (line 274) | const string & Json::string_value() const { return m_ptr...
function Json (line 277) | const Json & Json::operator[] (size_t i) const { return (*m_p...
function Json (line 278) | const Json & Json::operator[] (const string &key) const { return (*m_p...
function string (line 283) | const string & JsonValue::string_value() const...
function Json (line 286) | const Json & JsonValue::operator[] (size_t) const...
function Json (line 287) | const Json & JsonValue::operator[] (const string &) const...
function Json (line 289) | const Json & JsonObject::operator[] (const string &key) const {
function Json (line 293) | const Json & JsonArray::operator[] (size_t i) const {
function string (line 328) | static inline string esc(char c) {
function in_range (line 338) | static inline bool in_range(long x, long lower, long upper) {
type JsonParser (line 347) | struct JsonParser final {
method Json (line 361) | Json fail(string &&msg) {
method T (line 366) | T fail(string &&msg, const T err_ret) {
method consume_whitespace (line 377) | void consume_whitespace() {
method consume_comment (line 386) | bool consume_comment() {
method consume_garbage (line 424) | void consume_garbage() {
method get_next_token (line 442) | char get_next_token() {
method encode_utf8 (line 455) | void encode_utf8(long pt, string & out) {
method string (line 480) | string parse_string() {
method Json (line 573) | Json parse_number() {
method Json (line 629) | Json expect(const string &expected, Json res) {
method Json (line 644) | Json parse_json(int depth) {
function Json (line 732) | Json Json::parse(const string &in, string &err, JsonParse strategy) {
FILE: firmware/lib/json11/json11.hpp
type json11 (line 71) | namespace json11 {
type JsonParse (line 73) | enum JsonParse {
class JsonValue (line 77) | class JsonValue
class Json (line 79) | class Json final {
type Type (line 82) | enum Type {
method Json (line 106) | Json(const T & t) : Json(t.to_json()) {}
method Json (line 113) | Json(const M & m) : Json(object(m.begin(), m.end())) {}
method Json (line 119) | Json(const V & v) : Json(array(v.begin(), v.end())) {}
method Json (line 123) | Json(void *) = delete;
method is_null (line 128) | bool is_null() const { return type() == NUL; }
method is_number (line 129) | bool is_number() const { return type() == NUMBER; }
method is_bool (line 130) | bool is_bool() const { return type() == BOOL; }
method is_string (line 131) | bool is_string() const { return type() == STRING; }
method is_array (line 132) | bool is_array() const { return type() == ARRAY; }
method is_object (line 133) | bool is_object() const { return type() == OBJECT; }
method dump (line 157) | std::string dump() const {
method Json (line 167) | static Json parse(const char * in,
method parse_multi (line 184) | static inline std::vector<Json> parse_multi(
class JsonValue (line 212) | class JsonValue {
FILE: firmware/lib/json11/test.cpp
function JSON11_TEST_CASE (line 62) | JSON11_TEST_CASE(json11_test) {
function parse_from_stdin (line 257) | static void parse_from_stdin() {
function main (line 273) | int main(int argc, char **argv) {
FILE: firmware/src/Adafruit_INA219.h
function class (line 153) | class Adafruit_INA219 {
FILE: firmware/src/acceleration.h
function namespace (line 23) | namespace Acceleration {
FILE: firmware/src/basic_io_config.h
function initialize_modules (line 46) | void initialize_modules() {
function motor_sensor_io (line 58) | inline void motor_sensor_io() {
function initialize_modules (line 91) | void initialize_modules() {
function motor_sensor_io (line 115) | inline void motor_sensor_io() {
FILE: firmware/src/generate_acceleration.py
function get_git_root (line 54) | def get_git_root():
function run (line 63) | def run(output_file_path):
FILE: firmware/src/spi_io_config.h
function reset_latch (line 116) | void reset_latch(spi_transaction_t *trans) {
function latch_registers (line 120) | void latch_registers(spi_transaction_t *trans) {
function initialize_modules (line 134) | inline void initialize_modules() {
function motor_sensor_io (line 234) | inline void motor_sensor_io() {
function chainlink_set_led (line 274) | void chainlink_set_led(uint8_t moduleIndex, bool on) {
function chainlink_loopbackMotorByte (line 285) | static uint8_t chainlink_loopbackMotorByte(uint8_t loopbackIndex) {
function chainlink_loopbackMotorBitMask (line 288) | static uint8_t chainlink_loopbackMotorBitMask(uint8_t loopbackIndex) {
function chainlink_loopbackSensorByte (line 291) | static uint8_t chainlink_loopbackSensorByte(uint8_t loopbackIndex) {
function chainlink_loopbackSensorBitMask (line 294) | static uint8_t chainlink_loopbackSensorBitMask(uint8_t loopbackIndex) {
function chainlink_test_startup_loopback (line 298) | bool chainlink_test_startup_loopback(bool results[NUM_LOOPBACKS]) {
function chainlink_set_loopback (line 313) | void chainlink_set_loopback(uint8_t loop_out_index) {
function chainlink_validate_loopback (line 323) | bool chainlink_validate_loopback(uint8_t loop_out_index, bool results[NU...
function chainlink_test_all_loopbacks (line 341) | bool chainlink_test_all_loopbacks(bool loopback_result[NUM_LOOPBACKS][NU...
FILE: firmware/src/splitflap_module.h
function class (line 58) | class SplitflapModule {
function Disable (line 167) | void SplitflapModule::Disable() {
function Panic (line 172) | void SplitflapModule::Panic(String message) {
function CheckSensor (line 179) | __attribute__((always_inline))
function SetMotor (line 188) | __attribute__((always_inline))
function GetFlapFloor (line 193) | __attribute__((always_inline))
function GetTargetStepForFlapIndex (line 199) | __attribute__((always_inline))
function GoToTargetFlapIndex (line 216) | __attribute__((always_inline))
function GoToFlapIndex (line 234) | __attribute__((always_inline))
function GetCurrentFlapIndex (line 247) | __attribute__((always_inline))
function GetTargetFlapIndex (line 252) | uint8_t SplitflapModule::GetTargetFlapIndex() {
function FindAndRecalibrateHome (line 256) | __attribute__((always_inline))
function Update (line 268) | __attribute__((always_inline))
function ResetErrorCounters (line 412) | void SplitflapModule::ResetErrorCounters() {
function ResetState (line 417) | void SplitflapModule::ResetState() {
function Init (line 430) | void SplitflapModule::Init() {
function GetHomeState (line 434) | bool SplitflapModule::GetHomeState() {
function IncreaseOffset (line 438) | void SplitflapModule::IncreaseOffset(uint8_t flap_tenths) {
function SetOffset (line 444) | void SplitflapModule::SetOffset() {
function GetOffset (line 450) | uint16_t SplitflapModule::GetOffset() {
function RestoreOffset (line 454) | void SplitflapModule::RestoreOffset(uint16_t offset) {
FILE: firmware/src/splitflap_module_data.h
type HomeState (line 21) | enum HomeState {
type State (line 31) | enum State {
FILE: proto/generate_protobuf.py
function run (line 23) | def run():
FILE: software/chainlink/demo.py
function _run (line 17) | def _run():
FILE: software/chainlink/js/packages/example-node-cli/src/index.ts
constant USB_SERIAL_NUMBERS (line 12) | const USB_SERIAL_NUMBERS: Array<string> = []
constant FLAPS (line 15) | const FLAPS = [
type anim (line 144) | type anim = [number, string]
FILE: software/chainlink/js/packages/example-webserial-basic/public/3d_viewer/js/OrbitControls.js
function getAutoRotationAngle (line 287) | function getAutoRotationAngle() {
function getZoomScale (line 293) | function getZoomScale() {
function rotateLeft (line 299) | function rotateLeft( angle ) {
function rotateUp (line 305) | function rotateUp( angle ) {
function dollyIn (line 382) | function dollyIn( dollyScale ) {
function dollyOut (line 403) | function dollyOut( dollyScale ) {
function handleMouseDownRotate (line 428) | function handleMouseDownRotate( event ) {
function handleMouseDownDolly (line 436) | function handleMouseDownDolly( event ) {
function handleMouseDownPan (line 444) | function handleMouseDownPan( event ) {
function handleMouseMoveRotate (line 452) | function handleMouseMoveRotate( event ) {
function handleMouseMoveDolly (line 473) | function handleMouseMoveDolly( event ) {
function handleMouseMovePan (line 497) | function handleMouseMovePan( event ) {
function handleMouseUp (line 513) | function handleMouseUp( event ) {
function handleMouseWheel (line 519) | function handleMouseWheel( event ) {
function handleKeyDown (line 553) | function handleKeyDown( event ) {
function handleTouchStartRotate (line 583) | function handleTouchStartRotate( event ) {
function handleTouchStartDolly (line 591) | function handleTouchStartDolly( event ) {
function handleTouchStartPan (line 604) | function handleTouchStartPan( event ) {
function handleTouchMoveRotate (line 612) | function handleTouchMoveRotate( event ) {
function handleTouchMoveDolly (line 633) | function handleTouchMoveDolly( event ) {
function handleTouchMovePan (line 662) | function handleTouchMovePan( event ) {
function handleTouchEnd (line 678) | function handleTouchEnd( event ) {
function onMouseDown (line 688) | function onMouseDown( event ) {
function onMouseMove (line 732) | function onMouseMove( event ) {
function onMouseUp (line 760) | function onMouseUp( event ) {
function onMouseWheel (line 776) | function onMouseWheel( event ) {
function onKeyDown (line 790) | function onKeyDown( event ) {
function onTouchStart (line 798) | function onTouchStart( event ) {
function onTouchMove (line 848) | function onTouchMove( event ) {
function onTouchEnd (line 892) | function onTouchEnd( event ) {
function onContextMenu (line 904) | function onContextMenu( event ) {
FILE: software/chainlink/js/packages/example-webserial-basic/public/3d_viewer/js/three.js
function hue2rgb (line 416) | function hue2rgb( p, q, t ) {
function handleAlpha (line 457) | function handleAlpha( string ) {
method x (line 864) | get x () {
method x (line 870) | set x ( value ) {
method y (line 877) | get y () {
method y (line 883) | set y ( value ) {
method z (line 890) | get z () {
method z (line 896) | set z ( value ) {
method w (line 903) | get w () {
method w (line 909) | set w ( value ) {
method width (line 1450) | get width() {
method width (line 1456) | set width( value ) {
method height (line 1462) | get height() {
method height (line 1468) | set height( value ) {
method x (line 3316) | get x () {
method x (line 3322) | set x ( value ) {
method y (line 3329) | get y () {
method y (line 3335) | set y ( value ) {
method z (line 3342) | get z () {
method z (line 3348) | set z ( value ) {
method order (line 3355) | get order () {
method order (line 3361) | set order ( value ) {
function interpolate (line 7292) | function interpolate( p0, p1, p2, p3, t, t2, t3 ) {
function ascSort (line 8352) | function ascSort( a, b ) {
function intersectObject (line 8358) | function intersectObject( object, raycaster, intersects, recursive ) {
function onRotationChange (line 8482) | function onRotationChange() {
function onQuaternionChange (line 8488) | function onQuaternionChange() {
function extractFromCache (line 9101) | function extractFromCache ( cache ) {
method count (line 9257) | get count() {
method needsUpdate (line 9263) | set needsUpdate( value ) {
method length (line 9656) | get length () {
method count (line 9662) | get count () {
method needsUpdate (line 9668) | set needsUpdate( value ) {
method length (line 9773) | get length() {
method count (line 9780) | get count() {
function addFace (line 10140) | function addFace( a, b, c, materialIndex ) {
function materialIndexSort (line 10769) | function materialIndexSort( a, b ) {
function setBit (line 10925) | function setBit( value, position, enabled ) {
function getNormalIndex (line 10931) | function getNormalIndex( normal ) {
function getColorIndex (line 10948) | function getColorIndex( color ) {
function getUvIndex (line 10965) | function getUvIndex( uv ) {
method total (line 13970) | get total() { return scope._actions.length; }
method inUse (line 13971) | get inUse() { return scope._nActiveActions; }
method total (line 13974) | get total() { return scope._bindings.length; }
method inUse (line 13975) | get inUse() { return scope._nActiveBindings; }
method total (line 13978) | get total() { return scope._controlInterpolants.length; }
method inUse (line 13979) | get inUse() { return scope._nActiveControlInterpolants; }
method total (line 14330) | get total() { return scope._objects.length; }
method inUse (line 14331) | get inUse() { return this.total - scope.nCachedObjects_; }
method bindingsPerObject (line 14334) | get bindingsPerObject() { return scope._bindings.length; }
function compareTime (line 14690) | function compareTime( i, j ) {
function loadTexture (line 18031) | function loadTexture( path, repeat, offset, wrap, anisotropy ) {
function parseModel (line 18623) | function parseModel( scale ) {
function parseSkin (line 18923) | function parseSkin() {
function parseMorphing (line 18968) | function parseMorphing( scale ) {
function parseAnimations (line 19013) | function parseAnimations() {
function loadImage (line 19724) | function loadImage( url ) {
function parseConstant (line 19760) | function parseConstant( value ) {
function getGeometry (line 19827) | function getGeometry( name ) {
function getMaterial (line 19839) | function getMaterial( name ) {
function loadTexture (line 20100) | function loadTexture( i ) {
function loadTexture (line 20268) | function loadTexture( i ) {
method needsUpdate (line 20436) | get needsUpdate() {
method needsUpdate (line 20442) | set needsUpdate( value ) {
function extractFromCache (line 20584) | function extractFromCache ( cache ) {
method needsUpdate (line 21949) | set needsUpdate( value ) {
function getDataURL (line 22000) | function getDataURL( image ) {
function update (line 22314) | function update() {
function testPoint (line 22405) | function testPoint( point, index ) {
function uvIntersection (line 22794) | function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) {
function checkIntersection (line 22808) | function checkIntersection( object, raycaster, ray, pA, pB, pC, point ) {
function checkBufferGeometryIntersection (line 22840) | function checkBufferGeometryIntersection( object, raycaster, ray, positi...
function getTargetPixelRatio (line 25037) | function getTargetPixelRatio() {
function glClearColor (line 25043) | function glClearColor( r, g, b, a ) {
function setDefaultGLState (line 25055) | function setDefaultGLState() {
function resetGLState (line 25066) | function resetGLState() {
function onContextLost (line 25278) | function onContextLost( event ) {
function onMaterialDispose (line 25289) | function onMaterialDispose( event ) {
function deallocateMaterial (line 25301) | function deallocateMaterial( material ) {
function releaseMaterialProgramReference (line 25310) | function releaseMaterialProgramReference( material ) {
function setupVertexAttributes (line 25618) | function setupVertexAttributes( material, program, geometry, startIndex ) {
function absNumericalSort (line 25783) | function absNumericalSort( a, b ) {
function painterSortStable (line 25789) | function painterSortStable ( a, b ) {
function reversePainterSortStable (line 25811) | function reversePainterSortStable ( a, b ) {
function pushRenderItem (line 25996) | function pushRenderItem( object, geometry, material, z, group ) {
function isObjectViewable (line 26047) | function isObjectViewable( object ) {
function isSpriteViewable (line 26061) | function isSpriteViewable( sprite ) {
function isSphereViewable (line 26071) | function isSphereViewable( sphere ) {
function projectObject (line 26096) | function projectObject( object, camera ) {
function renderObjects (line 26194) | function renderObjects( renderList, camera, fog, overrideMaterial ) {
function initMaterial (line 26232) | function initMaterial( material, fog, object ) {
function setMaterial (line 26377) | function setMaterial( material ) {
function setProgram (line 26404) | function setProgram( camera, fog, material, object ) {
function refreshUniformsCommon (line 26686) | function refreshUniformsCommon ( uniforms, material ) {
function refreshUniformsLine (line 26786) | function refreshUniformsLine ( uniforms, material ) {
function refreshUniformsDash (line 26793) | function refreshUniformsDash ( uniforms, material ) {
function refreshUniformsPoints (line 26801) | function refreshUniformsPoints ( uniforms, material ) {
function refreshUniformsFog (line 26821) | function refreshUniformsFog ( uniforms, fog ) {
function refreshUniformsLambert (line 26838) | function refreshUniformsLambert ( uniforms, material ) {
function refreshUniformsPhong (line 26855) | function refreshUniformsPhong ( uniforms, material ) {
function refreshUniformsStandard (line 26897) | function refreshUniformsStandard ( uniforms, material ) {
function refreshUniformsPhysical (line 26958) | function refreshUniformsPhysical ( uniforms, material ) {
function markUniformsLightsNeedsUpdate (line 26969) | function markUniformsLightsNeedsUpdate ( uniforms, value ) {
function setupShadows (line 26982) | function setupShadows ( lights ) {
function setupLights (line 27002) | function setupLights ( lights, camera ) {
function allocTextureUnit (line 27168) | function allocTextureUnit() {
function paramThreeToGL (line 27420) | function paramThreeToGL ( p ) {
function setMode (line 27639) | function setMode( value ) {
function render (line 27645) | function render( start, count ) {
function renderInstances (line 27655) | function renderInstances( geometry ) {
function resetGlobalState (line 27790) | function resetGlobalState() {
function projectPlanes (line 27803) | function projectPlanes( planes, camera, dstOffset, skipTransform ) {
function setMode (line 27861) | function setMode( value ) {
function setIndex (line 27869) | function setIndex( index ) {
function render (line 27885) | function render( start, count ) {
function renderInstances (line 27895) | function renderInstances( geometry, start, count ) {
function getMaxAnisotropy (line 27987) | function getMaxAnisotropy() {
function getMaxPrecision (line 28007) | function getMaxPrecision( precision ) {
function get (line 28084) | function get( object ) {
function onGeometryDispose (line 28122) | function onGeometryDispose( event ) {
function getAttributeBuffer (line 28167) | function getAttributeBuffer( attribute ) {
function deleteAttribute (line 28179) | function deleteAttribute( attribute ) {
function deleteAttributes (line 28192) | function deleteAttributes( attributes ) {
function removeAttributeBuffer (line 28202) | function removeAttributeBuffer( attribute ) {
function update (line 28315) | function update( object ) {
function updateAttribute (line 28362) | function updateAttribute( attribute, bufferType ) {
function createBuffer (line 28380) | function createBuffer( attributeProperties, data, bufferType ) {
function updateBuffer (line 28393) | function updateBuffer( attributeProperties, data, bufferType ) {
function getAttributeBuffer (line 28420) | function getAttributeBuffer( attribute ) {
function getWireframeAttribute (line 28432) | function getWireframeAttribute( geometry ) {
function checkEdge (line 28496) | function checkEdge( edges, a, b ) {
function getEncodingComponents (line 28537) | function getEncodingComponents( encoding ) {
function getTexelDecodingFunction (line 28562) | function getTexelDecodingFunction( functionName, encoding ) {
function getTexelEncodingFunction (line 28569) | function getTexelEncodingFunction( functionName, encoding ) {
function getToneMappingFunction (line 28576) | function getToneMappingFunction( functionName, toneMapping ) {
function generateExtensions (line 28607) | function generateExtensions( extensions, parameters, rendererExtensions ) {
function generateDefines (line 28622) | function generateDefines( defines ) {
function fetchAttributeLocations (line 28640) | function fetchAttributeLocations( gl, program, identifiers ) {
function filterEmptyLine (line 28661) | function filterEmptyLine( string ) {
function replaceLightNums (line 28667) | function replaceLightNums( string, parameters ) {
function parseIncludes (line 28677) | function parseIncludes( string ) {
function unrollLoops (line 28699) | function unrollLoops( string ) {
function allocateBones (line 29221) | function allocateBones ( object ) {
function getTextureEncodingFromMap (line 29259) | function getTextureEncodingFromMap( map, gammaOverrideLinear ) {
function addLineNumbers (line 29519) | function addLineNumbers( string ) {
function getDepthMaterial (line 29859) | function getDepthMaterial( object, material, isPointLight, lightPosition...
function projectObject (line 29962) | function projectObject( object, camera, shadowCamera ) {
function createTexture (line 30047) | function createTexture( type, target, count ) {
function clampToMaxSize (line 30930) | function clampToMaxSize ( image, maxSize ) {
function isPowerOfTwo (line 30956) | function isPowerOfTwo( image ) {
function makePowerOfTwo (line 30962) | function makePowerOfTwo( image ) {
function textureNeedsPowerOfTwo (line 30983) | function textureNeedsPowerOfTwo( texture ) {
function filterFallback (line 30994) | function filterFallback ( f ) {
function onTextureDispose (line 31008) | function onTextureDispose( event ) {
function onRenderTargetDispose (line 31021) | function onRenderTargetDispose( event ) {
function deallocateTexture (line 31035) | function deallocateTexture( texture ) {
function deallocateRenderTarget (line 31060) | function deallocateRenderTarget( renderTarget ) {
function setTexture2D (line 31104) | function setTexture2D( texture, slot ) {
function setTextureCube (line 31134) | function setTextureCube ( texture, slot ) {
function setTextureCubeDynamic (line 31250) | function setTextureCubeDynamic ( texture, slot ) {
function setTextureParameters (line 31257) | function setTextureParameters ( textureType, texture, isPowerOfTwoImage ) {
function uploadTexture (line 31309) | function uploadTexture( textureProperties, texture, slot ) {
function setupFrameBufferTexture (line 31453) | function setupFrameBufferTexture ( framebuffer, renderTarget, attachment...
function setupRenderBufferStorage (line 31465) | function setupRenderBufferStorage ( renderbuffer, renderTarget ) {
function setupDepthTexture (line 31491) | function setupDepthTexture ( framebuffer, renderTarget ) {
function setupDepthRenderbuffer (line 31521) | function setupDepthRenderbuffer( renderTarget ) {
function setupRenderTarget (line 31562) | function setupRenderTarget( renderTarget ) {
function updateRenderTargetMipmap (line 31631) | function updateRenderTargetMipmap( renderTarget ) {
function init (line 32280) | function init() {
function createProgram (line 32622) | function createProgram ( shader ) {
function init (line 32671) | function init() {
function createProgram (line 32895) | function createProgram () {
function painterSortStable (line 33002) | function painterSortStable ( a, b ) {
function snip (line 33928) | function snip( contour, u, v, w, n, verts ) {
function point_in_segment_2D_colin (line 34082) | function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) {
function intersect_segments_2D (line 34113) | function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2P...
function isPointInsideAngle (line 34286) | function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt...
function removeHoles (line 34329) | function removeHoles( contour, holes ) {
function b2p0 (line 34572) | function b2p0( t, p ) {
function b2p1 (line 34579) | function b2p1( t, p ) {
function b2p2 (line 34585) | function b2p2( t, p ) {
function b3p0 (line 34603) | function b3p0( t, p ) {
function b3p1 (line 34610) | function b3p1( t, p ) {
function b3p2 (line 34617) | function b3p2( t, p ) {
function b3p3 (line 34624) | function b3p3( t, p ) {
function createPaths (line 35120) | function createPaths( text ) {
function createPath (line 35141) | function createPath( c, scale, offset ) {
function extractSubpaths (line 35716) | function extractSubpaths( inActions ) {
function toShapesNoHoles (line 35754) | function toShapesNoHoles( inSubpaths ) {
function isPointInsidePolygon (line 35776) | function isPointInsidePolygon( inPt, inPolygon ) {
function CubicPoly (line 36464) | function CubicPoly() {
function calculateVertexCount (line 36732) | function calculateVertexCount ( w, h, d ) {
function calculateIndexCount (line 36745) | function calculateIndexCount ( w, h, d ) {
function buildPlane (line 36758) | function buildPlane ( u, v, w, udir, vdir, width, height, depth, gridX, ...
function calculateVertexCount (line 37049) | function calculateVertexCount() {
function calculateIndexCount (line 37063) | function calculateIndexCount() {
function generateTorso (line 37077) | function generateTorso() {
function generateCap (line 37177) | function generateCap( top ) {
function sortFunction (line 37400) | function sortFunction( a, b ) {
function scalePt2 (line 37658) | function scalePt2 ( pt, vec, size ) {
function getBevelVec (line 37674) | function getBevelVec( inPt, inPrev, inNext ) {
function buildLidFaces (line 37994) | function buildLidFaces() {
function buildSideFaces (line 38048) | function buildSideFaces() {
function sidewalls (line 38066) | function sidewalls( contour, layeroffset ) {
function v (line 38100) | function v( x, y, z ) {
function f3 (line 38106) | function f3( a, b, c ) {
function f4 (line 38120) | function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourInd...
function calculatePositionOnCurve (line 39290) | function calculatePositionOnCurve( u, p, q, radius, position ) {
function vert (line 39405) | function vert( x, y, z ) {
function initialNormal3 (line 39557) | function initialNormal3() {
function prepare (line 39748) | function prepare( vector ) {
function make (line 39766) | function make( v1, v2, v3 ) {
function subdivide (line 39786) | function subdivide( face, detail ) {
function azimuth (line 39855) | function azimuth( vector ) {
function inclination (line 39864) | function inclination( vector ) {
function correctUV (line 39873) | function correctUV( uv, vector, azimuth ) {
function sortFunction (line 40145) | function sortFunction( a, b ) {
function addLine (line 40656) | function addLine( a, b, hex ) {
function addPoint (line 40663) | function addPoint( id, hex ) {
function setPoint (line 40702) | function setPoint( point, x, y, z ) {
FILE: software/chainlink/js/packages/example-webserial-basic/public/3d_viewer/js/url-search-params/index.js
function invoke (line 53) | function invoke(value) {
function URLSearchParams (line 82) | function URLSearchParams(query) {
function addEach (line 132) | function addEach(value, key) {
function appendTo (line 136) | function appendTo(dict, key, value) {
function decode (line 144) | function decode(str) {
function encode (line 148) | function encode(str) {
function replacer (line 152) | function replacer(match) {
function iterator (line 243) | function iterator(self, callback) {
function append (line 262) | function append(name, value) {
function del (line 267) | function del(name) {
function set (line 272) | function set(name, value) {
FILE: software/chainlink/js/packages/example-webserial-basic/public/3d_viewer/js/viewer.js
function addShadowedLight (line 30) | function addShadowedLight(x, y, z, color, intensity, top, right, bottom,...
FILE: software/chainlink/js/packages/example-webserial-basic/src/App.tsx
constant LEGACY_FLAPS (line 10) | const LEGACY_FLAPS = [
constant FLAP_COLOR_BLOCKS (line 17) | const FLAP_COLOR_BLOCKS: Record<string, string> = {
type Config (line 26) | type Config = NoUndefinedField<PB.ISplitflapConfig>
type LogLine (line 32) | type LogLine = [Date, string]
type LogDisplay (line 33) | type LogDisplay = {
type AppProps (line 45) | type AppProps = object
type SplitflapModuleDisplayProps (line 401) | type SplitflapModuleDisplayProps = {
type CalibrationStep (line 413) | enum CalibrationStep {
type LogsProps (line 598) | type LogsProps = {
FILE: software/chainlink/js/packages/example-webserial-basic/src/util.ts
type NoUndefinedField (line 9) | type NoUndefinedField<T> = {
FILE: software/chainlink/js/packages/splitflapjs-core/src/cobs.ts
function encode (line 3) | function encode(buf: Uint8Array) {
function decode (line 31) | function decode(buf: Uint8Array) {
FILE: software/chainlink/js/packages/splitflapjs-core/src/index.ts
type MessageCallback (line 7) | type MessageCallback = (message: PB.FromSplitflap) => void
type SendBytes (line 8) | type SendBytes = (packet: Uint8Array) => void
type QueueEntry (line 10) | type QueueEntry = {
class SplitflapCore (line 17) | class SplitflapCore {
method constructor (line 44) | constructor(onMessage: MessageCallback, sendBytes: SendBytes) {
method sendConfig (line 50) | public sendConfig(config: PB.SplitflapConfig): void {
method sendModuleCommand (line 58) | private sendModuleCommand(position: number, command: PB.SplitflapComma...
method saveAllOffsets (line 70) | public saveAllOffsets(): void {
method offsetIncrementTenth (line 80) | public offsetIncrementTenth(position: number): void {
method offsetIncrementHalf (line 84) | public offsetIncrementHalf(position: number): void {
method offsetSetToCurrentStep (line 88) | public offsetSetToCurrentStep(position: number): void {
method hardReset (line 95) | protected async hardReset(): Promise<void> {
method onStart (line 99) | protected onStart() {
method onReceivedData (line 105) | protected onReceivedData(data: Uint8Array) {
method enqueueMessage (line 156) | private enqueueMessage(message: PB.ToSplitflap) {
method handleAck (line 178) | private handleAck(nonce: number): void {
method serviceQueue (line 191) | private serviceQueue(): void {
FILE: software/chainlink/js/packages/splitflapjs-core/src/util.ts
function convert2dDualRowZigZagTo1dChainlink (line 47) | function convert2dDualRowZigZagTo1dChainlink<T>(arr: T[][], flipFirstRow...
function convert1dChainlinkTo2dDualRowZigZag (line 67) | function convert1dChainlinkTo2dDualRowZigZag<T>(arr: T[], cols: number, ...
FILE: software/chainlink/js/packages/splitflapjs-node/src/index.ts
class SplitflapNode (line 8) | class SplitflapNode extends SplitflapCore {
method constructor (line 11) | constructor(serialPath: string, onMessage: MessageCallback) {
method hardReset (line 25) | public override async hardReset() {
FILE: software/chainlink/js/packages/splitflapjs-webserial/src/index.ts
class SplitflapWebSerial (line 3) | class SplitflapWebSerial extends SplitflapCore {
method constructor (line 7) | constructor(port: SerialPort, onMessage: MessageCallback) {
method openAndLoop (line 21) | public async openAndLoop() {
method onError (line 56) | private onError(e: unknown) {
FILE: software/chainlink/splitflap_proto.py
class Splitflap (line 35) | class Splitflap(object):
class ForceMovement (line 37) | class ForceMovement(Enum):
method __init__ (line 51) | def __init__(self, serial_instance):
method _read_loop (line 68) | def _read_loop(self):
method _process_frame (line 85) | def _process_frame(self, frame):
method _write_loop (line 136) | def _write_loop(self):
method _enqueue_message (line 170) | def _enqueue_message(self, message):
method get_alphabet (line 193) | def get_alphabet(self):
method set_text (line 196) | def set_text(self, text, force_movement=ForceMovement.NONE):
method set_positions (line 213) | def set_positions(self, positions, force_movement=None):
method start (line 230) | def start(self):
method shutdown (line 236) | def shutdown(self):
method add_handler (line 246) | def add_handler(self, message_type, handler):
method _remove_handler (line 251) | def _remove_handler(self, message_type, handler):
method request_state (line 255) | def request_state(self):
method hard_reset (line 260) | def hard_reset(self):
method get_num_modules (line 267) | def get_num_modules(self):
function splitflap_context (line 273) | def splitflap_context(serial_port, default_logging=True, wait_for_comms=...
function ask_for_serial_port (line 302) | def ask_for_serial_port():
function _run_example (line 320) | def _run_example():
FILE: software/classic/demo.py
function run (line 17) | def run():
FILE: software/classic/flap_test.py
function run (line 7) | def run():
FILE: software/classic/splitflap.py
class Splitflap (line 20) | class Splitflap(object):
method __init__ (line 22) | def __init__(self, serial_instance):
method _loop_for_status (line 33) | def _loop_for_status(self):
method in_character_list (line 79) | def in_character_list(self, letter):
method get_character_list (line 82) | def get_character_list(self):
method set_text (line 85) | def set_text(self, text):
method recalibrate_all (line 96) | def recalibrate_all(self):
method get_text (line 100) | def get_text(self):
method get_status (line 107) | def get_status(self):
method get_num_modules (line 110) | def get_num_modules(self):
method print_status (line 113) | def print_status(self, status=None):
function splitflap (line 131) | def splitflap(serial_port):
function ask_for_serial_port (line 138) | def ask_for_serial_port():
FILE: software/classic/terminal.py
function filter_string (line 5) | def filter_string(string, sf):
function run (line 22) | def run():
FILE: thirdparty/panelize.py
function check_instance (line 40) | def check_instance( inst, cls ):
class KicadObj (line 49) | class KicadObj:
method __init__ (line 51) | def __init__( self, name ):
method clone (line 54) | def clone( self ):
method tag_start (line 57) | def tag_start( self, factory, name ):
method tag_end (line 60) | def tag_end( self, factory, obj ):
method tag_value (line 63) | def tag_value( self, factory, value ):
method is_set (line 66) | def is_set( self ):
method write (line 69) | def write( self, writer ):
method write_contents (line 74) | def write_contents( self, writer ):
class KicadFatObj (line 78) | class KicadFatObj( KicadObj ):
method __init__ (line 80) | def __init__( self, name, object_types ):
method clone (line 94) | def clone( self ):
method clone_obj (line 105) | def clone_obj( self, obj ):
method tag_start (line 120) | def tag_start( self, factory, name ):
method tag_end (line 131) | def tag_end( self, factory, obj ):
method set (line 138) | def set( self, name, value ):
method write_contents (line 145) | def write_contents( self, writer ):
method write_obj (line 148) | def write_obj( self, writer, name, newline=True ):
method write_objects (line 162) | def write_objects( self, writer, keys, newline=True ):
class Int (line 170) | class Int( KicadObj ):
method __init__ (line 172) | def __init__( self, name, value=None ):
method tag_value (line 176) | def tag_value( self, factory, value ):
method clone (line 179) | def clone( self ):
method is_set (line 182) | def is_set( self ):
method write_contents (line 185) | def write_contents( self, writer ):
method __str__ (line 188) | def __str__( self ):
class Float (line 194) | class Float( KicadObj ):
method __init__ (line 196) | def __init__( self, name, value=None ):
method tag_value (line 200) | def tag_value( self, factory, value ):
method clone (line 203) | def clone( self ):
method is_set (line 206) | def is_set( self ):
method write_contents (line 209) | def write_contents( self, writer ):
method __str__ (line 212) | def __str__( self ):
class Text (line 218) | class Text( KicadObj ):
method __init__ (line 220) | def __init__( self, name, value=None ):
method tag_value (line 224) | def tag_value( self, factory, value ):
method clone (line 227) | def clone( self ):
method is_set (line 230) | def is_set( self ):
method write_contents (line 233) | def write_contents( self, writer ):
method __str__ (line 236) | def __str__( self ):
class Bool (line 242) | class Bool( KicadObj ):
method __init__ (line 244) | def __init__( self, name ):
method tag_value (line 248) | def tag_value( self, factory, value ):
method clone (line 256) | def clone( self ):
method is_set (line 261) | def is_set( self ):
method write_contents (line 264) | def write_contents( self, writer ):
method __str__ (line 270) | def __str__( self ):
class YesNo (line 279) | class YesNo( KicadObj ):
method __init__ (line 281) | def __init__( self, name ):
method tag_value (line 285) | def tag_value( self, factory, value ):
method clone (line 293) | def clone( self ):
method is_set (line 298) | def is_set( self ):
method write_contents (line 301) | def write_contents( self, writer ):
method __str__ (line 307) | def __str__( self ):
class Vector (line 316) | class Vector( KicadObj ):
method __init__ (line 318) | def __init__( self, name, x=None, y=None ):
method tag_value (line 323) | def tag_value( self, factory, value ):
method clone (line 331) | def clone( self ):
method is_set (line 334) | def is_set( self ):
method write_contents (line 337) | def write_contents( self, writer ):
method __add__ (line 341) | def __add__( self, v ):
method __sub__ (line 344) | def __sub__( self, v ):
method __str__ (line 347) | def __str__( self ):
method unrot (line 352) | def unrot( self ):
method get_angle (line 355) | def get_angle( self ):
method rotate (line 358) | def rotate( self, angle ):
class VectorAngle (line 379) | class VectorAngle( Vector ):
method __init__ (line 381) | def __init__( self, name, x=None, y=None, a=0 ):
method tag_value (line 385) | def tag_value( self, factory, value ):
method clone (line 397) | def clone( self ):
method write_contents (line 400) | def write_contents( self, writer ):
method __add__ (line 406) | def __add__( self, v ):
method __sub__ (line 410) | def __sub__( self, v ):
method __str__ (line 414) | def __str__( self ):
method unrot (line 421) | def unrot( self ):
method get_angle (line 438) | def get_angle( self ):
class Vector3D (line 442) | class Vector3D( Vector ):
method __init__ (line 444) | def __init__( self, name, x=None, y=None, z=None ):
method tag_value (line 448) | def tag_value( self, factory, value ):
method clone (line 458) | def clone( self ):
method write_contents (line 461) | def write_contents( self, writer ):
method __str__ (line 467) | def __str__( self ):
class Area (line 473) | class Area( KicadObj ):
method __init__ (line 480) | def __init__( self, name, v1=None, v2=None ):
method tag_value (line 491) | def tag_value( self, factory, value ):
method clone (line 498) | def clone( self ):
method is_set (line 506) | def is_set( self ):
method write_contents (line 509) | def write_contents( self, writer ):
method normalize (line 515) | def normalize( self ):
method is_inside (line 522) | def is_inside( self, vect ):
method __add__ (line 528) | def __add__( self, v ):
method __sub__ (line 536) | def __sub__( self, v ):
method __str__ (line 544) | def __str__( self ):
class List (line 548) | class List( KicadObj ):
method __init__ (line 550) | def __init__( self, name ):
method tag_value (line 554) | def tag_value( self, factory, value ):
method clone (line 557) | def clone( self ):
method is_set (line 563) | def is_set( self ):
method write_contents (line 566) | def write_contents( self, writer ):
class Keepout (line 570) | class Keepout( KicadFatObj ):
method __init__ (line 571) | def __init__( self, name ):
method tag_value (line 579) | def tag_value( self, factory, value ):
method write_contents (line 585) | def write_contents( self, writer ):
method is_set (line 594) | def is_set( self ):
class Fill (line 597) | class Fill( KicadFatObj ):
method __init__ (line 599) | def __init__( self, name ):
method tag_value (line 607) | def tag_value( self, factory, value ):
method write_contents (line 613) | def write_contents( self, writer ):
class ConnectPads (line 623) | class ConnectPads( KicadFatObj ):
method __init__ (line 625) | def __init__( self, name ):
method tag_value (line 631) | def tag_value( self, factory, value ):
method write_contents (line 637) | def write_contents( self, writer ):
class Zone (line 645) | class Zone( KicadFatObj ):
method __init__ (line 647) | def __init__( self, name ):
method write_contents (line 663) | def write_contents( self, writer ):
method is_inside (line 680) | def is_inside( self, area ):
method copy (line 687) | def copy( self, trans ):
class Via (line 703) | class Via( KicadFatObj ):
method __init__ (line 705) | def __init__( self, name ):
method is_inside (line 716) | def is_inside( self, area ):
method copy (line 719) | def copy( self, trans ):
class Segment (line 731) | class Segment( KicadFatObj ):
method __init__ (line 733) | def __init__( self, name ):
method is_inside (line 744) | def is_inside( self, area ):
method copy (line 747) | def copy( self, trans ):
class DimLinePoints (line 759) | class DimLinePoints( KicadFatObj ):
method __init__ (line 761) | def __init__( self, name ):
class ListOfPoints (line 769) | class ListOfPoints( KicadObj ):
method __init__ (line 771) | def __init__( self, name ):
method tag_start (line 776) | def tag_start( self, factory, name ):
method tag_end (line 787) | def tag_end( self, factory, obj ):
method write (line 790) | def write( self, writer ):
method is_inside (line 812) | def is_inside( self, area ):
method copy (line 818) | def copy( self, trans ):
class Dimension (line 826) | class Dimension( KicadFatObj ):
method __init__ (line 828) | def __init__( self, name ):
method tag_value (line 845) | def tag_value( self, factory, value ):
method is_inside (line 851) | def is_inside( self, area ):
method write_contents (line 854) | def write_contents( self, writer ):
class GrCircle (line 871) | class GrCircle( KicadFatObj ):
method __init__ (line 873) | def __init__( self, name ):
method is_inside (line 881) | def is_inside( self, area ):
method copy (line 884) | def copy( self, trans ):
class GrArc (line 893) | class GrArc( KicadFatObj ):
method __init__ (line 895) | def __init__( self, name ):
method is_inside (line 905) | def is_inside( self, area ):
method copy (line 908) | def copy( self, trans ):
class GrLine (line 919) | class GrLine( KicadFatObj ):
method __init__ (line 921) | def __init__( self, name ):
method is_inside (line 931) | def is_inside( self, area ):
method copy (line 934) | def copy( self, trans ):
class GrText (line 945) | class GrText( KicadFatObj ):
method __init__ (line 947) | def __init__( self, name ):
method tag_value (line 956) | def tag_value( self, factory, value ):
method write (line 962) | def write( self, writer ):
method is_inside (line 974) | def is_inside( self, area ):
method copy (line 977) | def copy( self, trans ):
class FpArc (line 987) | class FpArc( KicadFatObj ):
method __init__ (line 989) | def __init__( self, name ):
method write (line 998) | def write( self, writer ):
method copy (line 1006) | def copy( self, trans ):
class FpCircle (line 1016) | class FpCircle( KicadFatObj ):
method __init__ (line 1018) | def __init__( self, name ):
method write (line 1026) | def write( self, writer ):
method copy (line 1034) | def copy( self, trans ):
class FpLine (line 1043) | class FpLine( KicadFatObj ):
method __init__ (line 1045) | def __init__( self, name ):
method write (line 1053) | def write( self, writer ):
method copy (line 1061) | def copy( self, trans ):
class FpText (line 1070) | class FpText( KicadFatObj ):
method __init__ (line 1072) | def __init__( self, name ):
method tag_value (line 1082) | def tag_value( self, factory, value ):
method write (line 1092) | def write( self, writer ):
method copy (line 1105) | def copy( self, trans ):
class Font (line 1116) | class Font( KicadFatObj ):
method __init__ (line 1118) | def __init__( self, name ):
method tag_value (line 1126) | def tag_value( self, factory, value ):
method write (line 1132) | def write( self, writer ):
method copy (line 1139) | def copy( self, trans ):
class Justify (line 1147) | class Justify( List ):
method __init__ (line 1149) | def __init__( self, name ):
method clone (line 1152) | def clone( self ):
method copy (line 1158) | def copy( self, trans ):
class Effects (line 1168) | class Effects( KicadFatObj ):
method __init__ (line 1170) | def __init__( self, name ):
method write (line 1176) | def write( self, writer ):
method copy (line 1181) | def copy( self, trans ):
class Drill (line 1188) | class Drill( KicadFatObj ):
method __init__ (line 1190) | def __init__( self, name ):
method tag_value (line 1196) | def tag_value( self, factory, value ):
method write (line 1199) | def write( self, writer ):
method copy (line 1208) | def copy( self, trans ):
class Pad (line 1217) | class Pad( KicadFatObj ):
method __init__ (line 1219) | def __init__( self, name ):
method tag_value (line 1235) | def tag_value( self, factory, value ):
method write (line 1245) | def write( self, writer ):
method copy (line 1271) | def copy( self, trans ):
class NamedVector3D (line 1288) | class NamedVector3D( KicadFatObj ):
method __init__ (line 1290) | def __init__( self, name ):
class Model (line 1296) | class Model( KicadFatObj ):
method __init__ (line 1298) | def __init__( self, name ):
method tag_value (line 1306) | def tag_value( self, factory, value ):
method write (line 1312) | def write( self, writer ):
method copy (line 1317) | def copy( self, trans ):
class Module (line 1324) | class Module( KicadFatObj ):
method __init__ (line 1326) | def __init__( self, name ):
method tag_value (line 1350) | def tag_value( self, factory, value ):
method write (line 1358) | def write( self, writer ):
method is_inside (line 1381) | def is_inside( self, area ):
method copy (line 1408) | def copy( self, trans ):
class NetClass (line 1426) | class NetClass( KicadFatObj ):
method __init__ (line 1428) | def __init__( self, name ):
method tag_value (line 1444) | def tag_value( self, factory, value ):
method write (line 1452) | def write( self, writer ):
class Net (line 1474) | class Net( KicadObj ):
method __init__ (line 1476) | def __init__( self, name, nr=None, netname=None ):
method tag_value (line 1481) | def tag_value( self, factory, value ):
method is_set (line 1489) | def is_set( self ):
method write (line 1492) | def write( self, writer ):
method clone (line 1498) | def clone( self ):
method copy (line 1501) | def copy( self, trans ):
class PcbPlotParams (line 1505) | class PcbPlotParams( KicadFatObj ):
method __init__ (line 1507) | def __init__( self, name ):
method init_defaults (line 1535) | def init_defaults( self ):
method write (line 1561) | def write( self, writer ):
class Setup (line 1578) | class Setup( KicadFatObj ):
method __init__ (line 1580) | def __init__( self, name ):
method init_defaults (line 1617) | def init_defaults( self ):
method write (line 1655) | def write( self, writer ):
class Layer (line 1674) | class Layer( KicadObj ):
method __init__ (line 1676) | def __init__( self, nr, name=None, kind=None, hide=None ):
method tag_value (line 1683) | def tag_value( self, factory, value ):
method write (line 1693) | def write( self, writer ):
class Layers (line 1700) | class Layers( KicadObj ):
method __init__ (line 1702) | def __init__( self, name ):
method clone (line 1708) | def clone( self ):
method tag_start (line 1716) | def tag_start( self, factory, name ):
method tag_end (line 1721) | def tag_end( self, factory, obj ):
method init_defaults (line 1725) | def init_defaults( self ):
method layer_added (line 1753) | def layer_added( self, obj ):
method get_internal_layer_count (line 1759) | def get_internal_layer_count( self ):
method write (line 1762) | def write( self, writer ):
class TitleBlock (line 1775) | class TitleBlock( KicadFatObj ):
method __init__ (line 1777) | def __init__( self, name ):
method append (line 1787) | def append( self, obj ):
method write (line 1791) | def write( self, writer ):
class General (line 1808) | class General( KicadFatObj ):
method __init__ (line 1810) | def __init__( self, name ):
method init_defaults (line 1823) | def init_defaults( self ):
method write (line 1837) | def write( self, writer ):
class NetsList (line 1847) | class NetsList:
method __init__ (line 1849) | def __init__( self ):
method append (line 1854) | def append( self, obj ):
method create (line 1860) | def create( self, name ):
method __iter__ (line 1867) | def __iter__( self ):
class KicadPcb (line 1876) | class KicadPcb( KicadFatObj ):
method __init__ (line 1878) | def __init__( self, name, template=None ):
method finished_loading (line 1922) | def finished_loading( self ):
method get_internal_layer_count (line 1925) | def get_internal_layer_count( self ):
method write (line 1928) | def write( self, writer ):
method copy (line 1955) | def copy( self, trans, dst_pcb ):
class Writer (line 1979) | class Writer:
method __init__ (line 1981) | def __init__( self, filename, compat_index ):
method compat (line 1989) | def compat( self, compat_index ):
method close (line 1992) | def close( self ):
method begin (line 1996) | def begin( self, name ):
method end (line 2004) | def end( self ):
method newline (line 2009) | def newline( self, double=False ):
method do_indent (line 2014) | def do_indent( self ):
method write_int (line 2021) | def write_int( self, value ):
method write_float (line 2024) | def write_float( self, value ):
method write_text (line 2030) | def write_text( self, value ):
function unquote (line 2052) | def unquote( s ):
class ParseError (line 2067) | class ParseError( Exception ):
method __init__ (line 2069) | def __init__( self, msg, factory ):
method __repr__ (line 2074) | def __repr__( self ):
method __str__ (line 2077) | def __str__( self ):
class KicadFactory (line 2081) | class KicadFactory( KicadObj ):
method __init__ (line 2083) | def __init__( self ):
method parse_file (line 2089) | def parse_file( self, filename ):
method parse_print (line 2100) | def parse_print( self, s ):
method parse_data (line 2106) | def parse_data( self, data ):
method parse_error (line 2174) | def parse_error( self, text ):
method tag_start (line 2177) | def tag_start( self, factory, name ):
method tag_end (line 2183) | def tag_end( self, factory, obj ):
method create_pcb (line 2186) | def create_pcb( self ):
class Transformer (line 2198) | class Transformer:
method __init__ (line 2200) | def __init__( self ):
method exclude_layer (line 2223) | def exclude_layer( self, name ):
method include_layer (line 2229) | def include_layer( self, name ):
method include_all_layers (line 2235) | def include_all_layers( self ):
method set_src_pcb (line 2238) | def set_src_pcb( self, pcb ):
method set_dst_pcb (line 2243) | def set_dst_pcb( self, pcb ):
method create_template (line 2246) | def create_template( self ):
method set_src_area (line 2252) | def set_src_area( self, area ):
method set_dst_vector (line 2255) | def set_dst_vector( self, vect ):
method set_rotate (line 2258) | def set_rotate( self, rotate ):
method set_flip (line 2261) | def set_flip( self, flip ):
method set_swap_internal_layers (line 2264) | def set_swap_internal_layers( self, swap_internal_layers ):
method recalculate (line 2267) | def recalculate( self ):
method copy (line 2347) | def copy( self ):
method is_inside (line 2351) | def is_inside( self, vect ):
method rel_vector (line 2354) | def rel_vector( self, vect ):
method vector (line 2366) | def vector( self, vect ):
method add_rotation (line 2378) | def add_rotation( self, vect ):
method add_flip_rot (line 2386) | def add_flip_rot( self, vect ):
method arc_angle (line 2397) | def arc_angle( self, f ):
method accepts_layer (line 2405) | def accepts_layer( self, layer ):
method accepts_layers (line 2417) | def accepts_layers( self, layers ):
method layer (line 2425) | def layer( self, layer ):
method net (line 2452) | def net( self, net ):
class Variable (line 2481) | class Variable:
method __init__ (line 2483) | def __init__( self, value ):
method __str__ (line 2486) | def __str__( self ):
class Main (line 2490) | class Main:
method __init__ (line 2492) | def __init__( self ):
method run (line 2828) | def run( self ):
method print_exc (line 2834) | def print_exc( self ):
method usage (line 2838) | def usage( self ):
method run_file (line 2867) | def run_file( self, filename ):
method split_line (line 2902) | def split_line( self, line, linenr ):
method make_cmd (line 2932) | def make_cmd( self, words, linenr ):
method set_title_block (line 2985) | def set_title_block( self, cmd, args ):
method new (line 3006) | def new( self, cmd, args ):
method load (line 3010) | def load( self, cmd, args ):
method create_template (line 3022) | def create_template( self, cmd, args ):
method save (line 3031) | def save( self, cmd, args ):
method compat (line 3042) | def compat( self, cmd, args ):
method source_area (line 3050) | def source_area( self, cmd, args ):
method clone_nets (line 3064) | def clone_nets( self, cmd, args ):
method exclude_layer (line 3073) | def exclude_layer( self, cmd, args ):
method include_layer (line 3082) | def include_layer( self, cmd, args ):
method swap_internal_layers (line 3095) | def swap_internal_layers( self, cmd, args ):
method copy (line 3104) | def copy( self, cmd, args ):
method draw_line (line 3120) | def draw_line( self, cmd, args ):
method draw_text (line 3139) | def draw_text( self, cmd, args ):
method set_layer (line 3167) | def set_layer( self, cmd, args ):
method set_line_thickness (line 3171) | def set_line_thickness( self, cmd, args ):
method set_text_font (line 3175) | def set_text_font( self, cmd, args ):
FILE: thirdparty/xvfbwrapper/xvfbwrapper.py
class Xvfb (line 18) | class Xvfb:
method __init__ (line 20) | def __init__(self, width=800, height=680, colordepth=24, **kwargs):
method __enter__ (line 42) | def __enter__(self):
method __exit__ (line 46) | def __exit__(self, exc_type, exc_val, exc_tb):
method start (line 49) | def start(self):
method stop (line 66) | def stop(self):
method _get_next_unused_display (line 79) | def _get_next_unused_display(self):
method _set_display_var (line 88) | def _set_display_var(self, display):
method xvfb_exists (line 91) | def xvfb_exists(self):
FILE: util/app_paths.py
function get (line 16) | def get(name):
function _check_path (line 52) | def _check_path(name, path):
FILE: util/file_util.py
function mkdir_p (line 6) | def mkdir_p(path):
FILE: util/inkscape.py
function _version (line 12) | def _version():
function without_gui (line 23) | def without_gui():
function _export_filename_v1 (line 33) | def _export_filename_v1(filename):
function export_png (line 37) | def export_png(filename):
function export_pdf (line 43) | def export_pdf(filename):
FILE: util/rev_info.py
function git_short_rev (line 7) | def git_short_rev():
function current_date (line 18) | def current_date():
function git_date (line 21) | def git_date(short=True):
function git_release_version (line 37) | def git_release_version(search_prefix, fallback=None):
Condensed preview — 408 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (7,049K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 65,
"preview": "# These are supported funding model platforms\n\ngithub: scottbez1\n"
},
{
"path": ".github/workflows/3d.yml",
"chars": 9604,
"preview": "name: Render 3D Designs\n\non:\n push:\n pull_request:\n\njobs:\n render-3d:\n runs-on: ubuntu-20.04\n\n steps:\n - n"
},
{
"path": ".github/workflows/electronics.yml",
"chars": 25925,
"preview": "name: Export Electronics\n\non:\n push:\n pull_request:\n\njobs:\n export-electronics-classic:\n name: Export Electronics "
},
{
"path": ".github/workflows/electronics_v2.yml",
"chars": 4803,
"preview": "name: Export Electronics (v2)\n\non:\n push:\n pull_request:\n\njobs:\n export-electronics-sensor-smd:\n name: Export Elec"
},
{
"path": ".github/workflows/js.yml",
"chars": 1450,
"preview": "name: JS\n\non:\n push:\n pull_request:\n workflow_dispatch:\n\npermissions:\n contents: read\n pages: write\n id-token: wri"
},
{
"path": ".github/workflows/pio.yml",
"chars": 2455,
"preview": "name: PlatformIO CI\n\non:\n push:\n pull_request:\n\njobs:\n pio-build:\n runs-on: ubuntu-20.04\n\n steps:\n - name: C"
},
{
"path": ".gitignore",
"chars": 242,
"preview": "*.swp\n*.pyc\nbuild/\n\n# KiCAD files\n*.000\n*.bak\n*.bck\n*.kicad_pcb-bak\n*.sch-bak\n*.net\n*.dsn\nfp-info-cache\n*-backups\n\\#auto"
},
{
"path": ".gitmodules",
"chars": 98,
"preview": "[submodule \"thirdparty/nanopb\"]\n\tpath = thirdparty/nanopb\n\turl = git@github.com:nanopb/nanopb.git\n"
},
{
"path": "3d/28byj-48.scad",
"chars": 4571,
"preview": "/*\n Copyright 2015-2018 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/assert.scad",
"chars": 1400,
"preview": "/*\n Copyright 2015-2020 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/color_util.scad",
"chars": 931,
"preview": "/*\n Copyright 2015-2021 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/combined_front_panel.scad",
"chars": 6444,
"preview": "/*\n Copyright 2021 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 (the "
},
{
"path": "3d/flap.scad",
"chars": 9586,
"preview": "/*\n Copyright 2015-2021 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/flap_3dp.scad",
"chars": 2010,
"preview": "/*\n Copyright 2025 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 (the "
},
{
"path": "3d/flap_characters.scad",
"chars": 122,
"preview": "\ncharacter_list = \" ABCDEFGHIJKLMNOPQRSTUVWXYZg0123456789r.?-$'#yp,!@&w\";\nfunction get_character_list() = character_list"
},
{
"path": "3d/flap_dimensions.scad",
"chars": 1046,
"preview": "/*\n Copyright 2018 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 (the "
},
{
"path": "3d/flap_fonts.scad",
"chars": 6234,
"preview": "/*\n Copyright 2020-2021 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/font_generator.scad",
"chars": 8754,
"preview": "/*\n Copyright 2020 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 (the "
},
{
"path": "3d/fonts/Epilogue/OFL.txt",
"chars": 4487,
"preview": "Copyright 2020 The Epilogue Project Authors (https://github.com/Etcetera-Type-Co/Epilogue)\r\n\r\nThis Font Software is lice"
},
{
"path": "3d/fonts/roboto/LICENSE.txt",
"chars": 11560,
"preview": "\r\n Apache License\r\n Version 2.0, January 2004\r\n "
},
{
"path": "3d/global_constants.scad",
"chars": 919,
"preview": "/*\n Copyright 2015-2021 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/label.scad",
"chars": 1082,
"preview": "/*\n Copyright 2015-2016 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/m4_dimensions.scad",
"chars": 940,
"preview": "/*\n Copyright 2015-2018 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/pcb.scad",
"chars": 2751,
"preview": "/*\n Copyright 2015-2018 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/projection_renderer.scad",
"chars": 1563,
"preview": "/*\n Copyright 2015-2021 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/rough7380.scad",
"chars": 1832,
"preview": "/*\n Copyright 2018 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 (the "
},
{
"path": "3d/scripts/colored_stl_exporter.py",
"chars": 10570,
"preview": "# Copyright 2015-2016 Scott Bezek and the splitflap contributors\n#\n# Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/scripts/dependencies.sh",
"chars": 265,
"preview": "#!/bin/bash\n\nset -e\n\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" && pwd )\"\n\nset -v\n\nsudo apt-get update -qq\nsudo DEBIA"
},
{
"path": "3d/scripts/generate_2d.py",
"chars": 11240,
"preview": "#!/usr/bin/env python3\n\n# Copyright 2015-2021 Scott Bezek and the splitflap contributors\n#\n# Licensed under the Apac"
},
{
"path": "3d/scripts/generate_3d_print_flaps.py",
"chars": 5035,
"preview": "#!/usr/bin/env python3\n\n# Copyright 2025 Scott Bezek and the splitflap contributors\n#\n# Licensed under the Apache Li"
},
{
"path": "3d/scripts/generate_combined_front_panel.py",
"chars": 6090,
"preview": "#!/usr/bin/env python3\n\n# Copyright 2021 Scott Bezek and the splitflap contributors\n#\n# Licensed under the Apache Li"
},
{
"path": "3d/scripts/generate_fonts.py",
"chars": 6459,
"preview": "#!/usr/bin/env python3\n\n# Copyright 2020 Scott Bezek and the splitflap contributors\n#\n# Licensed under the Apache Li"
},
{
"path": "3d/scripts/generate_gif.py",
"chars": 3601,
"preview": "#!/usr/bin/env python3\n\n# Copyright 2015-2021 Scott Bezek and the splitflap contributors\n#\n# Licensed under the Apac"
},
{
"path": "3d/scripts/generate_snapshot.py",
"chars": 1271,
"preview": "#!/usr/bin/env python\n\n# Copyright 2015-2016 Scott Bezek and the splitflap contributors\n#\n# Licensed under the Apach"
},
{
"path": "3d/scripts/generate_stl.py",
"chars": 1494,
"preview": "#!/usr/bin/env python3\n\n# Copyright 2015-2021 Scott Bezek and the splitflap contributors\n#\n# Licensed under the Apac"
},
{
"path": "3d/scripts/kerf_presets.py",
"chars": 142,
"preview": "\nKERF_PRESETS = {\n 'ponoko-3mm-mdf': 0.18,\n 'ponoko-3mm-acrylic': 0.1,\n 'elecrow-3mm-wood': 0.185,\n 'elecrow"
},
{
"path": "3d/scripts/openscad.py",
"chars": 4853,
"preview": "# Copyright 2015-2020 Scott Bezek and the splitflap contributors\n#\n# Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/scripts/projection_renderer.py",
"chars": 4992,
"preview": "# Copyright 2015-2021 Scott Bezek and the splitflap contributors\n#\n# Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/scripts/requirements.txt",
"chars": 14,
"preview": "svg.path==6.3\n"
},
{
"path": "3d/scripts/svg_processor.py",
"chars": 18838,
"preview": "# Copyright 2015-2016 Scott Bezek and the splitflap contributors\n#\n# Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/sensor_pcb_dimensions.scad",
"chars": 1528,
"preview": "/*\n Copyright 2018 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 (the "
},
{
"path": "3d/shapes.scad",
"chars": 9694,
"preview": "/*\n Copyright 2020 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 (the "
},
{
"path": "3d/splitflap.scad",
"chars": 55194,
"preview": "/*\n Copyright 2015-2021 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/spool.scad",
"chars": 2575,
"preview": "/*\n Copyright 2015-2016 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 "
},
{
"path": "3d/tools/classic_pcb_mount.scad",
"chars": 12390,
"preview": "/*\n Copyright 2021 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 (the "
},
{
"path": "3d/tools/connector_case.scad",
"chars": 3938,
"preview": "/*\n Copyright 2021 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 (the "
},
{
"path": "3d/tools/flap_container.scad",
"chars": 6415,
"preview": "/*\n Copyright 2020 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 (the "
},
{
"path": "3d/tools/mounting_bracket.scad",
"chars": 9561,
"preview": "/*\n Copyright 2021 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 (the "
},
{
"path": "3d/tools/punch_jig.scad",
"chars": 2721,
"preview": "/*\n Copyright 2018 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 (the "
},
{
"path": "3d/tools/scoring_jig.scad",
"chars": 2631,
"preview": "/*\n Copyright 2020 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Version 2.0 (the "
},
{
"path": "LICENSE.txt",
"chars": 11994,
"preview": " splitflap - Copyright 2015-2021 Scott Bezek and the splitflap contributors\n\n Licensed under the Apache License, Ver"
},
{
"path": "README.md",
"chars": 49310,
"preview": "# Split-Flap Display\n\nThis is a DIY ESP32-based [split-flap display](https://en.wikipedia.org/wiki/Split-flap_display), "
},
{
"path": "__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "arduino/splitflap/MOVED.txt",
"chars": 388,
"preview": "As of 2024-10-06, the firmware code that used to live in this directory has been moved to the \"firmware\" directory at th"
},
{
"path": "docs/DocumentationIndex.md",
"chars": 1489,
"preview": "# Splitflap Documentation Index\nThis is the documentation for the open-source splitflap display project: https://github."
},
{
"path": "docs/ElectronicsGuide.md",
"chars": 21297,
"preview": "# Chainlink Driver Electronics User Guide\n[<< Back to Documentation Index](DocumentationIndex.md)\n\n# Intro\n\nIf you’re ju"
},
{
"path": "docs/Flaps.md",
"chars": 14556,
"preview": "[<< Back to Documentation Index](../DocumentationIndex.md)\n\n# About flaps\n\n\nThe"
},
{
"path": "docs/MotorGuide.md",
"chars": 3981,
"preview": "# 28BYJ-48 Motor Buying Guide\n[<< Back to Documentation Index](DocumentationIndex.md)\n\nThere are a *lot* of different mo"
},
{
"path": "docs/OpenSauce2024.md",
"chars": 4171,
"preview": "\n\n\n## Intro\nBuilding a split-flap"
},
{
"path": "docs/v0/OrderingComplete.md",
"chars": 70248,
"preview": "# Splitflap v0 Comprehensive Ordering Guide\n[<< Back to Documentation Index](../DocumentationIndex.md)\n\nThe instructions"
},
{
"path": "docs/v0/OrderingEasy.md",
"chars": 6248,
"preview": "# Splitflap v0 Ordering (the “easy” route)\n[<< Back to Documentation Index](../DocumentationIndex.md)\n\nThis is the “easy"
},
{
"path": "docs/v2/Assembly.md",
"chars": 21961,
"preview": "# Splitflap v2 Assembly Guide\n[<< Back to Documentation Index](../DocumentationIndex.md)\n\n# Electronics Part 1: Sensor P"
},
{
"path": "docs/v2/OrderingComplete.md",
"chars": 37627,
"preview": "# Splitflap v2 Comprehensive Ordering Guide\n[<< Back to Documentation Index](../DocumentationIndex.md)\n\nThe instructions"
},
{
"path": "docs/v2/OrderingEasy.md",
"chars": 7154,
"preview": "# Splitflap v2 Ordering guide\n[<< Back to Documentation Index](../DocumentationIndex.md)\n\nThis is the “easy” way to orde"
},
{
"path": "electronics/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "electronics/chainlinkBase/.gitignore",
"chars": 6,
"preview": "*.xml\n"
},
{
"path": "electronics/chainlinkBase/chainlinkBase-cache.lib",
"chars": 17113,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# Analog_ADC_INA219BxD\n#\nDEF Analog_ADC_INA219BxD U 0 20 Y Y 1 F N\nF0 \"U\""
},
{
"path": "electronics/chainlinkBase/chainlinkBase.kibot.yml",
"chars": 418,
"preview": "kibot:\n version: 1\n\npreflight:\n check_zone_fills: true\n run_drc: true\n run_erc: true\n filters:\n - filter: 'Overl"
},
{
"path": "electronics/chainlinkBase/chainlinkBase.kicad_pcb",
"chars": 620569,
"preview": "(kicad_pcb (version 20171130) (host pcbnew 5.1.10-88a1d61d58~90~ubuntu20.04.1)\n\n (general\n (thickness 1.6)\n (draw"
},
{
"path": "electronics/chainlinkBase/chainlinkBase.lib",
"chars": 26725,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# 74HC165_2_74HC165\n#\nDEF 74HC165_2_74HC165 U 0 40 Y Y 1 F N\nF0 \"U\" 150 7"
},
{
"path": "electronics/chainlinkBase/chainlinkBase.pro",
"chars": 3663,
"preview": "update=Tue 06 Apr 2021 09:21:01 PM PDT\nversion=1\nlast_client=kicad\n[general]\nversion=1\nRootSch=\nBoardNm=\n[cvpcb]\nversion"
},
{
"path": "electronics/chainlinkBase/chainlinkBase.sch",
"chars": 46815,
"preview": "EESchema Schematic File Version 4\nEELAYER 30 0\nEELAYER END\n$Descr USLedger 17000 11000\nencoding utf-8\nSheet 1 6\nTitle \"\""
},
{
"path": "electronics/chainlinkBase/fp-lib-table",
"chars": 806,
"preview": "(fp_lib_table\n (lib (name INA219)(type KiCad)(uri ${KIPRJMOD}/../lib/INA219.pretty)(options \"\")(descr \"\"))\n (lib (name"
},
{
"path": "electronics/chainlinkBase/powerChannel.sch",
"chars": 17078,
"preview": "EESchema Schematic File Version 4\nEELAYER 30 0\nEELAYER END\n$Descr A4 11693 8268\nencoding utf-8\nSheet 3 6\nTitle \"\"\nDate \""
},
{
"path": "electronics/chainlinkBase/sym-lib-table",
"chars": 723,
"preview": "(sym_lib_table\n (lib (name INA219_Breakout)(type Legacy)(uri ${KIPRJMOD}/../lib/INA219_Breakout.lib)(options \"\")(descr "
},
{
"path": "electronics/chainlinkBuddyBreadboard/chainlinkBuddyBreadboard-cache.lib",
"chars": 1824,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# Connector_Conn_01x05_Male\n#\nDEF Connector_Conn_01x05_Male J 0 40 Y N 1 "
},
{
"path": "electronics/chainlinkBuddyBreadboard/chainlinkBuddyBreadboard.kibot.yml",
"chars": 303,
"preview": "kibot:\n version: 1\n\npreflight:\n check_zone_fills: true\n run_drc: true\n run_erc: true\n update_xml: true\n\noutputs:\n "
},
{
"path": "electronics/chainlinkBuddyBreadboard/chainlinkBuddyBreadboard.kicad_pcb",
"chars": 29489,
"preview": "(kicad_pcb (version 20171130) (host pcbnew 5.1.10-88a1d61d58~90~ubuntu20.04.1)\n\n (general\n (thickness 1.6)\n (draw"
},
{
"path": "electronics/chainlinkBuddyBreadboard/chainlinkBuddyBreadboard.pro",
"chars": 3624,
"preview": "update=Thu 28 Oct 2021 09:45:46 PM PDT\nversion=1\nlast_client=kicad\n[general]\nversion=1\nRootSch=\nBoardNm=\n[cvpcb]\nversion"
},
{
"path": "electronics/chainlinkBuddyBreadboard/chainlinkBuddyBreadboard.sch",
"chars": 2262,
"preview": "EESchema Schematic File Version 4\nEELAYER 30 0\nEELAYER END\n$Descr USLetter 11000 8500\nencoding utf-8\nSheet 1 1\nTitle \"Sp"
},
{
"path": "electronics/chainlinkBuddyBreadboard/fp-lib-table",
"chars": 516,
"preview": "(fp_lib_table\n (lib (name ModifiedSymbols)(type KiCad)(uri ${KIPRJMOD}/../lib/ModifiedSymbols.pretty)(options \"\")(descr"
},
{
"path": "electronics/chainlinkBuddyBreadboard/kikit_panelize.json",
"chars": 450,
"preview": "{\n \"layout\": {\n \"type\": \"grid\",\n \"rows\": 5,\n \"cols\": 3,\n \"hspace\": \"0mm\",\n \"vspace"
},
{
"path": "electronics/chainlinkBuddyBreadboard/sym-lib-table",
"chars": 109,
"preview": "(sym_lib_table\n (lib (name no_pin)(type Legacy)(uri ${KIPRJMOD}/../lib/no_pin.lib)(options \"\")(descr \"\"))\n)\n"
},
{
"path": "electronics/chainlinkBuddyTDisplay/chainlinkBuddyTDisplay-cache.lib",
"chars": 7393,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# Connector_Barrel_Jack_Switch\n#\nDEF Connector_Barrel_Jack_Switch J 0 20 "
},
{
"path": "electronics/chainlinkBuddyTDisplay/chainlinkBuddyTDisplay.kibot.yml",
"chars": 302,
"preview": "kibot:\n version: 1\n\npreflight:\n check_zone_fills: true\n run_drc: true\n run_erc: true\n update_xml: true\n\noutputs:\n "
},
{
"path": "electronics/chainlinkBuddyTDisplay/chainlinkBuddyTDisplay.kicad_pcb",
"chars": 108697,
"preview": "(kicad_pcb (version 20171130) (host pcbnew 5.1.10-88a1d61d58~90~ubuntu20.04.1)\n\n (general\n (thickness 1.6)\n (draw"
},
{
"path": "electronics/chainlinkBuddyTDisplay/chainlinkBuddyTDisplay.pro",
"chars": 3624,
"preview": "update=Thu 28 Oct 2021 09:45:46 PM PDT\nversion=1\nlast_client=kicad\n[general]\nversion=1\nRootSch=\nBoardNm=\n[cvpcb]\nversion"
},
{
"path": "electronics/chainlinkBuddyTDisplay/chainlinkBuddyTDisplay.sch",
"chars": 11797,
"preview": "EESchema Schematic File Version 4\nEELAYER 30 0\nEELAYER END\n$Descr USLetter 11000 8500\nencoding utf-8\nSheet 1 1\nTitle \"Sp"
},
{
"path": "electronics/chainlinkBuddyTDisplay/fp-lib-table",
"chars": 516,
"preview": "(fp_lib_table\n (lib (name ModifiedSymbols)(type KiCad)(uri ${KIPRJMOD}/../lib/ModifiedSymbols.pretty)(options \"\")(descr"
},
{
"path": "electronics/chainlinkBuddyTDisplay/kikit_panelize.json",
"chars": 450,
"preview": "{\n \"layout\": {\n \"type\": \"grid\",\n \"rows\": 2,\n \"cols\": 1,\n \"hspace\": \"0mm\",\n \"vspace"
},
{
"path": "electronics/chainlinkBuddyTDisplay/sym-lib-table",
"chars": 213,
"preview": "(sym_lib_table\n (lib (name ESP32Modules)(type Legacy)(uri ${KIPRJMOD}/../lib/ESP32Modules.lib)(options \"\")(descr \"\"))\n "
},
{
"path": "electronics/chainlinkDriver/.gitignore",
"chars": 6,
"preview": "*.xml\n"
},
{
"path": "electronics/chainlinkDriver/chainlinkDriver-cache.lib",
"chars": 9698,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# 74HC125_74HC125\n#\nDEF 74HC125_74HC125 U 0 40 Y Y 1 F N\nF0 \"U\" -300 650 "
},
{
"path": "electronics/chainlinkDriver/chainlinkDriver.kibot.yml",
"chars": 303,
"preview": "kibot:\n version: 1\n\npreflight:\n check_zone_fills: true\n run_drc: true\n run_erc: true\n update_xml: true\n\noutputs:\n "
},
{
"path": "electronics/chainlinkDriver/chainlinkDriver.kicad_pcb",
"chars": 345083,
"preview": "(kicad_pcb (version 20171130) (host pcbnew 5.1.10-88a1d61d58~90~ubuntu20.04.1)\n\n (general\n (thickness 1.6)\n (draw"
},
{
"path": "electronics/chainlinkDriver/chainlinkDriver.lib",
"chars": 26725,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# 74HC165_2_74HC165\n#\nDEF 74HC165_2_74HC165 U 0 40 Y Y 1 F N\nF0 \"U\" 150 7"
},
{
"path": "electronics/chainlinkDriver/chainlinkDriver.pro",
"chars": 4015,
"preview": "update=Wed 03 Feb 2021 05:28:49 PM PST\nversion=1\nlast_client=kicad\n[general]\nversion=1\nRootSch=\nBoardNm=\n[cvpcb]\nversion"
},
{
"path": "electronics/chainlinkDriver/chainlinkDriver.sch",
"chars": 58938,
"preview": "EESchema Schematic File Version 4\nEELAYER 30 0\nEELAYER END\n$Descr USLedger 17000 11000\nencoding utf-8\nSheet 1 1\nTitle \"\""
},
{
"path": "electronics/chainlinkDriver/fp-lib-table",
"chars": 1259,
"preview": "(fp_lib_table\n (lib (name JST_XH_Connectors)(type KiCad)(uri \"$(KIPRJMOD)/../lib/JST_XH_Connectors.pretty\")(options \"\")"
},
{
"path": "electronics/chainlinkDriver/sym-lib-table",
"chars": 1369,
"preview": "(sym_lib_table\n (lib (name Mega2560Shield)(type Legacy)(uri ${KIPRJMOD}/../lib/Mega2560Shield.lib)(options \"\")(descr \"\""
},
{
"path": "electronics/chainlinkDriverTester/.gitignore",
"chars": 6,
"preview": "*.xml\n"
},
{
"path": "electronics/chainlinkDriverTester/chainlinkDriverTester-cache.lib",
"chars": 16337,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# Analog_ADC_INA219BxD\n#\nDEF Analog_ADC_INA219BxD U 0 20 Y Y 1 F N\nF0 \"U\""
},
{
"path": "electronics/chainlinkDriverTester/chainlinkDriverTester.kibot.yml",
"chars": 830,
"preview": "kibot:\n version: 1\n\npreflight:\n check_zone_fills: true\n run_drc: true\n run_erc: true\n filters:\n - filter: 'Overl"
},
{
"path": "electronics/chainlinkDriverTester/chainlinkDriverTester.kicad_pcb",
"chars": 437390,
"preview": "(kicad_pcb (version 20171130) (host pcbnew 5.1.10-88a1d61d58~90~ubuntu20.04.1)\n\n (general\n (thickness 1.6)\n (draw"
},
{
"path": "electronics/chainlinkDriverTester/chainlinkDriverTester.lib",
"chars": 26725,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# 74HC165_2_74HC165\n#\nDEF 74HC165_2_74HC165 U 0 40 Y Y 1 F N\nF0 \"U\" 150 7"
},
{
"path": "electronics/chainlinkDriverTester/chainlinkDriverTester.pro",
"chars": 4183,
"preview": "update=Fri 02 Apr 2021 09:21:45 AM PDT\nversion=1\nlast_client=kicad\n[general]\nversion=1\nRootSch=\nBoardNm=\n[cvpcb]\nversion"
},
{
"path": "electronics/chainlinkDriverTester/chainlinkDriverTester.sch",
"chars": 76413,
"preview": "EESchema Schematic File Version 4\nEELAYER 30 0\nEELAYER END\n$Descr USLedger 17000 11000\nencoding utf-8\nSheet 1 1\nTitle \"\""
},
{
"path": "electronics/chainlinkDriverTester/fp-lib-table",
"chars": 1104,
"preview": "(fp_lib_table\n (lib (name INA219)(type KiCad)(uri ${KIPRJMOD}/../lib/INA219.pretty)(options \"\")(descr \"\"))\n (lib (name"
},
{
"path": "electronics/chainlinkDriverTester/sym-lib-table",
"chars": 415,
"preview": "(sym_lib_table\n (lib (name ESP32Modules)(type Legacy)(uri ${KIPRJMOD}/../lib/ESP32Modules.lib)(options \"\")(descr \"\"))\n "
},
{
"path": "electronics/lib/54-00131.STEP",
"chars": 541504,
"preview": "ISO-10303-21;\r\nHEADER;\r\nFILE_DESCRIPTION (( 'STEP AP203' ),\r\n '1' );\r\nFILE_NAME ('54-00131.STEP',\r\n '2018-09-27T23"
},
{
"path": "electronics/lib/74HC125.dcm",
"chars": 48,
"preview": "EESchema-DOCLIB Version 2.0\n#\n#End Doc Library\n"
},
{
"path": "electronics/lib/74HC125.lib",
"chars": 927,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# 74HC125\n#\nDEF 74HC125 U 0 40 Y Y 1 F N\nF0 \"U\" -300 650 50 H V C CNN\nF1 "
},
{
"path": "electronics/lib/74HC165-DIP.pretty/74HC165_DIP-16_W7.62mm.kicad_mod",
"chars": 2813,
"preview": "(module 74HC165_DIP-16_W7.62mm (layer F.Cu) (tedit 5F7C351F)\n (descr \"16-lead dip package, row spacing 7.62 mm (300 mil"
},
{
"path": "electronics/lib/74HC165.lib",
"chars": 826,
"preview": "EESchema-LIBRARY Version 2.2 Date: 30/09/2017-22:29:58\n#\n# 74HC165\n#\nDEF 74HC165 U 0 40 Y Y 1 0 N\nF0 \"U\" 0 -100 50 H V "
},
{
"path": "electronics/lib/74HC165_2.dcm",
"chars": 48,
"preview": "EESchema-DOCLIB Version 2.0\n#\n#End Doc Library\n"
},
{
"path": "electronics/lib/74HC165_2.lib",
"chars": 827,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# 74HC165\n#\nDEF 74HC165 U 0 40 Y Y 1 F N\nF0 \"U\" 150 700 50 H V C CNN\nF1 \""
},
{
"path": "electronics/lib/ArduinoUnoShield.pretty/arduino_uno_shield.kicad_mod",
"chars": 1694,
"preview": "(module arduino_uno_shield (layer F.Cu) (tedit 5B96F2A4)\n (fp_text reference U1 (at 21.844 -43.18) (layer F.SilkS) hide"
},
{
"path": "electronics/lib/BK-6013.models/Memory_Protection_Devices_-_BK-6013.step",
"chars": 192316,
"preview": "ISO-10303-21;\nHEADER;\nFILE_DESCRIPTION( ( '' ), ' ' );\nFILE_NAME( '/vol/tmp/translate-16711847959468192611/5be15f8d5737e"
},
{
"path": "electronics/lib/BK-6013.pretty/BK-6013.kicad_mod",
"chars": 1401,
"preview": "(module BK-6013 (layer F.Cu) (tedit 605D2E8E)\n (fp_text reference REF** (at 0 -3.81) (layer F.SilkS)\n (effects (font"
},
{
"path": "electronics/lib/BK-6013.pretty/Memory_Protection_Devices-BK-6013-0-0-0.kicad_mod",
"chars": 1648,
"preview": "(module Memory_Protection_Devices-BK-6013-0-0-0 (layer F.Cu) (tedit 605D2BF8)\n (fp_text reference REF** (at -7.25 -3.57"
},
{
"path": "electronics/lib/Buck.pretty/BuckModule.kicad_mod",
"chars": 1947,
"preview": "(module BuckModule (layer F.Cu) (tedit 5FE8C9BE)\n (fp_text reference REF** (at 0 0.5) (layer F.SilkS)\n (effects (fon"
},
{
"path": "electronics/lib/Buck.pretty/BuckModuleBackSilk.kicad_mod",
"chars": 2204,
"preview": "(module BuckModuleBackSilk (layer F.Cu) (tedit 606E666B)\n (fp_text reference REF** (at 0 0.5 180) (layer F.SilkS) hide\n"
},
{
"path": "electronics/lib/BuckModule.dcm",
"chars": 48,
"preview": "EESchema-DOCLIB Version 2.0\n#\n#End Doc Library\n"
},
{
"path": "electronics/lib/BuckModule.lib",
"chars": 402,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# Buck\n#\nDEF Buck U 0 40 Y Y 1 F N\nF0 \"U\" 0 0 50 H V C CNN\nF1 \"Buck\" 0 0 "
},
{
"path": "electronics/lib/CustomPower.dcm",
"chars": 163,
"preview": "EESchema-DOCLIB Version 2.0\n#\n$CMP +12V_Monitored\nD Power flag, +12V\nK power-flag\n$ENDCMP\n#\n$CMP IOVCC\nD Power flag, +1"
},
{
"path": "electronics/lib/CustomPower.lib",
"chars": 636,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# +12V_Monitored\n#\nDEF +12V_Monitored #PWR 0 0 Y Y 1 F P\nF0 \"#PWR\" 0 -150"
},
{
"path": "electronics/lib/CustomSymbols.pretty/PolarityCenterPositive.kicad_mod",
"chars": 1034,
"preview": "(module PolarityCenterPositive (layer F.Cu) (tedit 5B69365B)\n (fp_text reference REF** (at 0 -3.175) (layer F.SilkS) hi"
},
{
"path": "electronics/lib/DML3006LFDS.dcm",
"chars": 48,
"preview": "EESchema-DOCLIB Version 2.0\n#\n#End Doc Library\n"
},
{
"path": "electronics/lib/DML3006LFDS.lib",
"chars": 593,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# DML3006LFDS\n#\nDEF DML3006LFDS U 0 40 Y Y 1 F N\nF0 \"U\" -350 300 50 H V C"
},
{
"path": "electronics/lib/Dummy.pretty/Dummy.kicad_mod",
"chars": 387,
"preview": "(module Dummy (layer F.Cu) (tedit 606FAF91)\n (attr virtual)\n (fp_text reference REF** (at 0 0.5) (layer Cmts.User)\n "
},
{
"path": "electronics/lib/ESP32.pretty/D1_32.kicad_mod",
"chars": 3803,
"preview": "(module D1_32 (layer F.Cu) (tedit 5FB222F8)\n (fp_text reference REF** (at 13.97 1.27) (layer F.SilkS)\n (effects (fon"
},
{
"path": "electronics/lib/ESP32.pretty/T-DISPLAY.kicad_mod",
"chars": 4833,
"preview": "(module T-DISPLAY (layer F.Cu) (tedit 607149BD)\n (fp_text reference REF** (at 11.43 -1.27) (layer F.SilkS)\n (effects"
},
{
"path": "electronics/lib/ESP32.pretty/T-DISPLAY_extra_pins.kicad_mod",
"chars": 7000,
"preview": "(module T-DISPLAY_extra_pins (layer F.Cu) (tedit 61706544)\n (fp_text reference REF** (at 11.43 -1.27) (layer F.SilkS)\n "
},
{
"path": "electronics/lib/ESP32.pretty/T-DISPLAY_extra_pins_labeled.kicad_mod",
"chars": 12896,
"preview": "(module T-DISPLAY_extra_pins_labeled (layer F.Cu) (tedit 617F1475)\n (fp_text reference REF** (at 11.43 -1.27) (layer F."
},
{
"path": "electronics/lib/ESP32.pretty/T-DISPLAY_extra_pins_labeled_double.kicad_mod",
"chars": 20959,
"preview": "(module T-DISPLAY_extra_pins_labeled_double (layer F.Cu) (tedit 617B751D)\n (fp_text reference REF** (at 11.43 -1.27) (l"
},
{
"path": "electronics/lib/ESP32Modules.dcm",
"chars": 48,
"preview": "EESchema-DOCLIB Version 2.0\n#\n#End Doc Library\n"
},
{
"path": "electronics/lib/ESP32Modules.lib",
"chars": 6056,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# D1_32\n#\nDEF D1_32 U 0 40 Y Y 1 F N\nF0 \"U\" 0 1100 50 H V C CNN\nF1 \"D1_32"
},
{
"path": "electronics/lib/GP2S60.pretty/GP2S60.kicad_mod",
"chars": 929,
"preview": "(module GP2S60 (layer F.Cu) (tedit 565C0AFE)\n (fp_text reference REF** (at 0 2) (layer F.SilkS)\n (effects (font (siz"
},
{
"path": "electronics/lib/GP2S60.pretty/GP2S60_WITH_MOUNT.kicad_mod",
"chars": 1832,
"preview": "(module GP2S60_WITH_MOUNT (layer F.Cu) (tedit 59DAA7C0)\n (fp_text reference REF** (at -40.17 1.43 270) (layer F.SilkS)\n"
},
{
"path": "electronics/lib/INA219.pretty/INA219_LARGE.kicad_mod",
"chars": 1497,
"preview": "(module INA219_LARGE (layer F.Cu) (tedit 5FB4C46D)\n (fp_text reference REF** (at 15.24 -2.54) (layer F.SilkS)\n (effe"
},
{
"path": "electronics/lib/INA219.pretty/INA219_SMALL.kicad_mod",
"chars": 1293,
"preview": "(module INA219_SMALL (layer F.Cu) (tedit 5FB4C0FB)\n (fp_text reference REF** (at 13.97 -2.54) (layer F.SilkS)\n (effe"
},
{
"path": "electronics/lib/INA219_Breakout.dcm",
"chars": 48,
"preview": "EESchema-DOCLIB Version 2.0\n#\n#End Doc Library\n"
},
{
"path": "electronics/lib/INA219_Breakout.lib",
"chars": 517,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# INA219_Breakout\n#\nDEF INA219_Breakout U 0 40 Y Y 1 F N\nF0 \"U\" -150 400 "
},
{
"path": "electronics/lib/JLCPCB.pretty/AssemblyToolingHole.kicad_mod",
"chars": 430,
"preview": "(module AssemblyToolingHole (layer F.Cu) (tedit 61803CCC)\n (attr virtual)\n (fp_text reference REF** (at 0 0.5) (layer "
},
{
"path": "electronics/lib/JST_XH_Connectors.pretty/JST_XH_2-5mm_5pin.kicad_mod",
"chars": 1206,
"preview": "(module JST_XH_2-5mm_5pin (layer F.Cu) (tedit 56E4AF35)\n (fp_text reference REF** (at 0 4) (layer F.SilkS)\n (effects"
},
{
"path": "electronics/lib/LCD.dcm",
"chars": 48,
"preview": "EESchema-DOCLIB Version 2.0\n#\n#End Doc Library\n"
},
{
"path": "electronics/lib/LCD.lib",
"chars": 1081,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# ST7789_240x240\n#\nDEF ST7789_240x240 U 0 40 Y Y 1 F N\nF0 \"U\" 0 450 50 H "
},
{
"path": "electronics/lib/LED3mmBetterSilkScreen.pretty/LED_D3.0mm.kicad_mod",
"chars": 1535,
"preview": "(module LED_D3.0mm (layer F.Cu) (tedit 5A074299)\n (descr \"LED, diameter 3.0mm, 2 pins\")\n (tags \"LED diameter 3.0mm 2 p"
},
{
"path": "electronics/lib/LM339.dcm",
"chars": 48,
"preview": "EESchema-DOCLIB Version 2.0\n#\n#End Doc Library\n"
},
{
"path": "electronics/lib/LM339.lib",
"chars": 2041,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# LM339\n#\nDEF LM339 U 0 40 Y Y 1 F N\nF0 \"U\" 300 500 50 H V C CNN\nF1 \"LM33"
},
{
"path": "electronics/lib/LevelShifterModule.dcm",
"chars": 48,
"preview": "EESchema-DOCLIB Version 2.0\n#\n#End Doc Library\n"
},
{
"path": "electronics/lib/LevelShifterModule.lib",
"chars": 713,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# LevelShifterModule\n#\nDEF LevelShifterModule U 0 40 Y Y 1 F N\nF0 \"U\" 0 3"
},
{
"path": "electronics/lib/LevelShifterModule.pretty/LevelShifterModule.kicad_mod",
"chars": 3113,
"preview": "(module LevelShifterModule (layer F.Cu) (tedit 5FE97398)\n (fp_text reference REF** (at 0 0.5) (layer F.SilkS)\n (effe"
},
{
"path": "electronics/lib/MIC5842.dcm",
"chars": 48,
"preview": "EESchema-DOCLIB Version 2.0\n#\n#End Doc Library\n"
},
{
"path": "electronics/lib/MIC5842.lib",
"chars": 916,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# MIC5842\n#\nDEF MIC5842 U 0 40 Y Y 1 F N\nF0 \"U\" 0 -100 50 H V C CNN\nF1 \"M"
},
{
"path": "electronics/lib/Mega2560Shield.dcm",
"chars": 48,
"preview": "EESchema-DOCLIB Version 2.0\n#\n#End Doc Library\n"
},
{
"path": "electronics/lib/Mega2560Shield.lib",
"chars": 3674,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# Mega2560Shield\n#\nDEF Mega2560Shield U 0 40 Y Y 1 F N\nF0 \"U\" 550 50 50 H"
},
{
"path": "electronics/lib/Mega2560Shield.pretty/Mega2560Shield.kicad_mod",
"chars": 11475,
"preview": "(module Mega2560Shield (layer F.Cu) (tedit 5FF9E708)\n (fp_text reference REF** (at 0 0.5) (layer F.SilkS)\n (effects "
},
{
"path": "electronics/lib/Mega2560Shield.pretty/Mega2560Shield_Modified.kicad_mod",
"chars": 11293,
"preview": "(module Mega2560Shield_Modified (layer F.Cu) (tedit 5FFB4B7A)\n (fp_text reference REF** (at 0 0.5) (layer F.SilkS)\n "
},
{
"path": "electronics/lib/ModifiedSymbols.pretty/IDC-Header_2x04_P2.54mm_Vertical.kicad_mod",
"chars": 3773,
"preview": "(module IDC-Header_2x04_P2.54mm_Vertical (layer F.Cu) (tedit 617B5015)\n (descr \"Through hole IDC box header, 2x04, 2.54"
},
{
"path": "electronics/lib/ModifiedSymbols.pretty/LED_0603_1608Metric_Silkscreen.kicad_mod",
"chars": 1986,
"preview": "(module LED_0603_1608Metric_Silkscreen (layer F.Cu) (tedit 6018D469)\n (descr \"LED SMD 0603 (1608 Metric), square (recta"
},
{
"path": "electronics/lib/ModifiedSymbols.pretty/LED_0805_2012Metric_Silkscreen.kicad_mod",
"chars": 2002,
"preview": "(module LED_0805_2012Metric_Silkscreen (layer F.Cu) (tedit 606FFE5F)\n (descr \"LED SMD 0805 (2012 Metric), square (recta"
},
{
"path": "electronics/lib/ModifiedSymbols.pretty/LED_WS2812B_PLCC4_5.0x5.0mm_P3.2mm.kicad_mod",
"chars": 1919,
"preview": "(module LED_WS2812B_PLCC4_5.0x5.0mm_P3.2mm (layer F.Cu) (tedit 60976B4B)\n (descr https://cdn-shop.adafruit.com/datashee"
},
{
"path": "electronics/lib/ModifiedSymbols.pretty/PinHeader_1x03_P2.54mm_Vertical_SolderJumper.kicad_mod",
"chars": 1960,
"preview": "(module PinHeader_1x03_P2.54mm_Vertical_SolderJumper (layer F.Cu) (tedit 60737402)\n (descr \"Through hole straight pin h"
},
{
"path": "electronics/lib/ModifiedSymbols.pretty/PinHeader_1x05_P2.54mm_Vertical_NoSilk.kicad_mod",
"chars": 1831,
"preview": "(module PinHeader_1x05_P2.54mm_Vertical_NoSilk (layer F.Cu) (tedit 617F0396)\n (descr \"Through hole straight pin header,"
},
{
"path": "electronics/lib/ModifiedSymbols.pretty/PinHoles_1x04_P2.54mm_NoSilk.kicad_mod",
"chars": 1269,
"preview": "(module PinHoles_1x04_P2.54mm_NoSilk (layer F.Cu) (tedit 6180B6F8)\n (descr \"Through hole straight socket strip, 1x04, 2"
},
{
"path": "electronics/lib/ModifiedSymbols.pretty/Pin_Header_Right_Angle_1x03.kicad_mod",
"chars": 1088,
"preview": "(module Pin_Header_Right_Angle_1x03 (layer F.Cu) (tedit 61809C8C)\n (descr \"Through hole pin header\")\n (tags \"pin heade"
},
{
"path": "electronics/lib/ModifiedSymbols.pretty/SOIC-14_3.9x8.7mm_P1.27mm_silk.kicad_mod",
"chars": 3096,
"preview": "(module SOIC-14_3.9x8.7mm_P1.27mm_silk (layer F.Cu) (tedit 6018D7BD)\n (descr \"SOIC, 14 Pin (JEDEC MS-012AB, https://www"
},
{
"path": "electronics/lib/ModifiedSymbols.pretty/SOIC-16_3.9x9.9mm_P1.27mm_silk.kicad_mod",
"chars": 3326,
"preview": "(module SOIC-16_3.9x9.9mm_P1.27mm_silk (layer F.Cu) (tedit 6018D7C5)\n (descr \"SOIC, 16 Pin (JEDEC MS-012AC, https://www"
},
{
"path": "electronics/lib/ModifiedSymbols.pretty/TO-220-3_Vertical.kicad_mod",
"chars": 1743,
"preview": "(module TO-220-3_Vertical (layer F.Cu) (tedit 617DDCF3)\n (descr \"TO-220-3, Vertical, RM 2.54mm, see https://www.vishay."
},
{
"path": "electronics/lib/MountingHoles.pretty/M4_mount.kicad_mod",
"chars": 443,
"preview": "(module M4_mount (layer F.Cu) (tedit 566491C2)\n (fp_text reference Z1 (at 0 1.7) (layer F.SilkS) hide\n (effects (fon"
},
{
"path": "electronics/lib/MountingHoles.pretty/M4_mount_2mm_play.kicad_mod",
"chars": 674,
"preview": "(module M4_mount_2mm_play (layer F.Cu) (tedit 56C6E222)\n (fp_text reference Z2 (at 0 1.7) (layer F.SilkS) hide\n (eff"
},
{
"path": "electronics/lib/MountingHoles.pretty/M4_mount_4mm_play.kicad_mod",
"chars": 674,
"preview": "(module M4_mount_4mm_play (layer F.Cu) (tedit 56C6D8E8)\n (fp_text reference Z2 (at 0 1.7) (layer F.SilkS) hide\n (eff"
},
{
"path": "electronics/lib/NCP45560.dcm",
"chars": 48,
"preview": "EESchema-DOCLIB Version 2.0\n#\n#End Doc Library\n"
},
{
"path": "electronics/lib/NCP45560.lib",
"chars": 732,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# NCP45560\n#\nDEF NCP45560 U 0 40 Y Y 1 F N\nF0 \"U\" -350 300 50 H V C CNN\nF"
},
{
"path": "electronics/lib/PJ-202A.pretty/PJ-202A.kicad_mod",
"chars": 1239,
"preview": "(module PJ-202A (layer F.Cu) (tedit 617CDF26)\n (descr \"CONN POWER JACK 2.1MM PCB\")\n (tags \"CONN POWER JACK 2.1MM PCB B"
},
{
"path": "electronics/lib/PinHeaders.pretty/Pin_Header_Straight_1x03.kicad_mod",
"chars": 1044,
"preview": "(module Pin_Header_Straight_1x03 (layer F.Cu) (tedit 5663AD12)\n (descr \"Through hole pin header\")\n (tags \"pin header\")"
},
{
"path": "electronics/lib/PinHeaders.pretty/Pin_Header_Straight_1x04.kicad_mod",
"chars": 1150,
"preview": "(module Pin_Headers:Pin_Header_Straight_1x04 (layer F.Cu) (tedit 56635161)\n (descr \"Through hole pin header\")\n (tags \""
},
{
"path": "electronics/lib/PinHeaders.pretty/Pin_Header_Straight_2x07_Pitch2.54mm_IDC_Shrouded.kicad_mod",
"chars": 3743,
"preview": "(module Pin_Header_Straight_2x07_Pitch2.54mm_IDC_Shrouded (layer F.Cu) (tedit 59E2F95E)\n (descr \"Through hole straight "
},
{
"path": "electronics/lib/Pogo.models/pogo1mmPoint.step",
"chars": 12376,
"preview": "ISO-10303-21;\r\nHEADER;\r\n/* Generated by software containing ST-Developer\r\n * from STEP Tools, Inc. (www.steptools.com) \r"
},
{
"path": "electronics/lib/Pogo.models/pogo2mmCup.step",
"chars": 13134,
"preview": "ISO-10303-21;\r\nHEADER;\r\n/* Generated by software containing ST-Developer\r\n * from STEP Tools, Inc. (www.steptools.com) \r"
},
{
"path": "electronics/lib/RS485.pretty/RS485Module.kicad_mod",
"chars": 2448,
"preview": "(module RS485Module (layer F.Cu) (tedit 5FFB4CC4)\n (fp_text reference REF** (at 1.27 -3.81) (layer F.SilkS)\n (effect"
},
{
"path": "electronics/lib/RS485Module.dcm",
"chars": 48,
"preview": "EESchema-DOCLIB Version 2.0\n#\n#End Doc Library\n"
},
{
"path": "electronics/lib/RS485Module.lib",
"chars": 726,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# RS485Module\n#\nDEF RS485Module U 0 40 Y Y 1 F N\nF0 \"U\" 0 300 50 H V C CN"
},
{
"path": "electronics/lib/Resistor0805ThroughHole.pretty/R_0805_ThroughHole.kicad_mod",
"chars": 1666,
"preview": "(module R_0805_ThroughHole (layer F.Cu) (tedit 59E052CC)\n (descr \"Resistor SMD 0805, hand soldering\")\n (tags \"resistor"
},
{
"path": "electronics/lib/SSD1306.pretty/SSD1306_128x32.kicad_mod",
"chars": 1387,
"preview": "(module SSD1306_128x32 (layer F.Cu) (tedit 5FF4B39A)\n (fp_text reference REF** (at 0 0.5) (layer F.SilkS)\n (effects "
},
{
"path": "electronics/lib/ST7789.pretty/ST7789_240x240.kicad_mod",
"chars": 2331,
"preview": "(module ST7789_240x240 (layer F.Cu) (tedit 5F95076D)\n (fp_text reference REF** (at -1.27 -3.81) (layer F.SilkS)\n (ef"
},
{
"path": "electronics/lib/ST7789.pretty/ST7789_80x160.kicad_mod",
"chars": 2430,
"preview": "(module ST7789_80x160 (layer F.Cu) (tedit 5F95B61B)\n (fp_text reference REF** (at -1.27 -3.81) (layer F.SilkS)\n (eff"
},
{
"path": "electronics/lib/ScrewTerminals.pretty/C72334_WJ500V-5.08-3P.kicad_mod",
"chars": 2014,
"preview": "(module C72334_WJ500V-5.08-3P (layer F.Cu) (tedit 601A2A0A)\n (fp_text reference REF** (at 0 0.5) (layer F.SilkS)\n (e"
},
{
"path": "electronics/lib/ScrewTerminals.pretty/C8465_WJ500V-5.08-2P.kicad_mod",
"chars": 1698,
"preview": "(module C8465_WJ500V-5.08-2P (layer F.Cu) (tedit 604991F7)\n (fp_text reference REF** (at 0 0.5) (layer F.SilkS)\n (ef"
},
{
"path": "electronics/lib/ScrewTerminals.pretty/EB147A-02-D.kicad_mod",
"chars": 1690,
"preview": "(module EB147A-02-D (layer F.Cu) (tedit 605D33FA)\n (fp_text reference REF** (at 2.54 -2.921) (layer F.SilkS)\n (effec"
},
{
"path": "electronics/lib/ScrewTerminals.pretty/Generic-5.08-2P.kicad_mod",
"chars": 1993,
"preview": "(module Generic-5.08-2P (layer F.Cu) (tedit 618039E9)\n (fp_text reference REF** (at 2.54 -2.921) (layer F.SilkS)\n (e"
},
{
"path": "electronics/lib/ScrewTerminals.pretty/Generic-5.08-3P.kicad_mod",
"chars": 2301,
"preview": "(module Generic-5.08-3P (layer F.Cu) (tedit 606BBBA9)\n (fp_text reference REF** (at 2.54 -2.921) (layer F.SilkS)\n (e"
},
{
"path": "electronics/lib/SolderJumpers.pretty/SolderJumper-2_P1.3mm_Bridged_Pad1.0x1.5mm.kicad_mod",
"chars": 1243,
"preview": "(module SolderJumper-2_P1.3mm_Bridged_Pad1.0x1.5mm (layer F.Cu) (tedit 5F7C5974)\n (descr \"SMD Solder Jumper, 1x1.5mm Pa"
},
{
"path": "electronics/lib/SwitchESE13.pretty/ESE13V01D.kicad_mod",
"chars": 1566,
"preview": "(module ESE13V01D (layer F.Cu) (tedit 60739FC9)\n (fp_text reference REF** (at -1.1 2.9) (layer F.SilkS)\n (effects (f"
},
{
"path": "electronics/lib/TTGO.models/TTGO_TDisplay.step",
"chars": 106500,
"preview": "ISO-10303-21;\r\nHEADER;\r\n/* Generated by software containing ST-Developer\r\n * from STEP Tools, Inc. (www.steptools.com) \r"
},
{
"path": "electronics/lib/ULN2003AModule.dcm",
"chars": 48,
"preview": "EESchema-DOCLIB Version 2.0\n#\n#End Doc Library\n"
},
{
"path": "electronics/lib/ULN2003AModule.lib",
"chars": 1296,
"preview": "EESchema-LIBRARY Version 2.4\n#encoding utf-8\n#\n# ULN2003AModule\n#\nDEF ULN2003AModule U 0 40 Y Y 1 F N\nF0 \"U\" 100 50 50 H"
},
{
"path": "electronics/lib/ULN2003AModule.pretty/ULN2003AModule.kicad_mod",
"chars": 5063,
"preview": "(module ULN2003AModule (layer F.Cu) (tedit 5FF54D69)\n (fp_text reference REF** (at 0 0.5) (layer F.SilkS)\n (effects "
}
]
// ... and 208 more files (download for full content)
About this extraction
This page contains the full source code of the scottbez1/splitflap GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 408 files (6.4 MB), approximately 1.7M tokens, and a symbol index with 1013 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.