diff --git a/extensions/fablabchemnitz/boxes.py/ABox-thumb.jpg b/extensions/fablabchemnitz/boxes.py/ABox-thumb.jpg index f9bfaea..0005508 100644 Binary files a/extensions/fablabchemnitz/boxes.py/ABox-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/ABox-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/AirPurifier-thumb.jpg b/extensions/fablabchemnitz/boxes.py/AirPurifier-thumb.jpg new file mode 100644 index 0000000..db7ee71 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/AirPurifier-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/Arcade-thumb.jpg b/extensions/fablabchemnitz/boxes.py/Arcade-thumb.jpg index f9bfaea..f7deb9b 100644 Binary files a/extensions/fablabchemnitz/boxes.py/Arcade-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/Arcade-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/BirdHouse-thumb.jpg b/extensions/fablabchemnitz/boxes.py/BirdHouse-thumb.jpg index f9bfaea..3552aba 100644 Binary files a/extensions/fablabchemnitz/boxes.py/BirdHouse-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/BirdHouse-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/BreadBox-thumb.jpg b/extensions/fablabchemnitz/boxes.py/BreadBox-thumb.jpg index f9bfaea..09cd8f7 100644 Binary files a/extensions/fablabchemnitz/boxes.py/BreadBox-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/BreadBox-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/CoinBankSafe-closed-thumb.jpg b/extensions/fablabchemnitz/boxes.py/CoinBankSafe-closed-thumb.jpg new file mode 100644 index 0000000..ff28c8f Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/CoinBankSafe-closed-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/CoinBankSafe-open-thumb.jpg b/extensions/fablabchemnitz/boxes.py/CoinBankSafe-open-thumb.jpg new file mode 100644 index 0000000..83767fb Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/CoinBankSafe-open-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/CoinBankSafe-pins-thumb.jpg b/extensions/fablabchemnitz/boxes.py/CoinBankSafe-pins-thumb.jpg new file mode 100644 index 0000000..7d35814 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/CoinBankSafe-pins-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/CoinBankSafe-thumb.jpg b/extensions/fablabchemnitz/boxes.py/CoinBankSafe-thumb.jpg new file mode 100644 index 0000000..4330eb7 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/CoinBankSafe-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/ConcaveKnob-thumb.jpg b/extensions/fablabchemnitz/boxes.py/ConcaveKnob-thumb.jpg index f9bfaea..f72ca0c 100644 Binary files a/extensions/fablabchemnitz/boxes.py/ConcaveKnob-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/ConcaveKnob-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/ConsoleArcadeStickBack-thumb.jpg b/extensions/fablabchemnitz/boxes.py/ConsoleArcadeStickBack-thumb.jpg new file mode 100644 index 0000000..d925638 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/ConsoleArcadeStickBack-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/ConsoleArcadeStickFront-thumb.jpg b/extensions/fablabchemnitz/boxes.py/ConsoleArcadeStickFront-thumb.jpg new file mode 100644 index 0000000..732354d Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/ConsoleArcadeStickFront-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/ConsoleArcadeStickInside-thumb.jpg b/extensions/fablabchemnitz/boxes.py/ConsoleArcadeStickInside-thumb.jpg new file mode 100644 index 0000000..52082f7 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/ConsoleArcadeStickInside-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/Desksign-thumb.jpg b/extensions/fablabchemnitz/boxes.py/Desksign-thumb.jpg new file mode 100644 index 0000000..fa569eb Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/Desksign-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/Dispenser-thumb.jpg b/extensions/fablabchemnitz/boxes.py/Dispenser-thumb.jpg index f9bfaea..eadae58 100644 Binary files a/extensions/fablabchemnitz/boxes.py/Dispenser-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/Dispenser-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/DrillBox-thumb.jpg b/extensions/fablabchemnitz/boxes.py/DrillBox-thumb.jpg index f9bfaea..646e2c2 100644 Binary files a/extensions/fablabchemnitz/boxes.py/DrillBox-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/DrillBox-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/DrillBoxes-thumb.jpg b/extensions/fablabchemnitz/boxes.py/DrillBoxes-thumb.jpg new file mode 100644 index 0000000..372084d Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/DrillBoxes-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/FanHole-thumb.jpg b/extensions/fablabchemnitz/boxes.py/FanHole-thumb.jpg index f9bfaea..b25bb2a 100644 Binary files a/extensions/fablabchemnitz/boxes.py/FanHole-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/FanHole-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/FilamentSpool-2-thumb.jpg b/extensions/fablabchemnitz/boxes.py/FilamentSpool-2-thumb.jpg new file mode 100644 index 0000000..61b5efa Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/FilamentSpool-2-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/FilamentSpool-thumb.jpg b/extensions/fablabchemnitz/boxes.py/FilamentSpool-thumb.jpg new file mode 100644 index 0000000..2c3333d Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/FilamentSpool-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/FillTest-thumb.jpg b/extensions/fablabchemnitz/boxes.py/FillTest-thumb.jpg index f9bfaea..3bb0401 100644 Binary files a/extensions/fablabchemnitz/boxes.py/FillTest-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/FillTest-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/FlexBook-2-thumb.jpg b/extensions/fablabchemnitz/boxes.py/FlexBook-2-thumb.jpg new file mode 100644 index 0000000..d8e2a2f Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/FlexBook-2-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/FlexBook-thumb.jpg b/extensions/fablabchemnitz/boxes.py/FlexBook-thumb.jpg new file mode 100644 index 0000000..a29939b Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/FlexBook-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/FlexBox5-thumb.jpg b/extensions/fablabchemnitz/boxes.py/FlexBox5-thumb.jpg index f9bfaea..50af933 100644 Binary files a/extensions/fablabchemnitz/boxes.py/FlexBox5-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/FlexBox5-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/FlexTest2-thumb.jpg b/extensions/fablabchemnitz/boxes.py/FlexTest2-thumb.jpg index f9bfaea..475e8cd 100644 Binary files a/extensions/fablabchemnitz/boxes.py/FlexTest2-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/FlexTest2-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/FrontPanel-thumb.jpg b/extensions/fablabchemnitz/boxes.py/FrontPanel-thumb.jpg new file mode 100644 index 0000000..54b362b Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/FrontPanel-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/GearBox-thumb.jpg b/extensions/fablabchemnitz/boxes.py/GearBox-thumb.jpg index f9bfaea..095b2cf 100644 Binary files a/extensions/fablabchemnitz/boxes.py/GearBox-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/GearBox-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/GridfinityBase-thumb.jpg b/extensions/fablabchemnitz/boxes.py/GridfinityBase-thumb.jpg new file mode 100644 index 0000000..0b3eea3 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/GridfinityBase-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/GridfinityTrayLayout-thumb.jpg b/extensions/fablabchemnitz/boxes.py/GridfinityTrayLayout-thumb.jpg new file mode 100644 index 0000000..d1cc411 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/GridfinityTrayLayout-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/HolePattern-thumb.jpg b/extensions/fablabchemnitz/boxes.py/HolePattern-thumb.jpg index f9bfaea..3227ac3 100644 Binary files a/extensions/fablabchemnitz/boxes.py/HolePattern-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/HolePattern-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/Hook-thumb.jpg b/extensions/fablabchemnitz/boxes.py/Hook-thumb.jpg index f9bfaea..397aa51 100644 Binary files a/extensions/fablabchemnitz/boxes.py/Hook-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/Hook-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/KeyHolder-2-thumb.jpg b/extensions/fablabchemnitz/boxes.py/KeyHolder-2-thumb.jpg new file mode 100644 index 0000000..40ddf17 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/KeyHolder-2-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/KeyHolder-thumb.jpg b/extensions/fablabchemnitz/boxes.py/KeyHolder-thumb.jpg new file mode 100644 index 0000000..9935708 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/KeyHolder-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/LaptopStand-thumb.jpg b/extensions/fablabchemnitz/boxes.py/LaptopStand-thumb.jpg index f9bfaea..df82dc1 100644 Binary files a/extensions/fablabchemnitz/boxes.py/LaptopStand-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/LaptopStand-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/MagazinFile-thumb.jpg b/extensions/fablabchemnitz/boxes.py/MagazineFile-thumb.jpg similarity index 100% rename from extensions/fablabchemnitz/boxes.py/MagazinFile-thumb.jpg rename to extensions/fablabchemnitz/boxes.py/MagazineFile-thumb.jpg diff --git a/extensions/fablabchemnitz/boxes.py/March36539-box-thumb.jpg b/extensions/fablabchemnitz/boxes.py/March36539-box-thumb.jpg deleted file mode 100644 index b5a57fd..0000000 Binary files a/extensions/fablabchemnitz/boxes.py/March36539-box-thumb.jpg and /dev/null differ diff --git a/extensions/fablabchemnitz/boxes.py/NemaPattern-thumb.jpg b/extensions/fablabchemnitz/boxes.py/NemaPattern-thumb.jpg index f9bfaea..7203d0a 100644 Binary files a/extensions/fablabchemnitz/boxes.py/NemaPattern-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/NemaPattern-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/OttoBody-thumb.jpg b/extensions/fablabchemnitz/boxes.py/OttoBody-thumb.jpg index 481d13c..f04d1f1 100644 Binary files a/extensions/fablabchemnitz/boxes.py/OttoBody-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/OttoBody-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/OttoLegs-thumb.jpg b/extensions/fablabchemnitz/boxes.py/OttoLegs-thumb.jpg index f9bfaea..227cdcc 100644 Binary files a/extensions/fablabchemnitz/boxes.py/OttoLegs-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/OttoLegs-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/OttoSoles-thumb.jpg b/extensions/fablabchemnitz/boxes.py/OttoSoles-thumb.jpg index f9bfaea..ac6b621 100644 Binary files a/extensions/fablabchemnitz/boxes.py/OttoSoles-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/OttoSoles-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/PirateChest-thumb.jpg b/extensions/fablabchemnitz/boxes.py/PirateChest-thumb.jpg new file mode 100644 index 0000000..40248cb Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/PirateChest-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/PizzaShovel-thumb.jpg b/extensions/fablabchemnitz/boxes.py/PizzaShovel-thumb.jpg new file mode 100644 index 0000000..210f0a7 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/PizzaShovel-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/Rack19Box-thumb.jpg b/extensions/fablabchemnitz/boxes.py/Rack19Box-thumb.jpg index f9bfaea..91e5d5f 100644 Binary files a/extensions/fablabchemnitz/boxes.py/Rack19Box-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/Rack19Box-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/RackBox-thumb.jpg b/extensions/fablabchemnitz/boxes.py/RackBox-thumb.jpg index f9bfaea..24d02f3 100644 Binary files a/extensions/fablabchemnitz/boxes.py/RackBox-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/RackBox-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/RectangularWall-thumb.jpg b/extensions/fablabchemnitz/boxes.py/RectangularWall-thumb.jpg index f9bfaea..4d586cd 100644 Binary files a/extensions/fablabchemnitz/boxes.py/RectangularWall-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/RectangularWall-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/RollHolder-thumb.jpg b/extensions/fablabchemnitz/boxes.py/RollHolder-thumb.jpg new file mode 100644 index 0000000..ea134d6 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/RollHolder-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/SevenSegmentPattern-thumb.jpg b/extensions/fablabchemnitz/boxes.py/SevenSegmentPattern-thumb.jpg new file mode 100644 index 0000000..c51ff35 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/SevenSegmentPattern-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/Shadowbox-backlit-thumb.jpg b/extensions/fablabchemnitz/boxes.py/Shadowbox-backlit-thumb.jpg new file mode 100644 index 0000000..3ac0558 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/Shadowbox-backlit-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/Shadowbox-diagram-thumb.jpg b/extensions/fablabchemnitz/boxes.py/Shadowbox-diagram-thumb.jpg new file mode 100644 index 0000000..a7f5d55 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/Shadowbox-diagram-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/Shadowbox-thumb.jpg b/extensions/fablabchemnitz/boxes.py/Shadowbox-thumb.jpg new file mode 100644 index 0000000..ffd2794 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/Shadowbox-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/Shoe-thumb.jpg b/extensions/fablabchemnitz/boxes.py/Shoe-thumb.jpg new file mode 100644 index 0000000..06d2aca Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/Shoe-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/SideDoorHousing-2-thumb.jpg b/extensions/fablabchemnitz/boxes.py/SideDoorHousing-2-thumb.jpg new file mode 100644 index 0000000..ef4d2a9 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/SideDoorHousing-2-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/SideDoorHousing-thumb.jpg b/extensions/fablabchemnitz/boxes.py/SideDoorHousing-thumb.jpg index f9bfaea..f9998ec 100644 Binary files a/extensions/fablabchemnitz/boxes.py/SideDoorHousing-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/SideDoorHousing-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/SkadisBoard-thumb.jpg b/extensions/fablabchemnitz/boxes.py/SkadisBoard-thumb.jpg new file mode 100644 index 0000000..a6ebc1d Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/SkadisBoard-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/SlantedTray-2-thumb.jpg b/extensions/fablabchemnitz/boxes.py/SlantedTray-2-thumb.jpg new file mode 100644 index 0000000..0e56869 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/SlantedTray-2-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/SlantedTray-thumb.jpg b/extensions/fablabchemnitz/boxes.py/SlantedTray-thumb.jpg new file mode 100644 index 0000000..c81d501 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/SlantedTray-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/SmallPartsTray-thumb.jpg b/extensions/fablabchemnitz/boxes.py/SmallPartsTray-thumb.jpg new file mode 100644 index 0000000..fd7fbd7 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/SmallPartsTray-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/SmallPartsTray2-thumb.jpg b/extensions/fablabchemnitz/boxes.py/SmallPartsTray2-thumb.jpg new file mode 100644 index 0000000..15ded3a Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/SmallPartsTray2-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/Spool-thumb.jpg b/extensions/fablabchemnitz/boxes.py/Spool-thumb.jpg new file mode 100644 index 0000000..3af542e Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/Spool-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/Tetris-thumb.jpg b/extensions/fablabchemnitz/boxes.py/Tetris-thumb.jpg new file mode 100644 index 0000000..2adfe7a Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/Tetris-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/WallChiselHolder-thumb.jpg b/extensions/fablabchemnitz/boxes.py/WallChiselHolder-thumb.jpg index f9bfaea..5806953 100644 Binary files a/extensions/fablabchemnitz/boxes.py/WallChiselHolder-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/WallChiselHolder-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/WallConsole-thumb.jpg b/extensions/fablabchemnitz/boxes.py/WallConsole-thumb.jpg index f9bfaea..d7d819b 100644 Binary files a/extensions/fablabchemnitz/boxes.py/WallConsole-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/WallConsole-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/WallPinRow-thumb.jpg b/extensions/fablabchemnitz/boxes.py/WallPinRow-thumb.jpg index f9bfaea..e3c239a 100644 Binary files a/extensions/fablabchemnitz/boxes.py/WallPinRow-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/WallPinRow-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/WallPlaneHolder-thumb.jpg b/extensions/fablabchemnitz/boxes.py/WallPlaneHolder-thumb.jpg index f9bfaea..ff7e455 100644 Binary files a/extensions/fablabchemnitz/boxes.py/WallPlaneHolder-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/WallPlaneHolder-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/WallRack-thumb.jpg b/extensions/fablabchemnitz/boxes.py/WallRack-thumb.jpg new file mode 100644 index 0000000..3be0760 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/WallRack-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/WallRollHolder-thumb.jpg b/extensions/fablabchemnitz/boxes.py/WallRollHolder-thumb.jpg new file mode 100644 index 0000000..b80397d Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/WallRollHolder-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/WallStairs-thumb.jpg b/extensions/fablabchemnitz/boxes.py/WallStairs-thumb.jpg index f9bfaea..62a1144 100644 Binary files a/extensions/fablabchemnitz/boxes.py/WallStairs-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/WallStairs-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/WallWrenchHolder-thumb.jpg b/extensions/fablabchemnitz/boxes.py/WallWrenchHolder-thumb.jpg index f9bfaea..3768f86 100644 Binary files a/extensions/fablabchemnitz/boxes.py/WallWrenchHolder-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/WallWrenchHolder-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.ABox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.ABox.inx index b5cabe2..2eb3df3 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.ABox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.ABox.inx @@ -3,51 +3,67 @@ ABox info.festi.boxes.py.ABox abox - - - 100.0 - 100.0 - 100.0 - true +100.0 +100.0 +100.0 +true - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./ABox-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + + + + + + + + + + + + + + +8.0 +4.0 +0.1 + + +./ABox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.AgricolaInsert.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.AgricolaInsert.inx index 2c7a635..5af6098 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.AgricolaInsert.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.AgricolaInsert.inx @@ -3,39 +3,38 @@ AgricolaInsert info.festi.boxes.py.AgricolaInsert agricolainsert - - - 3.0 - 0.0 - false - true - 100 - - - - +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 1.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./AgricolaInsert-thumb.jpg - +1.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./AgricolaInsert-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.AirPurifier.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.AirPurifier.inx new file mode 100644 index 0000000..18883e2 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.AirPurifier.inx @@ -0,0 +1,79 @@ + + +AirPurifier +info.festi.boxes.py.AirPurifier +airpurifier +498.0 +496.0 +46.77 +30.0 + + + + + + + + + + + + +true +-1 +-1 +0 +0 +5.0 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +50 +1.0 +0.2 +2 + + +./AirPurifier-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.AllEdges.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.AllEdges.inx index c4e3b21..44c7787 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.AllEdges.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.AllEdges.inx @@ -3,103 +3,97 @@ AllEdges info.festi.boxes.py.AllEdges alledges - - - 100 - - - 3.0 - 0.0 - false - true - 100 - - - - +100 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - 0 - false - 0.5 - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + +0 +false +0.5 + + + - 2.0 - 0 - 1 - - - 90.0 - 0 - true - - - - - +2.0 +0 +1 + +90.0 +0 +true + + + + + - - - - - + + + + + - 2.0 - 1.0 - 0.0 - 3.0 - 0.05 - 2.0 - 1.0 - - - 5.0 - 0.1 - 3.0 - - - 1.05 - 1.0 - 0.5 - 5.0 - - - 20.0 - 75.0 - 40:40 - true - 10.0 - 1.0 - - - ./AllEdges-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +3.0 +0.05 +2.0 +1.0 + +5.0 +0.1 +3.0 + +1.05 +1.0 +0.5 +5.0 + +20.0 +75.0 +40:40 +true +10.0 +1.0 + + +./AllEdges-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.AngledBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.AngledBox.inx index 1c70106..85400a9 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.AngledBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.AngledBox.inx @@ -3,58 +3,56 @@ AngledBox info.festi.boxes.py.AngledBox angledbox - - - 100.0 - 100.0 - 100.0 - true +100.0 +100.0 +100.0 +true - 5 - - - - - +5 + + + + + - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./AngledBox-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./AngledBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.AngledCutJig.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.AngledCutJig.inx index 2dc90e7..6b1102b 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.AngledCutJig.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.AngledCutJig.inx @@ -3,44 +3,42 @@ AngledCutJig info.festi.boxes.py.AngledCutJig angledcutjig - - - 50 - 100 - 45.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +50 +100 +45.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 1.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./AngledCutJig-thumb.jpg - +1.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./AngledCutJig-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Arcade.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Arcade.inx index b872d8c..78ed0a9 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Arcade.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Arcade.inx @@ -3,44 +3,42 @@ Arcade info.festi.boxes.py.Arcade arcade - - - 450.0 - 350.0 - 150.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +450.0 +350.0 +150.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./Arcade-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./Arcade-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Atreus21.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Atreus21.inx index 86579d6..d623913 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Atreus21.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Atreus21.inx @@ -3,31 +3,29 @@ Atreus21 info.festi.boxes.py.Atreus21 atreus21 - - - true - true - false - false - castle - 4@3/4@6/4@11/4@5/4@0/1@7.8 - - - 3.0 - 0.0 - false - true - 100 - - - - +true +true +false +false +castle +4@3/4@6/4@11/4@5/4@0/1@7.8 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./Atreus21-thumb.jpg - +0.1 + + +./Atreus21-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.BasedBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.BasedBox.inx index f3f51cb..9f16bef 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.BasedBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.BasedBox.inx @@ -3,45 +3,43 @@ BasedBox info.festi.boxes.py.BasedBox basedbox - - - 100.0 - 100.0 - 100.0 - true - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +100.0 +true + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./BasedBox-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./BasedBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.BayonetBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.BayonetBox.inx index 250dce2..7e47c6a 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.BayonetBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.BayonetBox.inx @@ -3,29 +3,27 @@ BayonetBox info.festi.boxes.py.BayonetBox bayonetbox - - - 50.0 - 10 - 1.0 - true - - - 3.0 - 0.0 - false - true - 100 - - - - +50.0 +10 +1.0 +true + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./BayonetBox-thumb.jpg - +0.1 + + +./BayonetBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.BinTray.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.BinTray.inx index 26b9e33..3a3aa45 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.BinTray.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.BinTray.inx @@ -3,47 +3,45 @@ BinTray info.festi.boxes.py.BinTray bintray - - - 50*3 - 50*3 - 100.0 - true - 3.5:6.5 - 0.4 - - - 3.0 - 0.0 - false - true - 100 - - - - +50*3 +50*3 +100.0 +true +3.5:6.5 +0.4 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.5 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./BinTray-thumb.jpg - +0.5 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./BinTray-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.BirdHouse.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.BirdHouse.inx index 8e03b7e..b9290b5 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.BirdHouse.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.BirdHouse.inx @@ -3,51 +3,50 @@ BirdHouse info.festi.boxes.py.BirdHouse birdhouse - - - 200 - 200 - 200 - - - 3.0 - 0.0 - false - true - 100 - - - - +200 +200 +200 +0.4 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 10.0 - 0.0 - 10.0 - 1.0 - - - ./BirdHouse-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +10.0 +0.0 +10.0 +1.0 + + +./BirdHouse-thumb.jpg + all - + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.BottleStack.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.BottleStack.inx index d4eb440..8642c79 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.BottleStack.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.BottleStack.inx @@ -3,29 +3,27 @@ BottleStack info.festi.boxes.py.BottleStack bottlestack - - - 80 - 3 - 80 - true - - - 3.0 - 0.0 - false - true - 100 - - - - +80 +3 +80 +true + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./BottleStack-thumb.jpg - +0.1 + + +./BottleStack-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.BottleTag.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.BottleTag.inx index d515ce7..ebcedfd 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.BottleTag.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.BottleTag.inx @@ -3,31 +3,29 @@ BottleTag info.festi.boxes.py.BottleTag bottletag - - - 72 - 98 - 24 - 50 - 15 - 3 - - - 3.0 - 0.0 - false - true - 100 - - - - +72 +98 +24 +50 +15 +3 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./BottleTag-thumb.jpg - +0.1 + + +./BottleTag-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.BreadBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.BreadBox.inx index 3f96e37..c1736b7 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.BreadBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.BreadBox.inx @@ -3,58 +3,55 @@ BreadBox info.festi.boxes.py.BreadBox breadbox - - - 150 - 100 - 100 - 40.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +150 +100 +100 +40.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.5 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.05 - 2.0 - 0.75 - 5.0 - - - ./BreadBox-thumb.jpg - +0.5 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.05 +2.0 +0.75 +5.0 + + +./BreadBox-thumb.jpg + all - + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.BurnTest.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.BurnTest.inx index 76dbe02..513c61b 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.BurnTest.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.BurnTest.inx @@ -3,44 +3,44 @@ BurnTest info.festi.boxes.py.BurnTest burntest - - - 100 - 0.01 - 2 - - - 3.0 - 0.0 - false - true - 100 - - - - +100 +0.01 +2 +false + + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./BurnTest-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./BurnTest-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.CanStorage.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.CanStorage.inx index 6ad5f97..a0a7257 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.CanStorage.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.CanStorage.inx @@ -3,9 +3,7 @@ CanStorage info.festi.boxes.py.CanStorage canstorage - - - + @@ -16,72 +14,71 @@ - 75 - 110 - 12 - 5.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +75 +110 +12 +5.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - 50 - - - - - - - +0.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + +50 + + + + + + + - 3.0 - 0.5 - - - - - - +3.0 +0.5 + + + + + + - 1000 - 4.0 - 4.0 - - - ./CanStorage-thumb.jpg - +1000 +4.0 +4.0 + + +./CanStorage-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.CarbonFilter.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.CarbonFilter.inx new file mode 100644 index 0000000..8de63fb --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.CarbonFilter.inx @@ -0,0 +1,57 @@ + + +CarbonFilter +info.festi.boxes.py.CarbonFilter +carbonfilter +550 +550 +250 +3 +12 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./CarbonFilter-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.CardBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.CardBox.inx index 5c61973..b3fae36 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.CardBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.CardBox.inx @@ -3,45 +3,54 @@ CardBox info.festi.boxes.py.CardBox cardbox - - - 30 - 65 - 90 - 2 - - - 3.0 - 0.0 - false - true - 100 - - - - +68 +92 +false +65*4 + + + - 0.1 - - - 90.0 - - - - - + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./CardBox-thumb.jpg - +20 +false + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./CardBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.CardHolder.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.CardHolder.inx index 8be679e..e668139 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.CardHolder.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.CardHolder.inx @@ -3,62 +3,65 @@ CardHolder info.festi.boxes.py.CardHolder cardholder - - - 68*3 - 100 - 40 - false - 7.5 - true - - - 3.0 - 0.0 - false - true - 100 - - - - +68*3 +100 +40 +false +7.5 +true + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 120 - 0.1 - false - false - 0.3 - - - - - +0.1 + +60 +0.0 +2.0 +1.0 +4.0 + +120 +0.1 +false +false +0.3 + + + + + - 30 - 0.2 - - - 90.0 - - - - - +30 +0.2 + +90.0 + + + + + - 1.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./CardHolder-thumb.jpg - +1.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./CardHolder-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Castle.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Castle.inx index 330e9e0..3aae195 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Castle.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Castle.inx @@ -3,39 +3,38 @@ Castle info.festi.boxes.py.Castle castle - - - 3.0 - 0.0 - false - true - 100 - - - - +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./Castle-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./Castle-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.ClosedBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.ClosedBox.inx index faa6146..fc8d749 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.ClosedBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.ClosedBox.inx @@ -3,45 +3,43 @@ ClosedBox info.festi.boxes.py.ClosedBox closedbox - - - 100.0 - 100.0 - 100.0 - true - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +100.0 +true + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./ClosedBox-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./ClosedBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.CoffeeCapsuleHolder.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.CoffeeCapsuleHolder.inx index 32de0c9..821c728 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.CoffeeCapsuleHolder.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.CoffeeCapsuleHolder.inx @@ -3,28 +3,26 @@ CoffeeCapsuleHolder info.festi.boxes.py.CoffeeCapsuleHolder coffeecapsuleholder - - - 4 - 5 - true - - - 3.0 - 0.0 - false - true - 100 - - - - +4 +5 +true + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./CoffeeCapsuleHolder-thumb.jpg - +0.1 + + +./CoffeeCapsuleHolder-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.CoinBankSafe.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.CoinBankSafe.inx new file mode 100644 index 0000000..8abb718 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.CoinBankSafe.inx @@ -0,0 +1,59 @@ + + +CoinBankSafe +info.festi.boxes.py.CoinBankSafe +coinbanksafe +100.0 +100.0 +100.0 +30 +4 +8 +1.5 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./CoinBankSafe-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.CoinDisplay.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.CoinDisplay.inx index 7a0fe8b..c0be2c3 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.CoinDisplay.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.CoinDisplay.inx @@ -3,49 +3,47 @@ CoinDisplay info.festi.boxes.py.CoinDisplay coindisplay - - - 100.0 - 100.0 - 100.0 - true - 20.0 - 50.0 - 50.0 - 30 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +100.0 +true +20.0 +50.0 +50.0 +30 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./CoinDisplay-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./CoinDisplay-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.ConcaveKnob.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.ConcaveKnob.inx index 2492b98..beaf93c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.ConcaveKnob.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.ConcaveKnob.inx @@ -3,32 +3,30 @@ ConcaveKnob info.festi.boxes.py.ConcaveKnob concaveknob - - - 50.0 - 3 - 0.2 - 70.0 - 6.0 - 1.0 - 10.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +50.0 +3 +0.2 +70.0 +6.0 +1.0 +10.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./ConcaveKnob-thumb.jpg - +0.1 + + +./ConcaveKnob-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Console.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Console.inx index d5a6c09..e41a7af 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Console.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Console.inx @@ -3,53 +3,51 @@ Console info.festi.boxes.py.Console console - - - 100 - 100 - 100 - false - 30 - 50 - - - 3.0 - 0.0 - false - true - 100 - - - - +100 +100 +100 +false +30 +50 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.5 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - ./Console-thumb.jpg - +0.5 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + + +./Console-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Console2.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Console2.inx index 6f3a5a4..34fb238 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Console2.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Console2.inx @@ -3,62 +3,60 @@ Console2 info.festi.boxes.py.Console2 console2 - - - 100 - 100 - 100 +100 +100 +100 - false - 30 - 50 - true - true - true - - - 3.0 - 0.0 - false - true - 100 - - - - +false +30 +50 +true +true +true + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.5 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - ./Console2-thumb.jpg - +0.5 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + + +./Console2-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Desksign.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Desksign.inx new file mode 100644 index 0000000..7335a34 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Desksign.inx @@ -0,0 +1,65 @@ + + +Desksign +info.festi.boxes.py.Desksign +desksign +150 +80 +60 + +20 +false +true + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +2.0 + + +./Desksign-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.DiceBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.DiceBox.inx index f54957d..11f7fc0 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.DiceBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.DiceBox.inx @@ -3,55 +3,52 @@ DiceBox info.festi.boxes.py.DiceBox dicebox - - - 100 - 100 - 18 - true - 18 - 5 - 6 - - - 3.0 - 0.0 - false - true - 100 - - - - +100 +100 +18 +true +18 +5 +6 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - true - true - 1.0 - 2.0 - 0.1 - - - ./DiceBox-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +true +true +1.0 +2.0 +0.1 + + +./DiceBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.DinRailBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.DinRailBox.inx index b319030..5b77d2d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.DinRailBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.DinRailBox.inx @@ -3,46 +3,44 @@ DinRailBox info.festi.boxes.py.DinRailBox dinrailbox - - - 70 - 90 - 60 - 35.0 - 0.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +70 +90 +60 +35.0 +0.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.8 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./DinRailBox-thumb.jpg - +0.8 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./DinRailBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.DiscRack.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.DiscRack.inx index fd9b79e..3f111aa 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.DiscRack.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.DiscRack.inx @@ -3,48 +3,46 @@ DiscRack info.festi.boxes.py.DiscRack discrack - - - 20*10 - 150.0 - 5.0 - 0.75 - 0.75 - 3.0 - 18 - - - 3.0 - 0.0 - false - true - 100 - - - - +20*10 +150.0 +5.0 +0.75 +0.75 +3.0 +18 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./DiscRack-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./DiscRack-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Dispenser.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Dispenser.inx index 5c55d1c..c6be1e9 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Dispenser.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Dispenser.inx @@ -3,56 +3,54 @@ Dispenser info.festi.boxes.py.Dispenser dispenser - - - 100 - 100 - 100 - 10.0 - 0.0 +100 +100 +100 +10.0 +0.0 - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - ./Dispenser-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + + +./Dispenser-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Display.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Display.inx index 5b154e3..ddaa79d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Display.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Display.inx @@ -3,29 +3,27 @@ Display info.festi.boxes.py.Display display - - - 150.0 - 200.0 - 5.0 - 0.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +150.0 +200.0 +5.0 +0.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./Display-thumb.jpg - +0.1 + + +./Display-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.DisplayCase.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.DisplayCase.inx index c9a9807..a802ea3 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.DisplayCase.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.DisplayCase.inx @@ -3,46 +3,44 @@ DisplayCase info.festi.boxes.py.DisplayCase displaycase - - - 100.0 - 100.0 - 100.0 - true - 2 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +100.0 +true +2 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./DisplayCase-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./DisplayCase-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.DisplayShelf.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.DisplayShelf.inx index 9d09746..0c738cc 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.DisplayShelf.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.DisplayShelf.inx @@ -3,48 +3,49 @@ DisplayShelf info.festi.boxes.py.DisplayShelf displayshelf - - - 400 - 100 - 300 - true - 3 - 20.0 - 30.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +400 +100 +300 +true +3 +20.0 +30.0 +false +false +20.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./DisplayShelf-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./DisplayShelf-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.DividerTray.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.DividerTray.inx index 84c5aa9..60fc542 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.DividerTray.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.DividerTray.inx @@ -3,73 +3,85 @@ DividerTray info.festi.boxes.py.DividerTray dividertray - - - 50*3 - 50*3 - 100.0 - true - true - true - true - false - false - - - 3.0 - 0.0 - false - true - 100 - - - - +50*3 +50*3 +100.0 +true +true +true +true +false +false + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 20.0 - 75.0 - 40:40 - true - 10.0 - 1.0 - - - 0 - 20 - 0.2 - 2 - - - 15 - 8 - 1 - - - 0 - 0.05 - - - ./DividerTray-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +20.0 +75.0 +40:40 +true +10.0 +1.0 + + + + + + + + + + + + + + + +8.0 +4.0 +0.1 + +0 +20 +0.2 +2 + +15 +8 +1 + +0 +0.05 + + +./DividerTray-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.DoubleFlexDoorBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.DoubleFlexDoorBox.inx index c0b6bfb..260bf5d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.DoubleFlexDoorBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.DoubleFlexDoorBox.inx @@ -3,53 +3,50 @@ DoubleFlexDoorBox info.festi.boxes.py.DoubleFlexDoorBox doubleflexdoorbox - - - 100.0 - 100.0 - 100.0 - true - 15 - 8 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +100.0 +true +15 +8 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.05 - 1.0 - 0.5 - 5.0 - - - ./DoubleFlexDoorBox-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.05 +1.0 +0.5 +5.0 + + +./DoubleFlexDoorBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.DrillBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.DrillBox.inx index eb37e3f..afc2221 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.DrillBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.DrillBox.inx @@ -3,88 +3,102 @@ DrillBox info.festi.boxes.py.DrillBox drillbox - - - + - 25*3 - 60*4 - 5:25:10 +25*3 +60*4 +5:25:10 - 3 - 1.0 - 0.5 - - - 3.0 - 0.0 - false - true - 100 - - - - +3 +1.0 +0.5 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 1.0 - 1.0 - 0.0 - 3.0 - 0.0 - 3.0 - 1.0 - - - 50.0 - 2.0 - 30.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - 6.5 - 3.0 - 0.125 - 2 - - - - - +1.0 +0.0 +1.0 +0.0 +3.0 +0.0 +3.0 +1.0 + +50.0 +2.0 +30.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + +6.5 +3.0 +0.125 +2 + + + + + - - - - + + + + - - - ./DrillBox-thumb.jpg - + + + + + + + + + + + + + + + +8.0 +4.0 +0.1 + + +./DrillBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.DrillStand.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.DrillStand.inx index 1fcad76..a6f3e94 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.DrillStand.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.DrillStand.inx @@ -3,51 +3,49 @@ DrillStand info.festi.boxes.py.DrillStand drillstand - - - 25*6 - 10:20:30 - 25:40:60 - 15.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +25*6 +10:20:30 +25:40:60 +15.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 60 - 1.0 - 1.0 - 3.0 - - - 90.0 - - - - - +0.1 + +60 +0.0 +1.0 +1.0 +3.0 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./DrillStand-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./DrillStand-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.ElectronicsBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.ElectronicsBox.inx index fc1b89b..d32a12c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.ElectronicsBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.ElectronicsBox.inx @@ -3,51 +3,49 @@ ElectronicsBox info.festi.boxes.py.ElectronicsBox electronicsbox - - - 100.0 - 100.0 - 100.0 - true - 25.0 - 2.0 - 3.0 - 3.0 - true - 7.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +100.0 +true +25.0 +2.0 +3.0 +3.0 +true +7.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./ElectronicsBox-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./ElectronicsBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.EuroRackSkiff.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.EuroRackSkiff.inx index e3672cb..41a239e 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.EuroRackSkiff.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.EuroRackSkiff.inx @@ -3,43 +3,41 @@ EuroRackSkiff info.festi.boxes.py.EuroRackSkiff eurorackskiff - - - 100.0 - 84 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +84 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./EuroRackSkiff-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./EuroRackSkiff-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.FanHole.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.FanHole.inx index ac0dee9..2b51624 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.FanHole.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.FanHole.inx @@ -3,35 +3,33 @@ FanHole info.festi.boxes.py.FanHole fanhole - - - 80 - 3 - 5 - 10 - 0.2 - - - - +80 +3 +5 +10 +0.2 + + + + - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./FanHole-thumb.jpg - +0.1 + + +./FanHole-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.FilamentSpool.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.FilamentSpool.inx new file mode 100644 index 0000000..3bfbc7c --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.FilamentSpool.inx @@ -0,0 +1,58 @@ + + +FilamentSpool +info.festi.boxes.py.FilamentSpool +filamentspool +48 +200.0 +100.0 +50.0 +8 +1.0 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./FilamentSpool-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.FillTest.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.FillTest.inx index 8604d7d..128bd93 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.FillTest.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.FillTest.inx @@ -3,50 +3,47 @@ FillTest info.festi.boxes.py.FillTest filltest - - - 320 - 220 - - - 3.0 - 0.0 - false - true - 100 - - - - +320 +220 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 50 - - - - - - - +0.1 + +50 + + + + + + + - 3.0 - 0.5 - - - - - - +3.0 +0.5 + + + + + + - 1000 - 4.0 - 4.0 - - - ./FillTest-thumb.jpg - +1000 +4.0 +4.0 + + +./FillTest-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBook.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBook.inx new file mode 100644 index 0000000..fc9f611 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBook.inx @@ -0,0 +1,62 @@ + + +FlexBook +info.festi.boxes.py.FlexBook +flexbook +75.0 +35.0 +125.0 +8 +false + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.05 +1.0 +0.5 +5.0 + + +./FlexBook-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox.inx index ad9a664..ca7655d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox.inx @@ -3,53 +3,50 @@ FlexBox info.festi.boxes.py.FlexBox flexbox - - - 100.0 - 100.0 - 100.0 - true - 15 - 8 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +100.0 +true +15 +8 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.05 - 1.0 - 0.5 - 5.0 - - - ./FlexBox-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.05 +1.0 +0.5 +5.0 + + +./FlexBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox2.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox2.inx index 93fe79f..bb0cbc3 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox2.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox2.inx @@ -3,53 +3,50 @@ FlexBox2 info.festi.boxes.py.FlexBox2 flexbox2 - - - 100.0 - 100.0 - 100.0 - true - 15 - 8 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +100.0 +true +15 +8 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.05 - 1.0 - 0.5 - 5.0 - - - ./FlexBox2-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.05 +1.0 +0.5 +5.0 + + +./FlexBox2-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox3.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox3.inx index 5fc1a9e..24a7004 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox3.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox3.inx @@ -3,54 +3,51 @@ FlexBox3 info.festi.boxes.py.FlexBox3 flexbox3 - - - 100.0 - 100.0 - true - 100.0 - 10.0 - 10.0 - 1.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +true +100.0 +10.0 +10.0 +1.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 1.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.05 - 1.0 - 0.5 - 5.0 - - - ./FlexBox3-thumb.jpg - +1.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.05 +1.0 +0.5 +5.0 + + +./FlexBox3-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox4.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox4.inx index 922f53e..ef864ba 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox4.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox4.inx @@ -3,53 +3,50 @@ FlexBox4 info.festi.boxes.py.FlexBox4 flexbox4 - - - 100.0 - 100.0 - 100.0 - true - 15 - 8 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +100.0 +true +15 +8 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.05 - 1.0 - 0.5 - 5.0 - - - ./FlexBox4-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.05 +1.0 +0.5 +5.0 + + +./FlexBox4-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox5.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox5.inx index 3959bd0..d846d7c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox5.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexBox5.inx @@ -3,53 +3,50 @@ FlexBox5 info.festi.boxes.py.FlexBox5 flexbox5 - - - 100.0 - 100.0 - true - 60 - 60 - 8 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +true +60 +60 +8 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.05 - 1.0 - 0.5 - 5.0 - - - ./FlexBox5-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.05 +1.0 +0.5 +5.0 + + +./FlexBox5-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexTest.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexTest.inx index fabf5d8..92e6df7 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexTest.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexTest.inx @@ -3,33 +3,30 @@ FlexTest info.festi.boxes.py.FlexTest flextest - - - 100.0 - 100.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 1.05 - 1.0 - 0.5 - 5.0 - - - ./FlexTest-thumb.jpg - +0.1 + +1.05 +1.0 +0.5 +5.0 + + +./FlexTest-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexTest2.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexTest2.inx index d43e759..41c8868 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.FlexTest2.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.FlexTest2.inx @@ -3,28 +3,26 @@ FlexTest2 info.festi.boxes.py.FlexTest2 flextest2 - - - 100.0 - 100.0 - 1 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +1 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./FlexTest2-thumb.jpg - +0.1 + + +./FlexTest2-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Folder.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Folder.inx index 68c4537..905dbf0 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Folder.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Folder.inx @@ -3,35 +3,32 @@ Folder info.festi.boxes.py.Folder folder - - - 100.0 - 100.0 - 20 - 10.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +20 +10.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 1.05 - 1.0 - 0.5 - 5.0 - - - ./Folder-thumb.jpg - +0.1 + +1.05 +1.0 +0.5 +5.0 + + +./Folder-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.FrontPanel.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.FrontPanel.inx new file mode 100644 index 0000000..2da1a07 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.FrontPanel.inx @@ -0,0 +1,35 @@ + + +FrontPanel +info.festi.boxes.py.FrontPanel +frontpanel +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + + +./FrontPanel-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.GearBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.GearBox.inx index d6e7ebe..375fa88 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.GearBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.GearBox.inx @@ -3,46 +3,44 @@ GearBox info.festi.boxes.py.GearBox gearbox - - - 8 - 20 - 3 - 6.0 - 4 - - - 3.0 - 0.0 - false - true - 100 - - - - +8 +20 +3 +6.0 +4 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./GearBox-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./GearBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Gears.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Gears.inx index 9ed0829..8a358a5 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Gears.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Gears.inx @@ -3,34 +3,32 @@ Gears info.festi.boxes.py.Gears gears - - - 12 - 6.0 - 75 - 32 - 0.0 - 0 - 2 - 20 - 20 - - - 3.0 - 0.0 - false - true - 100 - - - - +12 +6.0 +75 +32 +0.0 +0 +2 +20 +20 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./Gears-thumb.jpg - +0.1 + + +./Gears-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.GridfinityBase.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.GridfinityBase.inx new file mode 100644 index 0000000..eedbfdc --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.GridfinityBase.inx @@ -0,0 +1,82 @@ + + +GridfinityBase +info.festi.boxes.py.GridfinityBase +gridfinitybase +3 +2 +21 +0.5 + + + + + + +42 +38 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +4.0 +0.0 +4.0 +1.0 + + + + + + + + + + + + + + + +8.0 +4.0 +0.1 + + +./GridfinityBase-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.GridfinityTrayLayout.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.GridfinityTrayLayout.inx new file mode 100644 index 0000000..b71c545 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.GridfinityTrayLayout.inx @@ -0,0 +1,77 @@ + + +GridfinityTrayLayout +info.festi.boxes.py.GridfinityTrayLayout +gridfinitytraylayout +50 +0 +3 +2 +5 +3 +0.75 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + + + + + + + + + + + + + + +8.0 +4.0 +0.1 + + +./GridfinityTrayLayout-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.HalfBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.HalfBox.inx index 8ae3fb4..f84ff7c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.HalfBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.HalfBox.inx @@ -3,65 +3,62 @@ HalfBox info.festi.boxes.py.HalfBox halfbox - - - 100 - 50:50 - 100 - false - 25.0 - false - false - - - 3.0 - 0.0 - false - true - 100 - - - - +100 +50:50 +100 +false +25.0 +false +false + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 6.5 - 3.0 - 0.125 - 2 - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +6.5 +3.0 +0.125 +2 + + + + + - - - - + + + + - - - ./HalfBox-thumb.jpg - + + +./HalfBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.HeartBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.HeartBox.inx index 4607e84..c24482a 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.HeartBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.HeartBox.inx @@ -3,54 +3,51 @@ HeartBox info.festi.boxes.py.HeartBox heartbox - - - 150 - 50 - - - - +150 +50 + + + + - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 1.0 - 0.0 - 1.0 - 1.0 - - - 1.05 - 1.0 - 0.5 - 5.0 - - - ./HeartBox-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +1.0 +0.0 +1.0 +1.0 + +1.05 +1.0 +0.5 +5.0 + + +./HeartBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.HingeBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.HingeBox.inx index 7ff5da2..008120c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.HingeBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.HingeBox.inx @@ -3,59 +3,56 @@ HingeBox info.festi.boxes.py.HingeBox hingebox - - - 100.0 - 100.0 - 100.0 - true - 20.0 - 0.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +100.0 +true +20.0 +0.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 3.2 - 5 - 2 - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +3.2 +5 +2 + + + - 1.5 - 0.05 - 2.0 - - - ./HingeBox-thumb.jpg - +1.5 +0.05 +2.0 + + +./HingeBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.HolePattern.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.HolePattern.inx index 4455983..2dc74ca 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.HolePattern.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.HolePattern.inx @@ -3,57 +3,54 @@ HolePattern info.festi.boxes.py.HolePattern holepattern - - - 100.0 - 100.0 - - - - - - +100.0 +100.0 + + + + + + - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 50 - - - - - - - +0.1 + +50 + + + + + + + - 3.0 - 0.5 - - - - - - +3.0 +0.5 + + + + + + - 1000 - 4.0 - 4.0 - - - ./HolePattern-thumb.jpg - +1000 +4.0 +4.0 + + +./HolePattern-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Hook.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Hook.inx index 582552a..d18f81f 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Hook.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Hook.inx @@ -3,46 +3,44 @@ Hook info.festi.boxes.py.Hook hook - - - 40.0 - 40.0 - 40.0 - 20.0 - 45.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +40.0 +40.0 +40.0 +20.0 +45.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.5 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./Hook-thumb.jpg - +0.5 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./Hook-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.IntegratedHingeBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.IntegratedHingeBox.inx index 61d123e..bbd3a37 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.IntegratedHingeBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.IntegratedHingeBox.inx @@ -3,53 +3,50 @@ IntegratedHingeBox info.festi.boxes.py.IntegratedHingeBox integratedhingebox - - - 100.0 - 100.0 - 100.0 - true - 20.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +100.0 +true +20.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - false - false - 1.0 - 2.0 - 0.1 - - - ./IntegratedHingeBox-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +false +false +1.0 +2.0 +0.1 + + +./IntegratedHingeBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.JointPanel.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.JointPanel.inx index fc87c5a..01a6bcf 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.JointPanel.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.JointPanel.inx @@ -3,34 +3,31 @@ JointPanel info.festi.boxes.py.JointPanel jointpanel - - - 400/2 - 400/3 - false - - - 3.0 - 0.0 - false - true - 100 - - - - +400/2 +400/3 +false + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 50 - 0.5 - 0.1 - 1 - - - ./JointPanel-thumb.jpg - +0.1 + +50 +0.5 +0.1 +1 + + +./JointPanel-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.KeyHolder.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.KeyHolder.inx new file mode 100644 index 0000000..f501236 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.KeyHolder.inx @@ -0,0 +1,76 @@ + + +KeyHolder +info.festi.boxes.py.KeyHolder +keyholder +7 +20 +5 +20 +10 +5 +30 +false + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +0.0 +0.0 +1.0 +0.0 +1.0 +0.0 +1.0 +1.0 + +6.5 +3.0 +0.125 +2 + + + + + + + + + + + + + +./KeyHolder-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Keypad.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Keypad.inx index 16bb06f..fa10578 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Keypad.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Keypad.inx @@ -3,50 +3,48 @@ Keypad info.festi.boxes.py.Keypad keypad - - - 30 - 1.5 - false - 1.5 - true - false - false - castle - 4x3 - - - 3.0 - 0.0 - false - true - 100 - - - - +30 +1.5 +false +1.5 +true +false +false +castle +4x3 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 1.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./Keypad-thumb.jpg - +1.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./Keypad-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.LBeam.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.LBeam.inx index 4449a25..040bd23 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.LBeam.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.LBeam.inx @@ -3,45 +3,43 @@ LBeam info.festi.boxes.py.LBeam lbeam - - - 100.0 - 100.0 - 100.0 - true - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +100.0 +true + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./LBeam-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./LBeam-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.LaptopStand.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.LaptopStand.inx index 99e1705..952ef64 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.LaptopStand.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.LaptopStand.inx @@ -3,30 +3,28 @@ LaptopStand info.festi.boxes.py.LaptopStand laptopstand - - - 250 - 10 - 15 - 10 - 10 - - - 3.0 - 0.0 - false - true - 100 - - - - +250 +10 +15 +10 +10 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./LaptopStand-thumb.jpg - +0.1 + + +./LaptopStand-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.LaserClamp.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.LaserClamp.inx index 492b85c..1871d9d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.LaserClamp.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.LaserClamp.inx @@ -3,44 +3,42 @@ LaserClamp info.festi.boxes.py.LaserClamp laserclamp - - - 25.0 - 50.0 - 0.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +25.0 +50.0 +0.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./LaserClamp-thumb.jpg - +0.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./LaserClamp-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.LaserHoldfast.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.LaserHoldfast.inx index bea0eae..0950754 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.LaserHoldfast.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.LaserHoldfast.inx @@ -3,29 +3,27 @@ LaserHoldfast info.festi.boxes.py.LaserHoldfast laserholdfast - - - 25 - 40 - 5.0 - 5.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +25 +40 +5.0 +5.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./LaserHoldfast-thumb.jpg - +0.1 + + +./LaserHoldfast-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.MagazinFile.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.MagazinFile.inx deleted file mode 100644 index 99f1dc5..0000000 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.MagazinFile.inx +++ /dev/null @@ -1,80 +0,0 @@ - - -MagazinFile -info.festi.boxes.py.MagazinFile -magazinfile - - - 100 - 200 - 300 - 0 - false - - - - - - - 3.0 - 0.0 - false - true - 100 - - - - - - 0.1 - - - 90.0 - - - - - - - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 6.5 - 3.0 - 0.0 - 1 - - - - - - - - - - - - - - ./MagazinFile-thumb.jpg - - - - - all - - - - - - - - \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.MagazineFile.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.MagazineFile.inx new file mode 100644 index 0000000..8958d32 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.MagazineFile.inx @@ -0,0 +1,77 @@ + + +MagazineFile +info.festi.boxes.py.MagazineFile +magazinefile +100 +200 +300 +0 +false + + + + + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +6.5 +3.0 +0.0 +1 + + + + + + + + + + + + + +./MagazineFile-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.MakitaPowerSupply.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.MakitaPowerSupply.inx index 0775e64..72eefd5 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.MakitaPowerSupply.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.MakitaPowerSupply.inx @@ -3,43 +3,41 @@ MakitaPowerSupply info.festi.boxes.py.MakitaPowerSupply makitapowersupply - - - 8.0 - 6.3 - - - 3.0 - 0.0 - false - true - 100 - - - - +8.0 +6.3 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./MakitaPowerSupply-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./MakitaPowerSupply-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.NemaMount.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.NemaMount.inx index d301163..d07002f 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.NemaMount.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.NemaMount.inx @@ -3,52 +3,50 @@ NemaMount info.festi.boxes.py.NemaMount nemamount - - - - - - - - - - - - + + + + + + + + + + - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./NemaMount-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./NemaMount-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.NemaPattern.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.NemaPattern.inx index 6071797..01c139a 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.NemaPattern.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.NemaPattern.inx @@ -3,53 +3,51 @@ NemaPattern info.festi.boxes.py.NemaPattern nemapattern - - - - - - - - - - - - + + + + + + + + + + - 0.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +0.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./NemaPattern-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./NemaPattern-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.NotesHolder.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.NotesHolder.inx index a49c58c..d595072 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.NotesHolder.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.NotesHolder.inx @@ -3,57 +3,57 @@ NotesHolder info.festi.boxes.py.NotesHolder notesholder - - - 78 - 78 - 35 +78*1 +78 +35 + - 40 - - - 3.0 - 0.0 - false - true - 100 - - - - +40 +false + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 1.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - ./NotesHolder-thumb.jpg - +1.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + + +./NotesHolder-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.OpenBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.OpenBox.inx index 993a391..0e48346 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.OpenBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.OpenBox.inx @@ -3,49 +3,47 @@ OpenBox info.festi.boxes.py.OpenBox openbox - - - 100.0 - 100.0 - 100.0 - true +100.0 +100.0 +100.0 +true - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./OpenBox-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./OpenBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.OrganPipe.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.OrganPipe.inx index 1a6aac7..e45b597 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.OrganPipe.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.OrganPipe.inx @@ -3,68 +3,66 @@ OrganPipe info.festi.boxes.py.OrganPipe organpipe - - - - - - - - - - - - - - - + + + + + + + + + + + + + - 2 - 2.0 - 0.25 - 0.3 - 0 - 588.4 - - - - - +2 +2.0 +0.25 +0.3 +0 +588.4 + + + + + - false - - - 3.0 - 0.0 - false - true - 100 - - - - +false + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 1.0 - 1.0 - 0.0 - 3.0 - 0.0 - 3.0 - 1.0 - - - ./OrganPipe-thumb.jpg - +1.0 +0.0 +1.0 +0.0 +3.0 +0.0 +3.0 +1.0 + + +./OrganPipe-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.OttoBody.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.OttoBody.inx index 629bf93..172b1c2 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.OttoBody.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.OttoBody.inx @@ -3,46 +3,44 @@ OttoBody info.festi.boxes.py.OttoBody ottobody - - - 3.0 - 0.0 - false - true - 100 - - - - +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - false - false - 1.0 - 2.0 - 0.1 - - - ./OttoBody-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +false +false +1.0 +2.0 +0.1 + + +./OttoBody-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.OttoLegs.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.OttoLegs.inx index b4e4f18..2bd25f0 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.OttoLegs.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.OttoLegs.inx @@ -3,44 +3,42 @@ OttoLegs info.festi.boxes.py.OttoLegs ottolegs - - - 3.0 - 2.6 - 34.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +3.0 +2.6 +34.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 1.0 - 1.0 - 0.0 - 1.0 - 0.0 - 1.0 - 1.0 - - - ./OttoLegs-thumb.jpg - +1.0 +0.0 +1.0 +0.0 +1.0 +0.0 +1.0 +1.0 + + +./OttoLegs-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.OttoSoles.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.OttoSoles.inx index db3713c..2e7350c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.OttoSoles.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.OttoSoles.inx @@ -3,30 +3,28 @@ OttoSoles info.festi.boxes.py.OttoSoles ottosoles - - - 58.0 - 38.0 - 4.0 - 5.0 - 2 - - - 3.0 - 0.0 - false - true - 100 - - - - +58.0 +38.0 +4.0 +5.0 +2 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./OttoSoles-thumb.jpg - +0.1 + + +./OttoSoles-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.PaintStorage.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.PaintStorage.inx index ec24629..6f322c3 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.PaintStorage.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.PaintStorage.inx @@ -3,54 +3,54 @@ PaintStorage info.festi.boxes.py.PaintStorage paintstorage - - - 100 - 300 - 50 - 30 - 10 - false - false - - - 3.0 - 0.0 - false - true - 100 - - - - +100 +300 +50 +30 +10 +false +false +false +false + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - ./PaintStorage-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + + +./PaintStorage-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.PaperBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.PaperBox.inx index f284bfe..0abceba 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.PaperBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.PaperBox.inx @@ -3,40 +3,38 @@ PaperBox info.festi.boxes.py.PaperBox paperbox - - - 100.0 - 100.0 - 100.0 - - - - +100.0 +100.0 +100.0 + + + + - 15 - 7 - 20 - 0 - 1.5 - 0.07982998571223732 - 15 - - - 3.0 - 0.0 - false - true - 100 - - - - +15 +7 +20 +0 +1.5 +0.07982998571223732 +15 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./PaperBox-thumb.jpg - +0.1 + + +./PaperBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.PhoneHolder.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.PhoneHolder.inx index ea68e21..6d12c7b 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.PhoneHolder.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.PhoneHolder.inx @@ -3,48 +3,46 @@ PhoneHolder info.festi.boxes.py.PhoneHolder phoneholder - - - 142 - 73 - 11 - 25 - 30 - 76 - 16 - - - 3.0 - 0.0 - false - true - 100 - - - - +142 +73 +11 +25 +30 +76 +16 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./PhoneHolder-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./PhoneHolder-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.PirateChest.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.PirateChest.inx new file mode 100644 index 0000000..f5ef968 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.PirateChest.inx @@ -0,0 +1,68 @@ + + +PirateChest +info.festi.boxes.py.PirateChest +piratechest +100.0 +100.0 +100.0 +true +5 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +1.0 +0.0 +1.0 +1.0 + +0 +false +0.5 + + + + +2.0 +0 +1 + + +./PirateChest-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.PizzaShovel.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.PizzaShovel.inx new file mode 100644 index 0000000..4272a4c --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.PizzaShovel.inx @@ -0,0 +1,66 @@ + + +PizzaShovel +info.festi.boxes.py.PizzaShovel +pizzashovel +382 +400 +250.0 +30.0 +3.0 +2.0 +3.0 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +40.0 +75.0 +30:30:30 +true +10.0 +0.0 + + +./PizzaShovel-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Planetary.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Planetary.inx index 86e809e..2e7b53b 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Planetary.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Planetary.inx @@ -3,31 +3,29 @@ Planetary info.festi.boxes.py.Planetary planetary - - - 8 - 20 - 0 - 0 - 3 - 6.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +8 +20 +0 +0 +3 +6.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./Planetary-thumb.jpg - +0.1 + + +./Planetary-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Planetary2.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Planetary2.inx index 2d07a92..7dce4cc 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Planetary2.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Planetary2.inx @@ -3,61 +3,59 @@ Planetary2 info.festi.boxes.py.Planetary2 planetary2 - - - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - 20 - 20 - 0 - 1 - 1.0 - 6.0 - 2.4 - 4.0 - 3.1 - - - 3.0 - 0.0 - false - true - 100 - - - - +20 +20 +0 +1 +1.0 +6.0 +2.4 +4.0 +3.1 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./Planetary2-thumb.jpg - +0.1 + + +./Planetary2-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Platonic.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Platonic.inx index 6357648..f9770f6 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Platonic.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Platonic.inx @@ -3,50 +3,48 @@ Platonic info.festi.boxes.py.Platonic platonic - - - 60 - true - - - - - - +60 +true + + + + + + - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./Platonic-thumb.jpg - +0.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./Platonic-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.PoleHook.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.PoleHook.inx index 65b9003..bc34f6c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.PoleHook.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.PoleHook.inx @@ -3,46 +3,44 @@ PoleHook info.festi.boxes.py.PoleHook polehook - - - 50.0 - 7.8 - 13.0 - 5.5 - 4.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +50.0 +7.8 +13.0 +5.5 +4.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./PoleHook-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./PoleHook-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Pulley.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Pulley.inx index 8210590..510db70 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Pulley.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Pulley.inx @@ -3,46 +3,44 @@ Pulley info.festi.boxes.py.Pulley pulley - - - 6.0 - - - - - - - - - - - - - - - +6.0 + + + + + + + + + + + + + + + - 20 - 5 - false - 0 - - - 3.0 - 0.0 - false - true - 100 - - - - +20 +5 +false +0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./Pulley-thumb.jpg - +0.1 + + +./Pulley-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Rack10Box.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Rack10Box.inx index db6dc08..ccbae38 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Rack10Box.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Rack10Box.inx @@ -3,63 +3,61 @@ Rack10Box info.festi.boxes.py.Rack10Box rack10box - - - 100.0 - - - - - - - - - - - - - - - - - +100.0 + + + + + + + + + + + + + + + + + - 25.0 - 2.0 - 3.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +25.0 +2.0 +3.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.5 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./Rack10Box-thumb.jpg - +0.5 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./Rack10Box-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Rack19Box.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Rack19Box.inx index 5bb9c90..ab00371 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Rack19Box.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Rack19Box.inx @@ -3,63 +3,61 @@ Rack19Box info.festi.boxes.py.Rack19Box rack19box - - - 100.0 - - - - - - - - - - - - - - - - - +100.0 + + + + + + + + + + + + + + + + + - 25.0 - 2.0 - 3.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +25.0 +2.0 +3.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.5 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./Rack19Box-thumb.jpg - +0.5 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./Rack19Box-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Rack19HalfWidth.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Rack19HalfWidth.inx index 9afb4ff..61b26f3 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Rack19HalfWidth.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Rack19HalfWidth.inx @@ -3,29 +3,27 @@ Rack19HalfWidth info.festi.boxes.py.Rack19HalfWidth rack19halfwidth - - - 1 - xxmpwx - 20 - 124 - - - 3.0 - 0.0 - false - true - 100 - - - - +1 +xxmpwx +20 +124 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./Rack19HalfWidth-thumb.jpg - +0.1 + + +./Rack19HalfWidth-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.RackBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.RackBox.inx index ed2bcc4..c045bb8 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.RackBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.RackBox.inx @@ -3,50 +3,48 @@ RackBox info.festi.boxes.py.RackBox rackbox - - - 100.0 - 100.0 - 100.0 - true - 25.0 - 2.0 - 3.0 - 4.0 - 7.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +100.0 +true +25.0 +2.0 +3.0 +4.0 +7.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 1.2 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./RackBox-thumb.jpg - +1.2 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./RackBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.RectangularWall.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.RectangularWall.inx index 3cc4177..b317ad2 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.RectangularWall.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.RectangularWall.inx @@ -3,10 +3,8 @@ RectangularWall info.festi.boxes.py.RectangularWall rectangularwall - - - 100 - 100 +100 +100 @@ -163,125 +161,118 @@ - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 3.2 - 5 - 2 - - - +0.1 + +3.2 +5 +2 + + + - 1.5 - 0.05 - 2.0 - - - 5.0 - 0.1 - 3.0 - - - 50 - 1.5 - 0.2 - 3 - - - 90.0 - - - - - +1.5 +0.05 +2.0 + +5.0 +0.1 +3.0 + +50 +1.5 +0.2 +3 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 20.0 - 0.0 - 3.0 - 20.0 - - - true - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +20.0 +0.0 +3.0 +20.0 + +true + + + - 0.3 - - - 0 - false - 0.5 - - - +0.3 + +0 +false +0.5 + + + - 2.0 - 0 - 1 - - - false - false - 1.0 - 2.0 - 0.1 - - - 90.0 - 0 - true - - - - - +2.0 +0 +1 + +false +false +1.0 +2.0 +0.1 + +90.0 +0 +true + + + + + - - - - - + + + + + - 2.0 - 1.0 - 0.0 - 3.0 - 0.05 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - ./RectangularWall-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +3.0 +0.05 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + + +./RectangularWall-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.RegularBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.RegularBox.inx index 2cdf68d..e8d7473 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.RegularBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.RegularBox.inx @@ -3,55 +3,64 @@ RegularBox info.festi.boxes.py.RegularBox regularbox - - - 100.0 - true - 50.0 - 5 - - - - - - - - +100.0 +true +50.0 +50.0 +5 + + + + + + + + + - 1.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +1.0 + + + + + + + + - 0.1 - - - 90.0 - - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 1.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./RegularBox-thumb.jpg - +0.1 + +90.0 + + + + + + +1.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./RegularBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.RegularStarBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.RegularStarBox.inx index 5e67d6b..26a3cba 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.RegularStarBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.RegularStarBox.inx @@ -3,49 +3,47 @@ RegularStarBox info.festi.boxes.py.RegularStarBox regularstarbox - - - 100.0 - true - 50.0 - - - - +100.0 +true +50.0 + + + + - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./RegularStarBox-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./RegularStarBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.RobotArm.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.RobotArm.inx index 6949094..066e386 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.RobotArm.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.RobotArm.inx @@ -3,116 +3,114 @@ RobotArm info.festi.boxes.py.RobotArm robotarm - - - - - - - - - + + + + + + + - - + + - - + + - 50.0 - - - - - - - +50.0 + + + + + + + - - + + - - + + - 50.0 - - - - - - - +50.0 + + + + + + + - - + + - - + + - 50.0 - - - - - - - +50.0 + + + + + + + - - + + - - + + - 50.0 - - - - - - - +50.0 + + + + + + + - - + + - - + + - 50.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +50.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./RobotArm-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./RobotArm-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.RollHolder.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.RollHolder.inx new file mode 100644 index 0000000..791ff73 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.RollHolder.inx @@ -0,0 +1,58 @@ + + +RollHolder +info.festi.boxes.py.RollHolder +rollholder +275 +120 +80 +25 +4 +true + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./RollHolder-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Rotary.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Rotary.inx index 088eb25..ba8291b 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Rotary.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Rotary.inx @@ -3,47 +3,45 @@ Rotary info.festi.boxes.py.Rotary rotary - - - 72.0 - 5.0 - 6.0 - 8.0 - 32.0 - 7.1 - - - 3.0 - 0.0 - false - true - 100 - - - - +72.0 +5.0 +6.0 +8.0 +32.0 +7.1 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./Rotary-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./Rotary-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.RoundedBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.RoundedBox.inx index 88c0c96..e94fb4f 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.RoundedBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.RoundedBox.inx @@ -3,74 +3,70 @@ RoundedBox info.festi.boxes.py.RoundedBox roundedbox - - - 100.0 - 100.0 - 100.0 - true - 15 - - - - - +100.0 +100.0 +100.0 +true +15 + + + + + - - - - + + + + - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 50 - 1.5 - 0.2 - 3 - - - 1.05 - 1.0 - 0.5 - 5.0 - - - ./RoundedBox-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +50 +1.5 +0.2 +3 + +1.05 +1.0 +0.5 +5.0 + + +./RoundedBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.RoyalGame.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.RoyalGame.inx index b6d5775..43352cd 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.RoyalGame.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.RoyalGame.inx @@ -3,42 +3,40 @@ RoyalGame info.festi.boxes.py.RoyalGame royalgame - - - 200 - - - 3.0 - 0.0 - false - true - 100 - - - - +200 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./RoyalGame-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./RoyalGame-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.SBCMicroRack.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.SBCMicroRack.inx index bac3d01..4ad25a8 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.SBCMicroRack.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.SBCMicroRack.inx @@ -3,61 +3,59 @@ SBCMicroRack info.festi.boxes.py.SBCMicroRack sbcmicrorack - - - 56 - 85 - 5 - 3 - 3 - 28 - 3.5 - 58 - 49 - 2.75 - 18 - 53 - false - false - - - 3.0 - 0.0 - false - true - 100 - - - - +56 +85 +5 +3 +3 +28 +3.5 +58 +49 +2.75 +18 +53 +false +false + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - ./SBCMicroRack-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + + +./SBCMicroRack-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.SevenSegmentClock.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.SevenSegmentClock.inx new file mode 100644 index 0000000..8866b41 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.SevenSegmentClock.inx @@ -0,0 +1,55 @@ + + +SevenSegmentClock +info.festi.boxes.py.SevenSegmentClock +sevensegmentclock +100.0 +20.0 +false + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./SevenSegmentClock-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.SevenSegmentPattern.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.SevenSegmentPattern.inx new file mode 100644 index 0000000..1949682 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.SevenSegmentPattern.inx @@ -0,0 +1,54 @@ + + +SevenSegmentPattern +info.festi.boxes.py.SevenSegmentPattern +sevensegmentpattern +100.0 +20.0 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./SevenSegmentPattern-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Shadowbox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Shadowbox.inx new file mode 100644 index 0000000..9056331 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Shadowbox.inx @@ -0,0 +1,64 @@ + + +Shadowbox +info.festi.boxes.py.Shadowbox +shadowbox +200 +260 +7 +10 +10 +20 +true + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +10 +1.5 +0.1 +1 + + +./Shadowbox-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Shoe.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Shoe.inx new file mode 100644 index 0000000..fb13e4d --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Shoe.inx @@ -0,0 +1,64 @@ + + +Shoe +info.festi.boxes.py.Shoe +shoe +65 +175 +100 +35 +20 +75 +30 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.05 +1.0 +0.5 +5.0 + + +./Shoe-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.ShutterBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.ShutterBox.inx index 4cfe83f..6c61b39 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.ShutterBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.ShutterBox.inx @@ -3,55 +3,52 @@ ShutterBox info.festi.boxes.py.ShutterBox shutterbox - - - 150 - 100 - 100 - 40.0 - - - +150 +100 +100 +40.0 + + + - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.5 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.05 - 2.0 - 0.75 - 5.0 - - - ./ShutterBox-thumb.jpg - +0.5 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.05 +2.0 +0.75 +5.0 + + +./ShutterBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.SideDoorHousing.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.SideDoorHousing.inx index 8969a26..2ad8dce 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.SideDoorHousing.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.SideDoorHousing.inx @@ -3,57 +3,55 @@ SideDoorHousing info.festi.boxes.py.SideDoorHousing sidedoorhousing - - - 100 - 100 - 100 +100 +100 +100 - true - - - 3.0 - 0.0 - false - true - 100 - - - - +true + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.5 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - ./SideDoorHousing-thumb.jpg - +0.5 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + + +./SideDoorHousing-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Silverware.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Silverware.inx index 8caa05f..840a437 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Silverware.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Silverware.inx @@ -3,31 +3,29 @@ Silverware info.festi.boxes.py.Silverware silverware - - - 250 - 154 - 120 - 30 - 150 - 120 - - - 3.0 - 0.0 - false - true - 100 - - - - +250 +154 +120 +30 +150 +120 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./Silverware-thumb.jpg - +0.1 + + +./Silverware-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.SkadisBoard.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.SkadisBoard.inx new file mode 100644 index 0000000..c06bcf8 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.SkadisBoard.inx @@ -0,0 +1,38 @@ + + +SkadisBoard +info.festi.boxes.py.SkadisBoard +skadisboard +17 +27 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + + +./SkadisBoard-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.SlantedTray.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.SlantedTray.inx new file mode 100644 index 0000000..d78ded8 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.SlantedTray.inx @@ -0,0 +1,57 @@ + + +SlantedTray +info.festi.boxes.py.SlantedTray +slantedtray +40*3 +40.0 +40.0 +false +0.3 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./SlantedTray-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.SlidingDrawer.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.SlidingDrawer.inx index e3cd0ec..7e8eac0 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.SlidingDrawer.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.SlidingDrawer.inx @@ -3,61 +3,58 @@ SlidingDrawer info.festi.boxes.py.SlidingDrawer slidingdrawer - - - 60 - 100 - 30 - true - 0.15 - - - 3.0 - 0.0 - false - true - 100 - - - - +60 +100 +30 +true +0.15 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 120 - 0.1 - false - false - 0.3 - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +120 +0.1 +false +false +0.3 + + + + + - 30 - 0.4 - - - ./SlidingDrawer-thumb.jpg - +30 +0.4 + + +./SlidingDrawer-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.SmallPartsTray.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.SmallPartsTray.inx new file mode 100644 index 0000000..689d0c5 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.SmallPartsTray.inx @@ -0,0 +1,78 @@ + + +SmallPartsTray +info.festi.boxes.py.SmallPartsTray +smallpartstray +50*3 +100 +30 +true +45.0 +0.5 +true +true + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + + + + + + + + + + + + + + +8.0 +4.0 +0.1 + + +./SmallPartsTray-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.SmallPartsTray2.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.SmallPartsTray2.inx new file mode 100644 index 0000000..e745971 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.SmallPartsTray2.inx @@ -0,0 +1,162 @@ + + +SmallPartsTray2 +info.festi.boxes.py.SmallPartsTray2 +smallpartstray2 +30 +50*3 +50*3 +0.0 +true +0.0 +0.0 +false + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +1.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + +0 +false +0.5 + + + + +2.0 +0 +1 + +3.2 +5 +2 + + + + +1.5 +0.05 +2.0 + +90.0 +0 +true + + + + + + + + + + + + +2.0 +0.0 +1.0 +0.0 +3.0 +0.05 +2.0 +1.0 + +5.0 +0.1 +3.0 + +50.0 +2.0 +30.0 +1.0 + +6.5 +3.0 +0.125 +2 + + + + + + + + + + + + +20.0 +75.0 +40:40 +true +10.0 +1.0 + + + + + + + + + + + + + + + +8.0 +4.0 +0.1 + + +./SmallPartsTray2-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.SpicesRack.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.SpicesRack.inx index b1570ae..335b830 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.SpicesRack.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.SpicesRack.inx @@ -3,48 +3,46 @@ SpicesRack info.festi.boxes.py.SpicesRack spicesrack - - - 55.0 - 60.0 - 10.0 - 5 - 6 - false - false - - - 3.0 - 0.0 - false - true - 100 - - - - +55.0 +60.0 +10.0 +5 +6 +false +false + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 1.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./SpicesRack-thumb.jpg - +1.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./SpicesRack-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Spool.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Spool.inx new file mode 100644 index 0000000..c145f21 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Spool.inx @@ -0,0 +1,59 @@ + + +Spool +info.festi.boxes.py.Spool +spool +100 +200.0 +80.0 +40.0 +8 +8 +0.0 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./Spool-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Stachel.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Stachel.inx index 076359a..f0c80c2 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.Stachel.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Stachel.inx @@ -3,28 +3,26 @@ Stachel info.festi.boxes.py.Stachel stachel - - - 115.0 - 25.0 - 7.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +115.0 +25.0 +7.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./Stachel-thumb.jpg - +0.1 + + +./Stachel-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.StorageRack.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.StorageRack.inx index 1471084..afa3102 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.StorageRack.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.StorageRack.inx @@ -3,13 +3,11 @@ StorageRack info.festi.boxes.py.StorageRack storagerack - - - 200 - 30 - 100.0 - 50*3 - true +200 +30 +100.0 +50*3 +true @@ -23,45 +21,45 @@ - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - ./StorageRack-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + + +./StorageRack-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.StorageShelf.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.StorageShelf.inx index 53eb8b2..0e8e7ed 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.StorageShelf.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.StorageShelf.inx @@ -3,12 +3,10 @@ StorageShelf info.festi.boxes.py.StorageShelf storageshelf - - - 100.0 - 50*3 - 50*3 - true +100.0 +50*3 +50*3 +true @@ -33,131 +31,125 @@ - 0.0 - false - - - 3.0 - 0.0 - false - true - 100 - - - - +0.0 +false + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.5 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - 0 - false - 0.5 - - - +0.5 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + +0 +false +0.5 + + + - 2.0 - 0 - 1 - - - 3.2 - 5 - 2 - - - +2.0 +0 +1 + +3.2 +5 +2 + + + - 1.5 - 0.05 - 2.0 - - - 90.0 - 0 - true - - - - - +1.5 +0.05 +2.0 + +90.0 +0 +true + + + + + - - - - - + + + + + - 2.0 - 1.0 - 0.0 - 3.0 - 0.05 - 2.0 - 1.0 - - - 5.0 - 0.1 - 3.0 - - - 50.0 - 2.0 - 30.0 - 1.0 - - - 6.5 - 3.0 - 0.125 - 2 - - - - - +2.0 +0.0 +1.0 +0.0 +3.0 +0.05 +2.0 +1.0 + +5.0 +0.1 +3.0 + +50.0 +2.0 +30.0 +1.0 + +6.5 +3.0 +0.125 +2 + + + + + - - - - + + + + - - - 20.0 - 75.0 - 40:40 - true - 10.0 - 1.0 - - - ./StorageShelf-thumb.jpg - + +20.0 +75.0 +40:40 +true +10.0 +1.0 + + +./StorageShelf-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.Tetris.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.Tetris.inx new file mode 100644 index 0000000..c91700a --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.Tetris.inx @@ -0,0 +1,60 @@ + + +Tetris +info.festi.boxes.py.Tetris +tetris +40.0 + + + + + + + + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./Tetris-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.TrafficLight.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.TrafficLight.inx index d0c5ea1..d91631a 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.TrafficLight.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.TrafficLight.inx @@ -3,47 +3,45 @@ TrafficLight info.festi.boxes.py.TrafficLight trafficlight - - - 100.0 - 3.5:6.5 - 100 - 50 - 3 - true - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +3.5:6.5 +100 +50 +3 +true + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./TrafficLight-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./TrafficLight-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.TrayInsert.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.TrayInsert.inx index f569bdc..dd160d7 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.TrayInsert.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.TrayInsert.inx @@ -3,29 +3,27 @@ TrayInsert info.festi.boxes.py.TrayInsert trayinsert - - - 50*3 - 50*3 - 100.0 - true - - - 3.0 - 0.0 - false - true - 100 - - - - +50*3 +50*3 +100.0 +true + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - ./TrayInsert-thumb.jpg - +0.1 + + +./TrayInsert-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.TriangleLamp.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.TriangleLamp.inx index d259dfe..7836fdc 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.TriangleLamp.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.TriangleLamp.inx @@ -3,47 +3,45 @@ TriangleLamp info.festi.boxes.py.TriangleLamp trianglelamp - - - 250 - 40 - 30 - 4 - 2 - false - - - 3.0 - 0.0 - false - true - 100 - - - - +250 +40 +30 +4 +2 +false + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 0.5 - 1.0 - 0.0 - 3.0 - 0.0 - 3.0 - 1.0 - - - ./TriangleLamp-thumb.jpg - +0.5 +0.0 +1.0 +0.0 +3.0 +0.0 +3.0 +1.0 + + +./TriangleLamp-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.TwoPiece.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.TwoPiece.inx index 8fad0d7..36cb1af 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.TwoPiece.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.TwoPiece.inx @@ -3,47 +3,45 @@ TwoPiece info.festi.boxes.py.TwoPiece twopiece - - - 100.0 - 100.0 - 100.0 - 0.0 - true - 0.15 - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +100.0 +0.0 +true +0.15 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./TwoPiece-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./TwoPiece-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.TypeTray.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.TypeTray.inx index 2d8ece6..4abe883 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.TypeTray.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.TypeTray.inx @@ -3,13 +3,11 @@ TypeTray info.festi.boxes.py.TypeTray typetray - - - 50*3 - 50*3 - 100.0 - 0.0 - true +50*3 +50*3 +100.0 +0.0 +true @@ -34,134 +32,170 @@ - 0.0 - 0.0 - 30 - 70 - false - - - 3.0 - 0.0 - false - true - 100 - - - - +0.0 +0.0 +30 +70 +false + + + + + + - 0.1 - - - 90.0 - - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.5 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - 0 - false - 0.5 - - - +0.1 + +90.0 + + + + + - 2.0 - 0 - 1 - - - 3.2 - 5 - 2 - - - +0.5 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + +0 +false +0.5 + + + - 1.5 - 0.05 - 2.0 - - - 90.0 - 0 - true - - - - - +2.0 +0 +1 + +3.2 +5 +2 + + + - - - - - +1.5 +0.05 +2.0 + +90.0 +0 +true + + + + + - 2.0 - 1.0 - 0.0 - 3.0 - 0.05 - 2.0 - 1.0 - - - 5.0 - 0.1 - 3.0 - - - 50.0 - 2.0 - 30.0 - 1.0 - - - 6.5 - 3.0 - 0.125 - 2 - - - - - + + + + + - - - - +2.0 +0.0 +1.0 +0.0 +3.0 +0.05 +2.0 +1.0 + +5.0 +0.1 +3.0 + +50.0 +2.0 +30.0 +1.0 + +6.5 +3.0 +0.125 +2 + + + + + - - - 20.0 - 75.0 - 40:40 - true - 10.0 - 1.0 - - - ./TypeTray-thumb.jpg - + + + + + + +20.0 +75.0 +40:40 +true +10.0 +1.0 + + + + + + + + + + + + + + + +8.0 +4.0 +0.1 + +12 + + + + + +2.0 +2.0 +false +labels.txt + +0.0 +0.0 +10.0 +0.9 +0.3 + + +./TypeTray-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.UBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.UBox.inx index e4b81db..ddaddd1 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.UBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.UBox.inx @@ -3,9 +3,7 @@ UBox info.festi.boxes.py.UBox ubox - - - + @@ -23,144 +21,150 @@ - 100.0 - 100.0 - 100.0 - 30.0 - - - - +100.0 +100.0 +100.0 +30.0 + +3.0 +0.0 +false +false +true +100 + + + + - - - 3.0 - 0.0 - false - true - 100 - - - - +0.1 + +90.0 + + + + + - 0.1 - - - 90.0 - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + +0 +false +0.5 + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - 0 - false - 0.5 - - - +2.0 +0 +1 + +3.2 +5 +2 + + + - 2.0 - 0 - 1 - - - 3.2 - 5 - 2 - - - +1.5 +0.05 +2.0 + +90.0 +0 +true + + + + + - 1.5 - 0.05 - 2.0 - - - 90.0 - 0 - true - - - - - + + + + + - - - - - +2.0 +0.0 +1.0 +0.0 +3.0 +0.05 +2.0 +1.0 + +5.0 +0.1 +3.0 + +50.0 +2.0 +30.0 +0.0 + +6.5 +3.0 +0.125 +2 + + + + + - 2.0 - 1.0 - 0.0 - 3.0 - 0.05 - 2.0 - 1.0 - - - 5.0 - 0.1 - 3.0 - - - 50.0 - 2.0 - 30.0 - 0.0 - - - 6.5 - 3.0 - 0.125 - 2 - - - - - + + + + - - - - + +20.0 +75.0 +40:40 +true +10.0 +1.0 + +1.05 +1.0 +0.5 +5.0 + + + + + + + - - - 20.0 - 75.0 - 40:40 - true - 10.0 - 1.0 - - - 1.05 - 1.0 - 0.5 - 5.0 - - - ./UBox-thumb.jpg - + + + + + + + +8.0 +4.0 +0.1 + + +./UBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.UnevenHeightBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.UnevenHeightBox.inx index adfccb9..bf3921a 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.UnevenHeightBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.UnevenHeightBox.inx @@ -3,72 +3,69 @@ UnevenHeightBox info.festi.boxes.py.UnevenHeightBox unevenheightbox - - - + - 100.0 - 100.0 - true - 50 - 50 - 100 - 100 - false - 0 - eeee - - - 3.0 - 0.0 - false - true - 100 - - - - +100.0 +100.0 +true +50 +50 +100 +100 +false +0 +eeee + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 120 - 0.1 - false - false - 0.3 - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +120 +0.1 +false +false +0.3 + + + + + - 30 - 0.2 - - - ./UnevenHeightBox-thumb.jpg - +30 +0.2 + + +./UnevenHeightBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.UniversalBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.UniversalBox.inx index 1aa4fcd..01608d7 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.UniversalBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.UniversalBox.inx @@ -3,9 +3,7 @@ UniversalBox info.festi.boxes.py.UniversalBox universalbox - - - + @@ -29,148 +27,154 @@ - 100.0 - 100.0 - 100.0 - true - - - +100.0 +100.0 +100.0 +true + + + - - - - + +3.0 +0.0 +false +false +true +100 + + + + - - - 3.0 - 0.0 - false - true - 100 - - - - +0.1 + +90.0 + + + + + - 0.1 - - - 90.0 - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + +0 +true +0.5 + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - 0 - true - 0.5 - - - +2.0 +0 +1 + +3.2 +5 +2 + + + - 2.0 - 0 - 1 - - - 3.2 - 5 - 2 - - - +1.5 +0.05 +2.0 + +90.0 +0 +true + + + + + - 1.5 - 0.05 - 2.0 - - - 90.0 - 0 - true - - - - - + + + + + - - - - - +2.0 +0.0 +1.0 +0.0 +3.0 +0.05 +2.0 +1.0 + +5.0 +0.1 +3.0 + +50.0 +2.0 +30.0 +1.0 + +6.5 +3.0 +0.125 +2 + + + + + - 2.0 - 1.0 - 0.0 - 3.0 - 0.05 - 2.0 - 1.0 - - - 5.0 - 0.1 - 3.0 - - - 50.0 - 2.0 - 30.0 - 1.0 - - - 6.5 - 3.0 - 0.125 - 2 - - - - - + + + + - - - - + +20.0 +75.0 +40:40 +true +10.0 +1.0 + +1.05 +1.0 +0.5 +5.0 + + + + + + + - - - 20.0 - 75.0 - 40:40 - true - 10.0 - 1.0 - - - 1.05 - 1.0 - 0.5 - 5.0 - - - ./UniversalBox-thumb.jpg - + + + + + + + +8.0 +4.0 +0.1 + + +./UniversalBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WaivyKnob.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WaivyKnob.inx deleted file mode 100644 index ac0ea77..0000000 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.WaivyKnob.inx +++ /dev/null @@ -1,44 +0,0 @@ - - -WaivyKnob -info.festi.boxes.py.WaivyKnob -waivyknob - - - 50.0 - 20 - 45.0 - 6.0 - 1.0 - 10.0 - - - 3.0 - 0.0 - false - true - 100 - - - - - - 0.1 - - - ./WaivyKnob-thumb.jpg - - - - - all - - - - - - - - \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WallCaliper.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WallCaliper.inx index 8d931e1..87f263f 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.WallCaliper.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WallCaliper.inx @@ -3,88 +3,82 @@ WallCaliper info.festi.boxes.py.WallCaliper wallcaliper - - - - - - - - + + + + + + - 100 - 18.0 - 6.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +100 +18.0 +6.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.0 - - - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.0 + + + + + + - 4.0 - 5.5 - 6.0 - 12.0 - 101.6 - 1.0 - 2.0 - - - - - +4.0 +5.5 +6.0 +12.0 +101.6 +1.0 +2.0 + + + + - 8.0 - 1.0 - - - 45.0 - - - - +8.0 +1.0 + +45.0 + + + + - 18.0 - 200.0 - 1.0 - - - ./WallCaliper-thumb.jpg - +18.0 +200.0 +1.0 + + +./WallCaliper-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WallChiselHolder.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WallChiselHolder.inx index 8027e63..f8b705d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.WallChiselHolder.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WallChiselHolder.inx @@ -3,96 +3,90 @@ WallChiselHolder info.festi.boxes.py.WallChiselHolder wallchiselholder - - - - - - - - + + + + + + - 120 - 30.0 - 30.0 - 5.0 - 5.0 - 6 - - - - +120 +30.0 +30.0 +5.0 +5.0 +6 + + + + - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.0 - - - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.0 + + + + + + - 4.0 - 5.5 - 6.0 - 12.0 - 101.6 - 1.0 - 2.0 - - - - - +4.0 +5.5 +6.0 +12.0 +101.6 +1.0 +2.0 + + + + - 8.0 - 1.0 - - - 45.0 - - - - +8.0 +1.0 + +45.0 + + + + - 18.0 - 200.0 - 1.0 - - - ./WallChiselHolder-thumb.jpg - +18.0 +200.0 +1.0 + + +./WallChiselHolder-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WallConsole.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WallConsole.inx index 35d077a..c7a55ea 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.WallConsole.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WallConsole.inx @@ -3,90 +3,84 @@ WallConsole info.festi.boxes.py.WallConsole wallconsole - - - - - - - - + + + + + + - 100 - 100 - true - 50 - 35 - - - 3.0 - 0.0 - false - true - 100 - - - - +100 +100 +true +50 +35 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.0 - - - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.0 + + + + + + - 4.0 - 5.5 - 6.0 - 12.0 - 101.6 - 1.0 - 2.0 - - - - - +4.0 +5.5 +6.0 +12.0 +101.6 +1.0 +2.0 + + + + - 8.0 - 1.0 - - - 45.0 - - - - +8.0 +1.0 + +45.0 + + + + - 18.0 - 200.0 - 1.0 - - - ./WallConsole-thumb.jpg - +18.0 +200.0 +1.0 + + +./WallConsole-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WallDrillBox.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WallDrillBox.inx index 1d6d08f..cecc729 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.WallDrillBox.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WallDrillBox.inx @@ -3,95 +3,89 @@ WallDrillBox info.festi.boxes.py.WallDrillBox walldrillbox - - - - - - - - + + + + + + - 25*6 - 10:20:30 - 25:40:60 - 15.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +25*6 +10:20:30 +25:40:60 +15.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.0 - - - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.0 + + + + + + - 4.0 - 5.5 - 6.0 - 12.0 - 101.6 - 1.0 - 2.0 - - - - - +4.0 +5.5 +6.0 +12.0 +101.6 +1.0 +2.0 + + + + - 8.0 - 1.0 - - - 45.0 - - - - +8.0 +1.0 + +45.0 + + + + - 18.0 - 200.0 - 1.0 - - - 60 - 1.0 - 1.0 - 3.0 - - - ./WallDrillBox-thumb.jpg - +18.0 +200.0 +1.0 + +60 +0.0 +1.0 +1.0 +3.0 + + +./WallDrillBox-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WallEdges.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WallEdges.inx index 4f11b77..6e14046 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.WallEdges.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WallEdges.inx @@ -3,86 +3,80 @@ WallEdges info.festi.boxes.py.WallEdges walledges - - - - - - - - + + + + + + - 120 - - - 3.0 - 0.0 - false - true - 100 - - - - +120 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.0 - - - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.0 + + + + + + - 4.0 - 5.5 - 6.0 - 12.0 - 101.6 - 1.0 - 2.0 - - - - - +4.0 +5.5 +6.0 +12.0 +101.6 +1.0 +2.0 + + + + - 8.0 - 1.0 - - - 45.0 - - - - +8.0 +1.0 + +45.0 + + + + - 18.0 - 200.0 - 1.0 - - - ./WallEdges-thumb.jpg - +18.0 +200.0 +1.0 + + +./WallEdges-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WallPinRow.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WallPinRow.inx index 057ffe2..bf073a7 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.WallPinRow.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WallPinRow.inx @@ -3,93 +3,87 @@ WallPinRow info.festi.boxes.py.WallPinRow wallpinrow - - - - - - - - + + + + + + - 8 - 35 - 10 - 35 - 0.0 - 20.0 - 3 - 50.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +8 +35 +10 +35 +0.0 +20.0 +3 +50.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.0 - - - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.0 + + + + + + - 4.0 - 5.5 - 6.0 - 12.0 - 101.6 - 1.0 - 2.0 - - - - - +4.0 +5.5 +6.0 +12.0 +101.6 +1.0 +2.0 + + + + - 8.0 - 1.0 - - - 45.0 - - - - +8.0 +1.0 + +45.0 + + + + - 18.0 - 200.0 - 1.0 - - - ./WallPinRow-thumb.jpg - +18.0 +200.0 +1.0 + + +./WallPinRow-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WallPlaneHolder.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WallPlaneHolder.inx index b1b563b..747b46e 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.WallPlaneHolder.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WallPlaneHolder.inx @@ -3,89 +3,83 @@ WallPlaneHolder info.festi.boxes.py.WallPlaneHolder wallplaneholder - - - - - - - - + + + + + + - 80 - 250 - 30 - 80 - - - 3.0 - 0.0 - false - true - 100 - - - - +80 +250 +30 +80 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.0 - - - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.0 + + + + + + - 4.0 - 5.5 - 6.0 - 12.0 - 101.6 - 1.0 - 2.0 - - - - - +4.0 +5.5 +6.0 +12.0 +101.6 +1.0 +2.0 + + + + - 8.0 - 1.0 - - - 45.0 - - - - +8.0 +1.0 + +45.0 + + + + - 18.0 - 200.0 - 1.0 - - - ./WallPlaneHolder-thumb.jpg - +18.0 +200.0 +1.0 + + +./WallPlaneHolder-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WallPliersHolder.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WallPliersHolder.inx index 8076930..9b9170d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.WallPliersHolder.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WallPliersHolder.inx @@ -3,90 +3,84 @@ WallPliersHolder info.festi.boxes.py.WallPliersHolder wallpliersholder - - - - - - - - + + + + + + - 100*3 - 50 - 50 - true - 45 - - - 3.0 - 0.0 - false - true - 100 - - - - +100*3 +50 +50 +true +45 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.0 - - - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.0 + + + + + + - 4.0 - 5.5 - 6.0 - 12.0 - 101.6 - 1.0 - 2.0 - - - - - +4.0 +5.5 +6.0 +12.0 +101.6 +1.0 +2.0 + + + + - 8.0 - 1.0 - - - 45.0 - - - - +8.0 +1.0 + +45.0 + + + + - 18.0 - 200.0 - 1.0 - - - ./WallPliersHolder-thumb.jpg - +18.0 +200.0 +1.0 + + +./WallPliersHolder-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WallRack.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WallRack.inx new file mode 100644 index 0000000..ed77827 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WallRack.inx @@ -0,0 +1,93 @@ + + +WallRack +info.festi.boxes.py.WallRack +wallrack +200 +50 +100*3 +false + + + + + + +true +20.0 +1.5 + + + + +false + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +1.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +6.5 +3.0 +0.125 +2 + + + + + + + + + + + + +20.0 +75.0 +40:40 +true +10.0 +1.0 + + +./WallRack-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WallRollHolder.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WallRollHolder.inx new file mode 100644 index 0000000..0ea10b5 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WallRollHolder.inx @@ -0,0 +1,96 @@ + + +WallRollHolder +info.festi.boxes.py.WallRollHolder +wallrollholder + + + + + + + +275 +120 +80 +25 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + +90.0 + + + + + + +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.0 + + + + + + + +4.0 +5.5 +6.0 +12.0 +101.6 +1.0 +2.0 + + + + + +8.0 +1.0 + +45.0 + + + + + +18.0 +200.0 +1.0 + + +./WallRollHolder-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WallSlottedHolder.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WallSlottedHolder.inx index 1c3f452..264058b 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.WallSlottedHolder.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WallSlottedHolder.inx @@ -3,97 +3,91 @@ WallSlottedHolder info.festi.boxes.py.WallSlottedHolder wallslottedholder - - - - - - - - + + + + + + - 120 - 50.0 - 50.0 - 5.0 - 35.0 - 5.0 - 6 - - - - +120 +50.0 +50.0 +5.0 +35.0 +5.0 +6 + + + + - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.0 - - - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.0 + + + + + + - 4.0 - 5.5 - 6.0 - 12.0 - 101.6 - 1.0 - 2.0 - - - - - +4.0 +5.5 +6.0 +12.0 +101.6 +1.0 +2.0 + + + + - 8.0 - 1.0 - - - 45.0 - - - - +8.0 +1.0 + +45.0 + + + + - 18.0 - 200.0 - 1.0 - - - ./WallSlottedHolder-thumb.jpg - +18.0 +200.0 +1.0 + + +./WallSlottedHolder-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WallStairs.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WallStairs.inx index 54e9ce9..3257240 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.WallStairs.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WallStairs.inx @@ -3,89 +3,83 @@ WallStairs info.festi.boxes.py.WallStairs wallstairs - - - - - - - - + + + + + + - 250/3 - 40*3 - 30:100:180 - 30 - - - 3.0 - 0.0 - false - true - 100 - - - - +250/3 +40*3 +30:100:180 +30 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.0 - - - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.0 + + + + + + - 4.0 - 5.5 - 6.0 - 12.0 - 101.6 - 1.0 - 2.0 - - - - - +4.0 +5.5 +6.0 +12.0 +101.6 +1.0 +2.0 + + + + - 8.0 - 1.0 - - - 45.0 - - - - +8.0 +1.0 + +45.0 + + + + - 18.0 - 200.0 - 1.0 - - - ./WallStairs-thumb.jpg - +18.0 +200.0 +1.0 + + +./WallStairs-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WallTypeTray.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WallTypeTray.inx index ee26971..a2090ef 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.WallTypeTray.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WallTypeTray.inx @@ -3,104 +3,98 @@ WallTypeTray info.festi.boxes.py.WallTypeTray walltypetray - - - - - - - - + + + + + + - 50*3 - 50*3 - 100.0 - 0.0 - true +50*3 +50*3 +100.0 +0.0 +true - 0.0 - 0.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +0.0 +0.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.0 - - - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.0 + + + + + + - 4.0 - 5.5 - 6.0 - 12.0 - 101.6 - 1.0 - 2.0 - - - - - +4.0 +5.5 +6.0 +12.0 +101.6 +1.0 +2.0 + + + + - 8.0 - 1.0 - - - 45.0 - - - - +8.0 +1.0 + +45.0 + + + + - 18.0 - 200.0 - 1.0 - - - 60 - 2.0 - 1.0 - 4.0 - - - ./WallTypeTray-thumb.jpg - +18.0 +200.0 +1.0 + +60 +0.0 +2.0 +1.0 +4.0 + + +./WallTypeTray-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WallWrenchHolder.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WallWrenchHolder.inx index c045e3c..f2f2132 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.WallWrenchHolder.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WallWrenchHolder.inx @@ -3,93 +3,87 @@ WallWrenchHolder info.festi.boxes.py.WallWrenchHolder wallwrenchholder - - - - - - - - + + + + + + - 100 - 30.0 - 11 - 8.0 - 25.0 - 3.0 - 5.0 - 0.0 - - - 3.0 - 0.0 - false - true - 100 - - - - +100 +30.0 +11 +8.0 +25.0 +3.0 +5.0 +0.0 + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - 1.0 - - - - - - - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + +1.0 + + + + + + - 4.0 - 5.5 - 6.0 - 12.0 - 101.6 - 1.0 - 2.0 - - - - - +4.0 +5.5 +6.0 +12.0 +101.6 +1.0 +2.0 + + + + - 8.0 - 1.0 - - - 45.0 - - - - +8.0 +1.0 + +45.0 + + + + - 18.0 - 200.0 - 1.0 - - - ./WallWrenchHolder-thumb.jpg - +18.0 +200.0 +1.0 + + +./WallWrenchHolder-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WavyKnob.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WavyKnob.inx new file mode 100644 index 0000000..8875768 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WavyKnob.inx @@ -0,0 +1,42 @@ + + +WavyKnob +info.festi.boxes.py.WavyKnob +wavyknob +50.0 +20 +45.0 +6.0 +1.0 +10.0 + +3.0 +0.0 +false +false +true +100 + + + + + +0.1 + + +./WavyKnob-thumb.jpg + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/boxes.py/boxes.py.WineRack.inx b/extensions/fablabchemnitz/boxes.py/boxes.py.WineRack.inx index 24d46f4..c85f078 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes.py.WineRack.inx +++ b/extensions/fablabchemnitz/boxes.py/boxes.py.WineRack.inx @@ -3,50 +3,48 @@ WineRack info.festi.boxes.py.WineRack winerack - - - 400 - 300 - 210 - 46.0 - - - - +400 +300 +210 +46.0 + + + + - - - 3.0 - 0.0 - false - true - 100 - - - - + +3.0 +0.0 +false +false +true +100 + + + + - 0.1 - - - 90.0 - - - - - +0.1 + +90.0 + + + + + - 2.0 - 1.0 - 0.0 - 2.0 - 0.0 - 2.0 - 1.0 - - - ./WineRack-thumb.jpg - +2.0 +0.0 +1.0 +0.0 +2.0 +0.0 +2.0 +1.0 + + +./WineRack-thumb.jpg + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/CONTRIBUTING.rst b/extensions/fablabchemnitz/boxes.py/boxes/CONTRIBUTING.rst index 0c3d382..07897bd 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/CONTRIBUTING.rst +++ b/extensions/fablabchemnitz/boxes.py/boxes/CONTRIBUTING.rst @@ -54,6 +54,19 @@ If you want feed back on you code feel free to open a PR. State that this is work in progress in the PR message. It's OK if it does not follow the guidelines (yet). +Check Code +.......... + +The `pre-commit `_ tool is used to verify the code style. +When installed, it automatically checks and corrects the code before each commit. + +* Install *pre-commit*, e.g. :code:`pip install pre-commit` +* Install githook :code:`pre-commit install` + +For manual check use :code:`pre-commit run --all-files`. + +To remove githook use :code:`pre-commit uninstall`. + Writing new Generators ...................... @@ -71,12 +84,11 @@ Adding new Dependencies ....................... Adding new dependencies should be considered thoroughly. If a new -depencendcy is added it needs to be added in all these places: +dependency is added it needs to be added in all these places: * *documentation/src/install.rst* * RST files in *documentation/src/install/* * *scripts/Dockerfile* -* *.travis.yml* If it is a Python module it also needs to be added: * *requirements.txt* @@ -96,23 +108,24 @@ To check your changes docs need to be build with *make html* in *documentation/build/html*. You need to have *sphinx* installed for this to work. -The online documentation gets build and updated automatically by the Travis CI -as soon as the changes makes it into the GitHub master branch. +The online documentation gets build and updated automatically by the Github Actions +as soon as the changes makes it into the GitHub *master* branch. Provide photos for generators ----------------------------- Many generators still come without an example photo. If you are creating such an item consider donating a good picture. You can -simply attach it to `ticket #140 -`_. If you want you can +simply attach it to `ticket #628 +`_. If you want you can also create a proper pull request instead: -* Make sure you have sh, convert (ImageMagick), sed and sha256sum installed +* Make sure you have sh, ImageMagick (You will need to install legacy utilities for convert), sed and sha256sum installed * The picture needs to be an jpg file with the name of the generator (This is case sensitive. Use CamelCase.) * The picture should be 1200 pixels wide and square or not too far from square (3:4 is fine). +* Minimize the file size by running it through `Tiny Png `_ * Place the file in *static/samples/* * Check if the picture shows up at the bottom of the settings page of the generator when running *scripts/boxesserver* @@ -142,7 +155,11 @@ perfect. We can work on it together. Running the Code ---------------------------- -To serve website, run `scripts/boxesserver` script +To serve website, run :code:`scripts/boxesserver` script. + +You can set the BOXES_GENERATOR_PATH environment variable to add +custom generators if you cannot easily copy them in the sources / +system installation. Reporting bugs -------------- diff --git a/extensions/fablabchemnitz/boxes.py/boxes/MANIFEST.in b/extensions/fablabchemnitz/boxes.py/boxes/MANIFEST.in new file mode 100644 index 0000000..58a9fdc --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/MANIFEST.in @@ -0,0 +1,6 @@ +include README.rst LICENSE.txt +include examples/*.svg +include documentation/presentation.odp +include inkex/*.txt +include po/* +include scripts/boxes2pot scripts/boxes2inkscape scripts/boxes_proxy.py diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/__init__.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/__init__.py index 5316877..c928017 100755 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/__init__.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/__init__.py @@ -13,28 +13,30 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from __future__ import annotations -import math -import sys import argparse -from argparse import ArgumentParser -import re -from functools import wraps -from xml.sax.saxutils import quoteattr -from contextlib import contextmanager import copy +import gettext +import math +import random +import re +import sys +from argparse import ArgumentParser +from contextlib import contextmanager +from functools import wraps from shlex import quote +from typing import Any +from xml.sax.saxutils import quoteattr + +import qrcode from shapely.geometry import * from shapely.ops import split -import random -from boxes import edges -from boxes import formats -from boxes import svgutil -from boxes import gears -from boxes import pulley -from boxes import parts -from boxes.Color import * +from boxes import edges, formats, gears, parts, pulley +from boxes.Color import * +from boxes.qrcode_factory import BoxesQrCodeFactory +from boxes.vectors import kerf ### Helpers @@ -43,16 +45,15 @@ def dist(dx, dy): Return distance :param dx: delta x - :param dy: delat y + :param dy: delay y """ return (dx * dx + dy * dy) ** 0.5 def restore(func): """ - Wrapper: Restore coordiantes after function + Wrapper: Restore coordinates after function :param func: function to wrap - """ @wraps(func) @@ -70,7 +71,6 @@ def holeCol(func): Wrapper: color holes differently :param func: function to wrap - """ @wraps(func) @@ -118,7 +118,7 @@ class NutHole: "M64": (95, 51), } - def __init__(self, boxes, settings): + def __init__(self, boxes, settings) -> None: self.boxes = boxes self.ctx = boxes.ctx self.settings = settings @@ -147,7 +147,6 @@ def argparseSections(s): Parse sections parameter :param s: string to parse - """ result = [] @@ -179,9 +178,9 @@ class ArgparseEdgeType: """argparse type to select from a set of edge types""" names = edges.getDescriptions() - edges = [] + edges: list[str] = [] - def __init__(self, edges=None): + def __init__(self, edges: str | None = None) -> None: if edges: self.edges = list(edges) @@ -195,17 +194,17 @@ class ArgparseEdgeType: def html(self, name, default, translate): options = "\n".join( - ("""""" % + """""" % (e, ' selected="selected"' if e == default else "", - translate("%s %s" % (e, self.names.get(e, "")))) for e in self.edges)) - return """\n""" % (name, name, name+"_id", name+"_description", options) + translate("{} {}".format(e, self.names.get(e, "")))) for e in self.edges) + return """\n""".format(name, name, name+"_id", name+"_description", options) def inx(self, name, viewname, arg): return (' \n' % (name, viewname, quoteattr(arg.help or "")) + - ''.join((' \n' % ( + ''.join(' \n'.format( e, e, self.names.get(e, "")) - for e in self.edges)) + + for e in self.edges) + ' \n') class BoolArg: @@ -242,7 +241,7 @@ Values: 'style' : ('circle', ), } - relative_params = {} + relative_params: dict[str, Any] = {} class fillHolesSettings(edges.Settings): """Settings for Hole filling @@ -277,22 +276,24 @@ Values: ############################################################################## class Boxes: - """Main class -- Generator should sub class this """ + """Main class -- Generator should subclass this """ webinterface = True ui_group = "Misc" + UI = "" - description = "" # Markdown syntax is supported + description: str = "" # Markdown syntax is supported - def __init__(self): + def __init__(self) -> None: self.formats = formats.Formats() self.ctx = None - description = self.__doc__ + description: str = self.__doc__ or "" if self.description: description += "\n\n" + self.description self.argparser = ArgumentParser(description=description) - self.edgesettings = {} - self.inkscapefile = None + self.edgesettings: dict[Any, Any] = {} + self.non_default_args: dict[Any, Any] = {} + self.translations = gettext.NullTranslations() self.metadata = { "name" : self.__class__.__name__, @@ -300,9 +301,14 @@ class Boxes: "description" : self.description, "group" : self.ui_group, "url" : "", - "command_line" : "" + "url_short" : "", + "cli" : "", + "cli_short" : "", } + # Dummy attribute for static analytic tools. Will be overwritten by `argparser` at runtime. + self.thickness: float = 0.0 + self.argparser._action_groups[1].title = self.__class__.__name__ + " Settings" defaultgroup = self.argparser.add_argument_group( "Default Settings") @@ -319,6 +325,9 @@ class Boxes: defaultgroup.add_argument( "--tabs", action="store", type=float, default=0.0, help="width of tabs holding the parts in place (in mm)(not supported everywhere) [\U0001F6C8](https://florianfesti.github.io/boxes/html/usermanual.html#tabs)") + defaultgroup.add_argument( + "--qr_code", action="store", type=boolarg, default=False, + help="Add a QR Code with link or command line to the generated output") defaultgroup.add_argument( "--debug", action="store", type=boolarg, default=False, help="print surrounding boxes for some structures [\U0001F6C8](https://florianfesti.github.io/boxes/html/usermanual.html#debug)") @@ -374,7 +383,7 @@ class Boxes: return self.bedBoltSettings = (3, 5.5, 2, 20, 15) # d, d_nut, h_nut, l, l1 - self.surface, self.ctx = self.formats.getSurface(self.format, self.output) + self.surface, self.ctx = self.formats.getSurface(self.format) if self.format == 'svg_Ponoko': self.ctx.set_line_width(0.01) @@ -390,20 +399,31 @@ class Boxes: self.move(self.reference, 10, "up", before=True) self.ctx.rectangle(0, 0, self.reference, 10) if self.reference < 80: - self.text("%.fmm, burn:%.2fmm" % (self.reference , self.burn), self.reference + 5, 5, + self.text(f"{self.reference:.2f}mm, burn:{self.burn:.2f}mm", self.reference + 5, 5, fontsize=8, align="middle left", color=Color.ANNOTATIONS) else: - self.text("%.fmm, burn:%.2fmm" % (self.reference , self.burn), self.reference / 2.0, 5, + self.text(f"{self.reference:.2f}mm, burn:{self.burn:.2f}mm", self.reference / 2.0, 5, fontsize=8, align="middle center", color=Color.ANNOTATIONS) self.move(self.reference, 10, "up") + if self.qr_code: + self.renderQrCode() self.ctx.stroke() + def renderQrCode(self): + content = self.metadata['url_short'] or self.metadata["cli_short"] + size = 1.5 + if content: + with self.saved_context(): + self.qrcode(content, box_size=size, move="right") + self.text(text=content, y=6, color=Color.ANNOTATIONS, fontsize=6) + self.qrcode(content, box_size=size, move="up only") + def buildArgParser(self, *l, **kw): """ Add commonly used arguments - :param \*l: parameter names - :param \*\*kw: parameters with new default values + :param l: parameter names + :param kw: parameters with new default values Supported parameters are @@ -506,41 +526,42 @@ class Boxes: Parse command line parameters :param args: (Default value = None) parameters, None for using sys.argv - """ if args is None: args = sys.argv[1:] - if len(args) > 1 and args[-1][0] != "-": - self.inkscapefile = args[-1] - del args[-1] - args = [a for a in args if not a.startswith('--tab=')] def cliquote(s): s = s.replace('\r', '') s = s.replace('\n', "\\n") return quote(s) - self.metadata["cli"] = "boxes " + self.__class__.__name__ + " " + " ".join((cliquote(arg) for arg in args)) + self.metadata["cli"] = "boxes " + self.__class__.__name__ + " " + " ".join(cliquote(arg) for arg in args) + for key, value in vars(self.argparser.parse_args(args=args)).items(): + default = self.argparser.get_default(key) + # treat edge settings separately for setting in self.edgesettings: if key.startswith(setting + '_'): self.edgesettings[setting][key[len(setting)+1:]] = value continue setattr(self, key, value) + if (value != default): + self.non_default_args[key] = value # Change file ending to format if not given explicitly format = getattr(self, "format", "svg") if getattr(self, 'output', None) == 'box.svg': self.output = 'box.' + format.split("_")[0] + self.metadata["cli_short"] = "boxes " + self.__class__.__name__ + " " + " ".join(cliquote(arg) for arg in args if (arg.split("=")[0][2:] in self.non_default_args)) + def addPart(self, part, name=None): """ Add Edge or other part instance to this one and add it as attribute :param part: Callable :param name: (Default value = None) attribute name (__name__ as default) - """ if name is None: name = part.__class__.__name__ @@ -556,7 +577,7 @@ class Boxes: self.addPart(part) def _buildObjects(self): - """Add default edges and parts """ + """Add default edges and parts""" self.edges = {} self.addPart(edges.Edge(self, None)) self.addPart(edges.OutSetEdge(self, None)) @@ -589,8 +610,8 @@ class Boxes: edges.CabinetHingeSettings(self.thickness, True, **self.edgesettings.get("CabinetHinge", {})).edgeObjects(self) # Sliding Lid - edges.LidSettings(self.thickness, True, - **self.edgesettings.get("Lid", {})).edgeObjects(self) + edges.SlideOnLidSettings(self.thickness, True, + **self.edgesettings.get("SlideOnLid", {})).edgeObjects(self) # Rounded Triangle Edge edges.RoundedTriangleEdgeSettings(self.thickness, True, **self.edgesettings.get("RoundedTriangleEdge", {})).edgeObjects(self) @@ -606,6 +627,11 @@ class Boxes: # HexHoles self.hexHolesSettings = HexHolesSettings(self.thickness, True, **self.edgesettings.get("HexHoles", {})) + # Lids + from . import lids + self.lidSettings = lids.LidSettings(self.thickness, True, + **self.edgesettings.get("Lid", {})) + self.lid = lids.Lid(self, self.lidSettings) # Nuts self.addPart(NutHole(self, None)) @@ -649,22 +675,20 @@ class Boxes: return l - walls def render(self): - """Implement this method in your sub class. + """Implement this method in your subclass. - You will typically need to call .parseArgs() before calling this one""" - self.open() + You will typically need to call .parseArgs() before calling this one + """ # Change settings and create new Edges and part classes here raise NotImplementedError - self.close() - def cc(self, callback, number, x=0.0, y=None): + def cc(self, callback, number, x=0.0, y=None, a=0.0): """Call callback from edge of a part :param callback: callback (callable or list of callables) :param number: number of the callback :param x: (Default value = 0.0) x position to be call on :param y: (Default value = None) y position to be called on (default does burn correction) - """ if y is None: y = self.burn @@ -678,7 +702,7 @@ class Boxes: if callback and callable(callback): with self.saved_context(): - self.moveTo(x, y) + self.moveTo(x, y, a) if number is None: callback() else: @@ -691,7 +715,6 @@ class Boxes: :param param: list or item :param idx: index in list - """ if isinstance(param, list): if len(param) > idx: @@ -706,7 +729,7 @@ class Boxes: Flush canvas to disk and convert output to requested format if needed. Call after .render()""" - if self.ctx == None: + if self.ctx is None: return self.ctx.stroke() @@ -715,15 +738,10 @@ class Boxes: self.surface.set_metadata(self.metadata) self.surface.flush() - self.surface.finish(self.inner_corners) + data = self.surface.finish(self.inner_corners) - self.formats.convert(self.output, self.format, self.metadata) - if self.inkscapefile: - try: - out = sys.stdout.buffer - except AttributeError: - out= sys.stdout - svgutil.svgMerge(self.output, self.inkscapefile, out) + data = self.formats.convert(data, self.format) + return data ############################################################ ### Turtle graphics commands @@ -737,7 +755,6 @@ class Boxes: :param degrees: angle :param radius: (Default value = 0) - """ try: @@ -795,7 +812,6 @@ class Boxes: """ Simple line :param length: length in mm - """ self.ctx.move_to(0, 0) if tabs and self.tabs: @@ -820,7 +836,7 @@ class Boxes: def step(self, out): """ - Create a parallel step prependicular to the current direction + Create a parallel step perpendicular to the current direction Positive values move to the outside of the part """ if out > 1E-5: @@ -841,7 +857,6 @@ class Boxes: :param y2: :param x3: :param y3: - """ self.ctx.curve_to(x1, y1, x2, y2, x3, y3) dx = x3 - x2 @@ -853,7 +868,7 @@ class Boxes: """ Draw multiple connected lines - :param \*args: Alternating length in mm and angle in degrees. + :param args: Alternating length in mm and angle in degrees. lengths may be a tuple (length, #tabs) angles may be tuple (angle, radius) @@ -875,8 +890,7 @@ class Boxes: Draw an edge with slot for a bed bolt :param length: length of the edge in mm - :param bedBoltSettings: (Default value = None) Dimmensions of the slot - + :param bedBoltSettings: (Default value = None) Dimensions of the slot """ d, d_nut, h_nut, l, l1 = bedBoltSettings or self.bedBoltSettings self.edge((length - d) / 2.0, tabs=tabs//2) @@ -950,7 +964,7 @@ class Boxes: """Create regular polygon as a wall :param corners: number of corners of the polygon - :param radius: distance center to one of the corners + :param r: radius distance center to one of the corners :param h: distance center to one of the sides (height of sector) :param side: length of one side :param edges: (Default value = "e", may be string/list of length corners) @@ -999,11 +1013,10 @@ class Boxes: self.move(tw, th, move) def grip(self, length, depth): - """Corrugated edge useful as an gipping area + """Corrugated edge useful as a gipping area :param length: length :param depth: depth of the grooves - """ grooves = max(int(length // (depth * 2.0)) + 1, 1) depth = length / grooves / 4.0 @@ -1014,9 +1027,7 @@ class Boxes: def _latchHole(self, length): """ - :param length: - """ self.edge(1.1 * self.thickness) self.corner(-90) @@ -1024,44 +1035,35 @@ class Boxes: self.corner(-90) self.edge(1.1 * self.thickness) - def _latchGrip(self, length): + def _latchGrip(self, length, extra_length=0.0): """ - :param length: - """ self.corner(90, self.thickness / 4.0) - self.grip(length / 2.0 - self.thickness / 2.0 - 0.2 * self.thickness, self.thickness / 2.0) + self.grip(length / 2.0 - self.thickness / 2.0 - 0.2 * self.thickness + extra_length, self.thickness / 2.0) self.corner(90, self.thickness / 4.0) - def latch(self, length, positive=True, reverse=False): + def latch(self, length, positive=True, reverse=False, extra_length=0.0): """Latch to fix a flex box door to the box :param length: length in mm :param positive: (Default value = True) False: Door side; True: Box side :param reverse: (Default value = False) True when running away from the latch - """ + t = self.thickness if positive: + poly = [0, -90, t, 90, length / 2.0, 90, t, -90, length / 2.] if reverse: - self.edge(length / 2.0) - self.corner(-90) - self.edge(self.thickness) - self.corner(90) - self.edge(length / 2.0) - self.corner(90) - self.edge(self.thickness) - self.corner(-90) - if not reverse: - self.edge(length / 2.0) + poly = list(reversed(poly)) + self.polyline(*poly) else: if reverse: - self._latchGrip(length) + self._latchGrip(length, extra_length) else: self.corner(90) self._latchHole(length) if not reverse: - self._latchGrip(length) + self._latchGrip(length, extra_length) else: self.corner(90) @@ -1072,7 +1074,6 @@ class Boxes: :param h: height in mm :param hl: height if th grip hole :param r: (Default value = 30) radius of the corners - """ d = (x - hl - 2 * r) / 2.0 @@ -1105,7 +1106,6 @@ class Boxes: :param x: :param y: (Default value = 0.0) :param degrees: (Default value = 0) - """ self.ctx.move_to(0, 0) self.ctx.translate(x, y) @@ -1134,7 +1134,6 @@ class Boxes: Set coordinate system to current position (end point) :param angle: (Default value = 0) heading - """ self.ctx.translate(*self.ctx.get_current_point()) self.ctx.rotate(angle) @@ -1144,16 +1143,15 @@ class Boxes: where can be combinations of "up" or "down", "left" or "right", "only", "mirror" and "rotated" when "only" is included the move is only done when before is True - "mirror" will flip the part along the y axis + "mirror" will flip the part along the y-axis "rotated" draws the parts rotated 90 counter clockwise The function returns whether actual drawing of the part - should be ommited. + should be omitted. :param x: width of part :param y: height of part :param where: which direction to move :param before: (Default value = False) called before or after part being drawn - """ if not where: where = "" @@ -1218,9 +1216,8 @@ class Boxes: Draw a round disc :param x: position - :param y: postion + :param y: position :param r: radius - """ r += self.burn self.moveTo(x + r, y) @@ -1239,11 +1236,10 @@ class Boxes: Draw a hole in shape of an n-edged regular polygon :param x: position - :param y: postion + :param y: position :param r: radius :param n: number of edges :param a: rotation angle - """ if not r: @@ -1284,9 +1280,8 @@ class Boxes: Draw a round hole :param x: position - :param y: postion + :param y: position :param r: radius - """ if not r: @@ -1310,7 +1305,6 @@ class Boxes: :param r: (Default value = 0) radius of the corners :param center_x: (Default value = True) if True, x position is the center, else the start :param center_y: (Default value = True) if True, y position is the center, else the start - """ r = min(r, dx/2., dy/2.) x_start = x if center_x else x + dx / 2.0 @@ -1333,8 +1327,7 @@ class Boxes: :param d: diameter :param w: width measured against flat side in mm :param rel_w: width in percent - :param angle: orentation (rotation) of the flat side - + :param angle: orientation (rotation) of the flat side """ if r is None: @@ -1367,7 +1360,6 @@ class Boxes: :param w: width measured against flat side in mm :param rel_w: width in percent :param angle: orientation (rotation) of the flat sides - """ if r is None: @@ -1398,11 +1390,10 @@ class Boxes: Draw a pear shaped mounting hole for sliding over a screw head. Total height = 1.5* d_shaft + d_head :param x: position - :param y: postion + :param y: position :param d_shaft: diameter of the screw shaft :param d_head: diameter of the screw head :param angle: rotation angle of the hole - """ if d_shaft < (2 * self.burn): @@ -1435,7 +1426,6 @@ class Boxes: :param y: (Default value = 0) :param angle: (Default value = 0) :param align: (Default value = "") string with combinations of (top|middle|bottom) and (left|center|right) separated by a space - """ self.moveTo(x, y, angle) text = text.split("\n") @@ -1534,7 +1524,7 @@ class Boxes: :param x: (Default value = 0) :param y: (Default value = 0) :param angle: (Default value = 0) - + :param screwholes: """ width, flange, holedistance, diameter = self.nema_sizes[size] if screwholes: @@ -1549,13 +1539,44 @@ class Boxes: y * 0.5 * holedistance, 0.5 * diameter) + def drawPoints(self, lines, kerfdir=1, close=True): + + if not lines: + return + + if kerfdir != 0: + lines = kerf(lines, self.burn*kerfdir, closed=close) + + self.ctx.save() + self.ctx.move_to(*lines[0]) + + for x, y in lines[1:]: + self.ctx.line_to(x, y) + + if close: + self.ctx.line_to(*lines[0]) + self.ctx.restore() + + def qrcode(self, content, box_size=1.0, color=Color.ETCHING, move=None): + q = qrcode.QRCode(image_factory=BoxesQrCodeFactory, box_size=box_size*10) + q.add_data(content) + m = q.get_matrix() + tw, th = len(m) * box_size, len(m[0]) * box_size + if self.move(tw, th, move, True): + return + + self.set_source_color(color) + q.make_image(ctx=self.ctx) + + self.move(tw, th, move) + @restore def showBorderPoly(self,border,color=Color.ANNOTATIONS): """ draw border polygon (for debugging only) :param border: array with coordinate [(x0,y0), (x1,y1),...] of the border polygon - + :param color: """ self.set_source_color(color) self.ctx.save() @@ -1588,7 +1609,6 @@ class Boxes: :param style: defines hole style - currently one of "round", "triangle", "square", "hexagon" or "octagon" :param bar_length: maximum bar length :param max_random: maximum number of random holes - """ if pattern not in ["random", "hex", "square", "hbar", "vbar"]: return @@ -1766,7 +1786,7 @@ class Boxes: # and process line while not xw > x_end: - # are we in inner polygone already? + # are we in inner polygon already? if (len(inner_line_split) > inner_line_index and xw > inner_line_split.geoms[inner_line_index].bounds[0]): # place inner, full size polygons @@ -1825,7 +1845,7 @@ class Boxes: segment_max = 0 segment_toggle ^= 1 - # create line from left to right and cut according to shrinked polygon + # create line from left to right and cut according to shrunk polygon line_complete = LineString([(min_x - 1, y), (max_x + 1, y)]) line_split = split(line_complete, cutPoly) @@ -1924,8 +1944,6 @@ class Boxes: :param settings: (Default value = None) :param skip: (Default value = None) function to check if hole should be present gets x, y, r, b, posx, posy - - """ if settings is None: @@ -1963,7 +1981,6 @@ class Boxes: :param d: diameter of the circle :param settings: (Default value = None) - """ d2 = d / 2.0 self.hexHolesRectangle(d, d, settings=settings, skip=self.__skipcircle) @@ -1976,19 +1993,16 @@ class Boxes: :param y: height :param rc: radius of the corners :param settings: (Default value = None) - """ def skip(x, y, r, b, posx, posy): """ - :param x: :param y: :param r: :param b: :param posx: :param posy: - """ posx = abs(posx - (x / 2.0)) posy = abs(posy - (y / 2.0)) @@ -2009,7 +2023,6 @@ class Boxes: :param h: height :param settings: (Default value = None) :param grow: (Default value = None) - """ if settings is None: settings = self.hexHolesSettings @@ -2024,7 +2037,7 @@ class Boxes: if grow == 'space ': b += leftover / (cy - 1) / 2 - # recalulate with adjusted values + # recalculate with adjusted values w = r + b / 2.0 dist = w * math.cos(math.pi / 6.0) @@ -2084,7 +2097,7 @@ class Boxes: :param x: x position of the center :param y: y position of the center :param angle: angle in which the rectangle is placed - :param outside: meassure size from the outside of the walls - not the inside + :param outside: measure size from the outside of the walls - not the inside """ self.moveTo(x, y, angle) d = 0.5*self.thickness @@ -2125,6 +2138,7 @@ class Boxes: :param x: width :param y: height :param r: radius of the corners + :param edge: :param callback: (Default value = None) :param holesMargin: (Default value = None) set to get hex holes :param holesSettings: (Default value = None) @@ -2133,7 +2147,6 @@ class Boxes: :param wallpieces: (Default value = 1) # of separate surrounding walls :param extend_corners: (Default value = True) have corners outset with the edges :param move: (Default value = None) - """ corner_holes = True @@ -2242,7 +2255,7 @@ class Boxes: callback=None, move=None): """ - Wall(s) with flex fiting around a roundedPlate() + Wall(s) with flex filing around a roundedPlate() For the callbacks the sides are counted depending on pieces @@ -2257,7 +2270,6 @@ class Boxes: :param pieces: (Default value = 1) number of separate pieces :param callback: (Default value = None) :param move: (Default value = None) - """ t = self.thickness c4 = (r + self.burn) * math.pi * 0.5 # circumference of quarter circle @@ -2360,8 +2372,7 @@ class Boxes: :param bedBoltSettings: (Default value = None) :param callback: (Default value = None) :param move: (Default value = None) - :param label: rendered to identify parts, it is not ment to be cut or etched (Default value = "") - + :param label: rendered to identify parts, it is not meant to be cut or etched (Default value = "") """ if len(edges) != 4: raise ValueError("four edges required") @@ -2415,7 +2426,7 @@ class Boxes: :param r: radius of the corners of the flange :param callback: (Default value = None) :param move: (Default value = None) - :param label: rendered to identify parts, it is not ment to be cut or etched (Default value = "") + :param label: rendered to identify parts, it is not meant to be cut or etched (Default value = "") """ t = self.thickness @@ -2448,7 +2459,7 @@ class Boxes: self.cc(callback, i, x=-rl) if flanges[i]: if edges[i] is self.edges["F"] or edges[i] is self.edges["h"]: - self.fingerHolesAt(flanges[i-1]+t-rl, 0.5*t+flanges[i], l, + self.fingerHolesAt(flanges[i-1]+edges[i-1].endwidth()-rl, 0.5*t+flanges[i], l, angle=0) self.edge(l+flanges[i-1]+flanges[i+1]+edges[i-1].endwidth()+edges[i+1].startwidth()-rl-rr) else: @@ -2469,13 +2480,13 @@ class Boxes: :param x: width :param y: height :param edges: (Default value = "eee") bottom, right[, diagonal] - :param r: radius towards the hypothenuse + :param r: radius towards the hypotenuse :param num: (Default value = 1) number of triangles :param bedBolts: (Default value = None) :param bedBoltSettings: (Default value = None) :param callback: (Default value = None) :param move: (Default value = None) - :param label: rendered to identify parts, it is not ment to be cut or etched (Default value = "") + :param label: rendered to identify parts, it is not meant to be cut or etched (Default value = "") """ edges = [self.edges.get(e, e) for e in edges] if len(edges) == 2: @@ -2486,7 +2497,6 @@ class Boxes: r = min(r, x, y) a = math.atan2(y-r, float(x-r)) alpha = math.degrees(a) - print(a, alpha) if a > 0: width = x + (edges[-1].spacing()+self.spacing)/math.sin(a) + edges[1].spacing() + self.spacing else: @@ -2495,7 +2505,7 @@ class Boxes: if num > 1: width = 2*width - x + r - self.spacing dx = width - x - edges[1].spacing() - self.spacing / 2 - dy = edges[0].spacing() + self.spacing / 2 + dy = edges[0].margin() + self.spacing / 2 overallwidth = width * (num // 2 + num % 2) - self.spacing overallheight = height - self.spacing @@ -2524,6 +2534,7 @@ class Boxes: edges[2](((x-r)**2+(y-r)**2)**0.5) self.step(-edges[2].endwidth()) self.corner(90-alpha, r) + self.edge(edges[0].startwidth()) self.corner(90) self.ctx.stroke() @@ -2545,7 +2556,7 @@ class Boxes: :param edges: (Default value = "eee") bottom, right, left :param callback: (Default value = None) :param move: (Default value = None) - :param label: rendered to identify parts, it is not ment to be cut or etched (Default value = "") + :param label: rendered to identify parts, it is not meant to be cut or etched (Default value = "") """ edges = [self.edges.get(e, e) for e in edges] @@ -2590,7 +2601,7 @@ class Boxes: :param radius: (Default value = 0.0) radius of upper corners :param callback: (Default value = None) :param move: (Default value = None) - :param label: rendered to identify parts, it is not ment to be cut or etched (Default value = "") + :param label: rendered to identify parts, it is not meant to be cut or etched (Default value = "") """ edges = [self.edges.get(e, e) for e in edges] @@ -2632,11 +2643,11 @@ class Boxes: edges[3](h0) self.edgeCorner(edges[-1], edges[0], 90) - self.move(overallwidth, overallheight, move, label) + self.move(overallwidth, overallheight, move, label=label) ### polygonWall and friends - def _polygonWallExtend(self, borders, edge, close=False): + def _polygonWallExtend(self, borders, edges, close=False): posx, posy = 0, 0 ext = [ 0.0 ] * 4 angle = 0 @@ -2647,6 +2658,24 @@ class Boxes: ext[2] = max(ext[2], x) ext[3] = max(ext[3], y) + # trace edge margins + nborders = [] + for i, val in enumerate(borders): + if i % 2: + nborders.append(val) + else: + edge = edges[(i//2)%len(edges)] + margin = edge.margin() + try: + l = val[0] + except TypeError: + l = val + if margin: + nborders.extend([0.0, -90, margin, 90, l, 90, margin, -90, 0.0]) + else: + nborders.append(val) + + borders = nborders for i in range(len(borders)): if i % 2: try: @@ -2684,25 +2713,67 @@ class Boxes: posy += borders[i] * math.sin(math.radians(angle)) checkpoint(ext, posx, posy) - ext[0] -= edge.margin() - ext[1] -= edge.margin() - ext[2] += edge.margin() - ext[3] += edge.margin() - return ext + def _closePolygon(self, borders): + posx, posy = 0, 0 + angle = 0.0 + + if borders and borders[-1] is not None: + return borders + + borders = borders[:-1] + + for i in range(len(borders)): + if i % 2: + try: + a, r = borders[i] + except TypeError: + angle = (angle + borders[i]) % 360 + continue + if a > 0: + centerx = posx + r * math.cos(math.radians(angle+90)) + centery = posy + r * math.sin(math.radians(angle+90)) + else: + centerx = posx + r * math.cos(math.radians(angle-90)) + centery = posy + r * math.sin(math.radians(angle-90)) + + angle = (angle + a) % 360 + if a > 0: + posx = centerx + r * math.cos(math.radians(angle-90)) + posy = centery + r * math.sin(math.radians(angle-90)) + else: + posx = centerx + r * math.cos(math.radians(angle+90)) + posy = centery + r * math.sin(math.radians(angle+90)) + else: + posx += borders[i] * math.cos(math.radians(angle)) + posy += borders[i] * math.sin(math.radians(angle)) + if len(borders) % 2 == 0: + borders.append(0.0) + + a = math.degrees(math.atan2(-posy, -posx)) + #print(a, angle, a - angle) + borders.append((a - angle + 360) % 360) + borders.append((posx**2 + posy**2)**.5) + borders.append(-a) + #print(borders) + return borders + def polygonWall(self, borders, edge="f", turtle=False, + correct_corners=True, callback=None, move=None, label=""): """ Polygon wall for all kind of multi-edged objects :param borders: array of distance and angles to draw - :param edge: (Default value = "f") Edges to apply. If the array of borders contains more segments that edges, the edge will wrap. Only edge types without start and end width suppported for now. + :param edge: (Default value = "f") Edges to apply. If the array of borders contains more segments that edges, the edge will wrap. Only edge types without start and end width supported for now. :param turtle: (Default value = False) + :param correct_corners: (Default value = True) :param callback: (Default value = None) :param move: (Default value = None) - :param label: rendered to identify parts, it is not ment to be cut or etched (Default value = "") + :param label: rendered to identify parts, it is not meant to be cut or etched (Default value = "") + borders is alternating between length of the edge and angle of the corner. For now neither tabs nor radii are supported. None at the end closes the polygon. """ try: edges = [self.edges.get(e, e) for e in edge] @@ -2711,7 +2782,9 @@ class Boxes: t = self.thickness # XXX edge.margin() - minx, miny, maxx, maxy = self._polygonWallExtend(borders, edges[0]) + borders = self._closePolygon(borders) + + minx, miny, maxx, maxy = self._polygonWallExtend(borders, edges) tw, th = maxx - minx, maxy - miny @@ -2723,13 +2796,14 @@ class Boxes: length_correction = 0. for i in range(0, len(borders), 2): - self.cc(callback, i) + self.cc(callback, i // 2) self.edge(length_correction) l = borders[i] - length_correction next_angle = borders[i+1] - if isinstance(next_angle, (int, float)) and next_angle < 0: - length_correction = t * math.tan(math.radians((-next_angle / 2))) + if (correct_corners and + isinstance(next_angle, (int, float)) and next_angle < 0): + length_correction = t * math.tan(math.radians(-next_angle / 2)) else: length_correction = 0.0 l -= length_correction @@ -2742,7 +2816,11 @@ class Boxes: self.move(tw, th, move, label=label) @restore - def polygonWalls(self, borders, h, bottom="F", top="F", symetrical=True): + def polygonWalls(self, borders, h, bottom="F", top="F", symmetrical=True): + if not borders: + return + + borders = self._closePolygon(borders) bottom = self.edges.get(bottom, bottom) top = self.edges.get(top, top) t = self.thickness # XXX edge.margin() @@ -2759,7 +2837,7 @@ class Boxes: self.moveTo(0, bottom.margin()) while i < len(borders): - if symetrical: + if symmetrical: if part_cnt % 2: left, right = lf, rf else: @@ -2795,7 +2873,7 @@ class Boxes: rightsettings.setValues(self.thickness, angle=angle) if angle < 0: - length_correction = t * math.tan(math.radians((-angle / 2))) + length_correction = t * math.tan(math.radians(-angle / 2)) else: length_correction = 0.0 l -= length_correction @@ -2831,10 +2909,10 @@ class Boxes: :param n: number of parts :param width: number of parts in a row (0 for same as n) - :param move: (Default value = None) + :param move: (Default value = "") :param part: callable that draws a part and knows move param - :param \*l: params for part - :param \*\*kw: keyword params for part + :param l: params for part + :param kw: keyword params for part """ if n <= 0: return diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/drawing.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/drawing.py index bca34db..3645772 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/drawing.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/drawing.py @@ -1,18 +1,33 @@ -import math -import datetime -from affine import Affine -from boxes.extents import Extents -from boxes.Color import Color as bColor +from __future__ import annotations -try: - from xml.etree import cElementTree as ET -except ImportError: - from xml.etree import ElementTree as ET +import codecs +import datetime +import io +import math +from typing import Any +from xml.etree import ElementTree as ET + +from affine import Affine + +from boxes.extents import Extents EPS = 1e-4 PADDING = 10 -RANDOMIZE_COLORS = False # enable to ease check for continuity of pathes +RANDOMIZE_COLORS = False # enable to ease check for continuity of paths + + +def reorder_attributes(root) -> None: + """ + Source: https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.remove + """ + for el in root.iter(): + attrib = el.attrib + if len(attrib) > 1: + # adjust attribute order, e.g. by sorting + attribs = sorted(attrib.items()) + attrib.clear() + attrib.update(attribs) def points_equal(x1, y1, x2, y2): @@ -30,10 +45,10 @@ class Surface: scale = 1.0 invert_y = False - def __init__(self, fname): - self._fname = fname - self.parts = [] + def __init__(self) -> None: + self.parts: list[Any] = [] self._p = self.new_part("default") + self.count = 0 def set_metadata(self, metadata): self.metadata = metadata @@ -81,6 +96,9 @@ class Surface: return p def append(self, *path): + self.count += 1 + if self.count > 100000: + raise ValueError("Too many lines") self._p.append(*path) def stroke(self, **params): @@ -96,9 +114,9 @@ class Surface: class Part: - def __init__(self, name): - self.pathes = [] - self.path = [] + def __init__(self, name) -> None: + self.pathes: list[Any] = [] + self.path: list[Any] = [] def extents(self): if not self.pathes: @@ -143,11 +161,11 @@ class Part: class Path: - def __init__(self, path, params): + def __init__(self, path, params) -> None: self.path = path self.params = params - def __repr__(self): + def __repr__(self) -> str: l = len(self.path) # x1,y1 = self.path[0][1:3] if l>0: @@ -214,13 +232,13 @@ class Path: self.path = [p for n, p in enumerate(self.path) if p != self.path[n-1]] class Context: - def __init__(self, surface, *al, **ad): + def __init__(self, surface, *al, **ad) -> None: self._renderer = self._dwg = surface self._bounds = Extents() self._padding = PADDING - self._stack = [] + self._stack: list[Any] = [] self._m = Affine.translation(0, 0) self._xy = (0, 0) self._mxy = self._m * self._xy @@ -430,6 +448,7 @@ class SVGSurface(Surface): if "url" in md and md["url"]: self._addTag(w, 'dc:source', md["url"]) + self._addTag(w, 'dc:source', md["url_short"]) else: self._addTag(w, 'dc:source', md["cli"]) @@ -438,9 +457,12 @@ class SVGSurface(Surface): desc += "\n\n" + md["description"] desc += "\n\nCreated with Boxes.py (https://festi.info/boxes.py)\n" desc += "Command line: %s\n" % md["cli"] + desc += "Command line short: %s\n" % md["cli_short"] if md["url"]: desc += "Url: %s\n" % md["url"] + desc += "Url short: %s\n" % md["url_short"] desc += "SettingsUrl: %s\n" % md["url"].replace("&render=1", "") + desc += "SettingsUrl short: %s\n" % md["url_short"].replace("&render=1", "") self._addTag(w, 'dc:description', desc) # title @@ -461,11 +483,13 @@ Created with Boxes.py (https://festi.info/boxes.py) Creation date: {date} """.format(date=date, **md) - txt += "Command line (remove spaces between dashes): %s\n" % md["cli"] + txt += "Command line (remove spaces between dashes): %s\n" % md["cli_short"] if md["url"]: txt += "Url: %s\n" % md["url"] + txt += "Url short: %s\n" % md["url_short"] txt += "SettingsUrl: %s\n" % md["url"].replace("&render=1", "") + txt += "SettingsUrl short: %s\n" % md["url_short"].replace("&render=1", "") m = ET.Comment(txt.replace("--", "- -").replace("--", "- -")) # ---- m.tail = '\n' root.insert(0, m) @@ -495,7 +519,7 @@ Creation date: {date} tree = ET.ElementTree(svg) self._add_metadata(svg) - + for i, part in enumerate(self.parts): if not part.pathes: continue @@ -533,7 +557,7 @@ Creation date: {date} elif C == "T": m, text, params = c[3:] m = m * Affine.translation(0, -params['fs']) - tm = " ".join((f"{m[i]:.3f}" for i in (0, 3, 1, 4, 2, 5))) + tm = " ".join(f"{m[i]:.3f}" for i in (0, 3, 1, 4, 2, 5)) font, bold, italic = params['ff'] fontweight = ("normal", "bold")[bool(bold)] fontstyle = ("normal", "italic")[bool(italic)] @@ -567,7 +591,11 @@ Creation date: {date} t.set("stroke-width", f'{path.params["lw"]:.2f}') t.tail = "\n " t.tail = "\n" - tree.write(open(self._fname, "wb"), xml_declaration=True, method="xml") + reorder_attributes(tree) + f = io.BytesIO() + tree.write(f, encoding="utf-8", xml_declaration=True, method="xml") + f.seek(0) + return f class PSSurface(Surface): @@ -607,9 +635,12 @@ class PSSurface(Surface): desc += "%\n" desc += "%% Command line: %s\n" % md["cli"] + desc += "%% Command line short: %s\n" % md["cli_short"] if md["url"]: desc += f'%%Url: {md["url"]}\n' + desc += f'%%Url short: {md["url_short"]}\n' desc += f'%%SettingsUrl: {md["url"].replace("&render=1", "")}\n' + desc += f'%%SettingsUrl short: {md["url_short"].replace("&render=1", "")}\n' return desc def finish(self, inner_corners="loop"): @@ -618,12 +649,14 @@ class PSSurface(Surface): w = extents.width h = extents.height - f = open(self._fname, "w", encoding="latin1", errors="replace") + data = io.BytesIO() + f = codecs.getwriter('utf-8')(data) + + f.write(f"""%!PS-Adobe-2.0 EPSF-2.0 +%%BoundingBox: 0 0 {w:.0f} {h:.0f} +{self._metadata()} +%%EndComments - f.write("%!PS-Adobe-2.0\n") - f.write(f"%%BoundingBox: 0 0 {w:.0f} {h:.0f}\n") - f.write(self._metadata()) - f.write(""" 1 setlinecap 1 setlinejoin 0.0 0.0 0.0 setrgbcolor @@ -670,10 +703,9 @@ class PSSurface(Surface): ) elif C == "T": m, text, params = c[3:] - tm = " ".join((f"{m[i]:.3f}" for i in (0, 3, 1, 4, 2, 5))) - text = text.replace("(", "r\(").replace(")", r"\)") - color = " ".join((f"{c:.2f}" - for c in params["rgb"])) + tm = " ".join(f"{m[i]:.3f}" for i in (0, 3, 1, 4, 2, 5)) + text = text.replace("(", r"\(").replace(")", r"\)") + color = " ".join(f"{c:.2f}" for c in params["rgb"]) align = params.get('align', 'left') f.write(f"/{self.fonts[params['ff']]}-Latin1 findfont\n") f.write(f"{params['fs']} scalefont\n") @@ -704,8 +736,7 @@ class PSSurface(Surface): else rgb_to_svg_color(*path.params["rgb"]) ) if p: # todo: might be empty since text is not implemented yet - color = " ".join((f"{c:.2f}" - for c in path.params["rgb"])) + color = " ".join(f"{c:.2f}" for c in path.params["rgb"]) f.write("newpath\n") f.write("\n".join(p)) f.write("\n") @@ -719,7 +750,8 @@ showpage %%EOF """ ) - f.close() + data.seek(0) + return data class LBRN2Surface(Surface): @@ -757,7 +789,7 @@ class LBRN2Surface(Surface): tree = ET.ElementTree(svg) if self.dbg: print ("8", num) - + cs = ET.SubElement(svg, "CutSetting", Type="Cut") index = ET.SubElement(cs, "index", Value="3") # green layer (ETCHING) name = ET.SubElement(cs, "name", Value="Etch") @@ -797,7 +829,7 @@ class LBRN2Surface(Surface): index = ET.SubElement(cs, "index", Value="30") # T1 layer (ANNOTATIONS) name = ET.SubElement(cs, "name", Value="T1") # tool layer do not support names priority = ET.SubElement(cs, "priority", Value="7") # is not cut at all - + for i, part in enumerate(self.parts): if self.dbg: print ("7", num) if not part.pathes: @@ -808,7 +840,7 @@ class LBRN2Surface(Surface): children = ET.SubElement(gp, "Children") children.text = "\n " children.tail = "\n" - + for j, path in enumerate(part.pathes): myColor = self.lbrn2_colors[4*int(path.params["rgb"][0])+2*int(path.params["rgb"][1])+int(path.params["rgb"][2])] @@ -820,18 +852,20 @@ class LBRN2Surface(Surface): path.faster_edges(inner_corners) num = 0 cnt = 1 - ende = len(path.path)-1 - if self.dbg: + end = len(path.path) - 1 + if self.dbg: for c in path.path: print ("6",num, c) num += 1 num = 0 - + c = path.path[num] C, x, y = c[0:3] - if self.dbg: print("ende:" ,ende) - while num < ende or (C == "T" and num <= ende): #len(path.path): - if self.dbg: print ("0", num) + if self.dbg: + print("end:", end) + while num < end or (C == "T" and num <= end): # len(path.path): + if self.dbg: + print("0", num) c = path.path[num] if self.dbg: print("first: ", num, c) @@ -852,7 +886,7 @@ class LBRN2Surface(Surface): # do something with M done = False bspline = False - while done == False and num < ende: #len(path.path): + while done == False and num < end: # len(path.path): num += 1 c = path.path[num] if self.dbg: print ("next: ",num, c) @@ -883,7 +917,7 @@ class LBRN2Surface(Surface): print("unknown", c) if done == False: x0, y0 = x, y - + if start and points_equal(start[1], start[2], x0, y0): if bspline == False: pl.text = "LineClosed" @@ -913,7 +947,7 @@ class LBRN2Surface(Surface): else: hor = '0' ver = 1 # vertical is always bottom, text is shifted in box class - + pos = text.find('%') offs = 0 if pos >- 1: @@ -955,7 +989,7 @@ class LBRN2Surface(Surface): sh.text = "\n " sh.tail = "\n" xf = ET.SubElement(sh, "XForm") - xf.text = " ".join((f"{m[i]:.3f}" for i in (0, 3, 1, 4, 2, 5))) + xf.text = " ".join(f"{m[i]:.3f}" for i in (0, 3, 1, 4, 2, 5)) xf.tail = "\n" else: if self.dbg: print ("4", num) @@ -963,13 +997,17 @@ class LBRN2Surface(Surface): num += 1 url = self.metadata["url"].replace("&render=1", "") # remove render argument to get web form again - + pl = ET.SubElement(svg, "Notes", ShowOnLoad="1", Notes="File created by Boxes.py script, programmed by Florian Festi.\nLightburn output by Klaus Steinhammer.\n\nURL with settings:\n" + str(url)) pl.text = "" pl.tail = "\n" if self.dbg: print ("5", num) - tree.write(open(self._fname, "wb"), xml_declaration=True, method="xml", encoding="UTF-8") + f = io.BytesIO() + tree.write(f, encoding="utf-8", xml_declaration=True, method="xml") + f.seek(0) + return f + from random import random @@ -992,7 +1030,7 @@ def line_intersection(line1, line2): div = det(xdiff, ydiff) if div == 0: - # todo: deal with paralel line intersection / overlay + # todo: deal with parallel line intersection / overlay return False, None, None d = (det(*line1), det(*line2)) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/edges.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/edges.py index 256168b..18f6643 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/edges.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/edges.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,29 +12,31 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from __future__ import annotations -import math -import inspect import argparse +import inspect +import math import re -import abc +from abc import ABC, abstractmethod +from typing import Any from boxes import gears -def argparseSections(s): + +def argparseSections(s: str) -> list[float]: """ Parse sections parameter :param s: string to parse - """ - result = [] + result: list[float] = [] - s = re.split(r"\s|:", s) + parse = re.split(r"\s|:", s) try: - for part in s: + for part in parse: m = re.match(r"^(\d+(\.\d+)?)/(\d+)$", part) if m: n = int(m.group(3)) @@ -56,7 +56,8 @@ def argparseSections(s): return result -def getDescriptions(): + +def getDescriptions() -> dict: d = {edge.char: edge.description for edge in globals().values() if inspect.isclass(edge) and issubclass(edge, BaseEdge) and edge.char} @@ -67,64 +68,58 @@ def getDescriptions(): d['O'] = d['o'] + ' (other end)' d['P'] = d['p'] + ' (other end)' d['U'] = d['u'] + ' top side' - d['v'] = d['u'] + u' for 90° lid' - d['V'] = d['u'] + u' 90° lid' + d['v'] = d['u'] + ' for 90° lid' + d['V'] = d['u'] + ' 90° lid' return d -class BoltPolicy(object): +class BoltPolicy(ABC): """Abstract class Distributes (bed) bolts on a number of segments (fingers of a finger joint) - """ - def drawbolt(self, pos): + def drawbolt(self, pos) -> bool: """Add a bolt to this segment? :param pos: number of the finger - """ return False - def numFingers(self, numfingers): + def numFingers(self, numFingers: int) -> int: """Return next smaller, possible number of fingers - :param numfingers: number of fingers to aim for - + :param numFingers: number of fingers to aim for """ - return numfingers + return numFingers - def _even(self, numFingers): + def _even(self, numFingers: int) -> int: """ Return same or next smaller even number :param numFingers: - """ return (numFingers // 2) * 2 - def _odd(self, numFingers): + def _odd(self, numFingers: int) -> int: """ Return same or next smaller odd number :param numFingers: - """ if numFingers % 2: return numFingers - else: - return numFingers - 1 + return numFingers - 1 class Bolts(BoltPolicy): """Distribute a fixed number of bolts evenly""" - def __init__(self, bolts=1): + def __init__(self, bolts: int = 1) -> None: self.bolts = bolts - def numFingers(self, numFingers): + def numFingers(self, numFingers: int) -> int: if self.bolts % 2: self.fingers = self._even(numFingers) else: @@ -137,7 +132,6 @@ class Bolts(BoltPolicy): Return if this finger needs a bolt :param pos: number of this finger - """ if pos > self.fingers // 2: pos = self.fingers - pos @@ -149,14 +143,14 @@ class Bolts(BoltPolicy): return False return (math.floor((float(pos) * (self.bolts + 1) / self.fingers) - 0.01) != - math.floor((float(pos + 1) * (self.bolts + 1) / self.fingers) - 0.01)) + math.floor((float(pos + 1) * (self.bolts + 1) / self.fingers) - 0.01)) ############################################################################# ### Settings ############################################################################# -class Settings(object): +class Settings: """Generic Settings class Used by different other classes to store measurements and details. @@ -164,17 +158,17 @@ class Settings(object): of the material used. Overload the absolute_params and relative_params class attributes with - the suported keys and default values. The values are available via + the supported keys and default values. The values are available via attribute access. """ - absolute_params = {} - relative_params = {} + absolute_params: dict[str, Any] = {} # TODO find better typing. + relative_params: dict[str, Any] = {} # TODO find better typing. @classmethod def parserArguments(cls, parser, prefix=None, **defaults): prefix = prefix or cls.__name__[:-len("Settings")] - lines = cls.__doc__.split("\n") + lines = cls.__doc__.split("\n") # Parse doc string descriptions = {} @@ -195,7 +189,7 @@ class Settings(object): t = type(default[0]) for val in default: if (type(val) is not t or - type(val) not in (bool, int, float, str)): + type(val) not in (bool, int, float, str)): raise ValueError("Type not supported: %r", val) default = default[0] @@ -211,13 +205,13 @@ class Settings(object): else: t = type(default) - group.add_argument("--%s_%s" % (prefix, name), + group.add_argument(f"--{prefix}_{name}", type=t, action="store", default=default, choices=choices, help=descriptions.get(name)) - def __init__(self, thickness, relative=True, **kw): + def __init__(self, thickness, relative: bool = True, **kw) -> None: self.values = {} for name, value in self.absolute_params.items(): if isinstance(value, tuple): @@ -234,19 +228,18 @@ class Settings(object): self.values[name] = value * factor self.setValues(thickness, relative, **kw) - def edgeObjects(self, boxes, chars="", add=True): + def edgeObjects(self, boxes, chars: str = "", add: bool = True): """ Generate Edge objects using this kind of settings :param boxes: Boxes object :param chars: sequence of chars to be used by Edge objects :param add: add the resulting Edge objects to the Boxes object's edges - """ - edges = [] + edges: list[Any] = [] return self._edgeObjects(edges, boxes, chars, add) - def _edgeObjects(self, edges, boxes, chars, add): + def _edgeObjects(self, edges, boxes, chars: str, add: bool): for i, edge in enumerate(edges): try: char = chars[i] @@ -259,14 +252,13 @@ class Settings(object): boxes.addParts(edges) return edges - def setValues(self, thickness, relative=True, **kw): + def setValues(self, thickness, relative: bool = True, **kw): """ Set values :param thickness: thickness of the material used - :param relative: (Default value = True) Do scale by thickness - :param \*\*kw: parameters to set - + :param relative: Do scale by thickness (Default value = True) + :param kw: parameters to set """ factor = 1.0 if relative: @@ -277,32 +269,32 @@ class Settings(object): elif name in self.relative_params: self.values[name] = value * factor else: - raise ValueError("Unknown parameter for %s: %s" % ( - self.__class__.__name__, name)) + raise ValueError(f"Unknown parameter for {self.__class__.__name__}: {name}") self.checkValues() - def checkValues(self): + def checkValues(self) -> None: """ - Check if all values are in the right range. Raise ValueError if needed + Check if all values are in the right range. Raise ValueError if needed. """ - return + pass def __getattr__(self, name): if "values" in self.__dict__ and name in self.values: return self.values[name] raise AttributeError + ############################################################################# ### Edges ############################################################################# -class BaseEdge(object): +class BaseEdge(ABC): """Abstract base class for all Edges""" - char = None - description = "Abstract Edge Class" + char: str | None = None + description: str = "Abstract Edge Class" - def __init__(self, boxes, settings): + def __init__(self, boxes, settings) -> None: self.boxes = boxes self.ctx = boxes.ctx self.settings = settings @@ -311,30 +303,30 @@ class BaseEdge(object): """Hack for using unalter code form Boxes class""" return getattr(self.boxes, name) - @abc.abstractmethod + @abstractmethod def __call__(self, length, **kw): pass - def startwidth(self): + def startwidth(self) -> float: """Amount of space the beginning of the edge is set below the inner space of the part """ return 0.0 - def endwidth(self): + def endwidth(self) -> float: return self.startwidth() - def margin(self): + def margin(self) -> float: """Space needed right of the starting point""" return 0.0 - def spacing(self): + def spacing(self) -> float: """Space the edge needs outside of the inner space of the part""" return self.startwidth() + self.margin() - def startAngle(self): + def startAngle(self) -> float: """Not yet supported""" return 0.0 - def endAngle(self): + def endAngle(self) -> float: """Not yet supported""" return 0.0 @@ -348,7 +340,7 @@ class Edge(BaseEdge): def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw): """Draw edge of length mm""" if bedBolts: - # distribute the bolts aequidistantly + # distribute the bolts equidistantly interval_length = length / bedBolts.bolts if self.positive: d = (bedBoltSettings or self.bedBoltSettings)[0] @@ -356,11 +348,11 @@ class Edge(BaseEdge): self.hole(0.5 * interval_length, 0.5 * self.thickness, 0.5 * d) self.edge(interval_length, tabs= - (i == 0 or i == bedBolts.bolts - 1)) + (i == 0 or i == bedBolts.bolts - 1)) else: for i in range(bedBolts.bolts): self.bedBoltHole(interval_length, bedBoltSettings, tabs= - (i == 0 or i == bedBolts.bolts - 1)) + (i == 0 or i == bedBolts.bolts - 1)) else: self.edge(length, tabs=2) @@ -371,8 +363,9 @@ class OutSetEdge(Edge): description = "Straight Edge (outset by thickness)" positive = True - def startwidth(self): - return self.boxes.thickness + def startwidth(self) -> float: + return self.settings if self.settings is not None else self.boxes.thickness + ############################################################################# #### MountingEdge @@ -391,10 +384,10 @@ Values: * d_head : 6.5 : head diameter of mounting screw (in mm) """ - PARAM_IN = "straight edge, within" + PARAM_IN = "straight edge, within" PARAM_EXT = "straight edge, extended" PARAM_TAB = "mounting tab" - + PARAM_LEFT = "left" PARAM_BACK = "back" PARAM_RIGHT = "right" @@ -405,31 +398,29 @@ Values: "side": (PARAM_BACK, PARAM_LEFT, PARAM_RIGHT, PARAM_FRONT), "num": 2, "margin": 0.125, - "d_shaft" : 3.0, - "d_head" : 6.5 + "d_shaft": 3.0, + "d_head": 6.5 } - def edgeObjects(self, boxes, chars="G", add=True): + def edgeObjects(self, boxes, chars: str = "G", add: bool = True): edges = [MountingEdge(boxes, self)] return self._edgeObjects(edges, boxes, chars, add) class MountingEdge(BaseEdge): - description = """Edge with pear shaped mounting holes""" # for slide-on mounting using flat-head screws""" + description = """Edge with pear shaped mounting holes""" # for slide-on mounting using flat-head screws""" char = 'G' - def margin(self): + def margin(self) -> float: if self.settings.style == MountingSettings.PARAM_TAB: return 2.75 * self.boxes.thickness + self.settings.d_head - else: - return 0 - - def startwidth(self): + return 0.0 + + def startwidth(self) -> float: if self.settings.style == MountingSettings.PARAM_EXT: return 2.5 * self.boxes.thickness + self.settings.d_head - else: - return 0 - + return 0.0 + def __call__(self, length, **kw): if length == 0.0: return @@ -450,7 +441,7 @@ class MountingEdge(BaseEdge): if num != int(num): raise ValueError(f"MountingEdge: num needs to be an integer number") - + check_bounds(margin, 0, 0.5, "margin") if not dh == 0: if not dh > ds: @@ -460,9 +451,9 @@ class MountingEdge(BaseEdge): count = max(1, int(num)) if count > 1: margin_ = length * margin - gap = (length - 2 * margin_ - width*count) / (count - 1) + gap = (length - 2 * margin_ - width * count) / (count - 1) if gap < width: - count = int(((length - 2 * margin + width) / (2 * width)) - 0.5) + count = int(((length - 2 * margin + width) / (2 * width)) - 0.5) if count < 1: self.edge(length) return @@ -470,37 +461,37 @@ class MountingEdge(BaseEdge): margin_ = (length - width) / 2 gap = 0 else: - gap = (length - 2 * margin_ - width*count) / (count - 1) + gap = (length - 2 * margin_ - width * count) / (count - 1) else: margin_ = (length - width) / 2 gap = 0 - + if style == MountingSettings.PARAM_TAB: - + # The edge until the first groove self.edge(margin_, tabs=1) - + for i in range(count): if i > 0: self.edge(gap) - self.corner(-90,self.thickness/2) - self.edge(dh+1.5*ds-self.thickness/4-dh/2) - self.corner(90,self.thickness+dh/2) + self.corner(-90, self.thickness / 2) + self.edge(dh + 1.5 * ds - self.thickness / 4 - dh / 2) + self.corner(90, self.thickness + dh / 2) self.corner(-90) self.corner(90) - self.mountingHole(0,self.thickness*1.25+ds/2,ds,dh,-90) - self.corner(90,self.thickness+dh/2) - self.edge(dh+1.5*ds-self.thickness/4-dh/2) - self.corner(-90,self.thickness/2) - + self.mountingHole(0, self.thickness * 1.25 + ds / 2, ds, dh, -90) + self.corner(90, self.thickness + dh / 2) + self.edge(dh + 1.5 * ds - self.thickness / 4 - dh / 2) + self.corner(-90, self.thickness / 2) + # The edge until the end self.edge(margin_, tabs=1) else: x = margin_ for i in range(count): - x += width/2 - self.mountingHole(x,ds/2+self.thickness*1.5,ds,dh,-90) - x += width/2 + x += width / 2 + self.mountingHole(x, ds / 2 + self.thickness * 1.5, ds, dh, -90) + x += width / 2 x += gap self.edge(length) @@ -541,34 +532,31 @@ Values: "interleave": False, } - def edgeObjects(self, boxes, chars="zZ", add=True): + def edgeObjects(self, boxes, chars: str = "zZ", add: bool = True): edges = [GroovedEdge(boxes, self), GroovedEdgeCounterPart(boxes, self)] return self._edgeObjects(edges, boxes, chars, add) class GroovedEdgeBase(BaseEdge): - def is_inverse(self): + def is_inverse(self) -> bool: return self.settings.inverse != self.inverse - - def groove_arc(self, width, angle=90, inv=-1.0): + def groove_arc(self, width, angle: float = 90.0, inv: float = -1.0) -> None: side_length = width / math.sin(math.radians(angle)) / 2 self.corner(inv * -angle) self.corner(inv * angle, side_length) self.corner(inv * angle, side_length) self.corner(inv * -angle) - - def groove_soft_arc(self, width, angle=60, inv=-1.0): + def groove_soft_arc(self, width, angle: float = 60.0, inv: float = -1.0) -> None: side_length = width / math.sin(math.radians(angle)) / 4 self.corner(inv * -angle, side_length) self.corner(inv * angle, side_length) self.corner(inv * angle, side_length) self.corner(inv * -angle, side_length) - - def groove_triangle(self, width, angle=45, inv=-1.0): + def groove_triangle(self, width, angle: float = 45.0, inv: float = -1.0) -> None: side_length = width / math.cos(math.radians(angle)) / 2 self.corner(inv * -angle) self.edge(side_length) @@ -576,7 +564,6 @@ class GroovedEdgeBase(BaseEdge): self.edge(side_length) self.corner(inv * -angle) - def __call__(self, length, **kw): if length == 0.0: return @@ -659,7 +646,7 @@ Values: * absolute_params - * style : "wave : "wave" or "bumps" + * style : "wave" : "wave" or "bumps" * outset : True : extend outward the straight edge * relative (in multiples of thickness) @@ -677,15 +664,16 @@ Values: "depth": 0.3, } - def edgeObjects(self, boxes, chars="g", add=True): + def edgeObjects(self, boxes, chars: str = "g", add: bool = True): edges = [GrippingEdge(boxes, self)] return self._edgeObjects(edges, boxes, chars, add) + class GrippingEdge(BaseEdge): description = """Corrugated edge useful as an gipping area""" char = 'g' - def wave(self, length): + def wave(self, length) -> None: depth = self.settings.depth grooves = int(length // (depth * 2.0)) + 1 depth = length / grooves / 4.0 @@ -696,7 +684,7 @@ class GrippingEdge(BaseEdge): self.corner(o * 180, depth) self.corner(o * -90, depth) - def bumps(self, length): + def bumps(self, length) -> None: depth = self.settings.depth grooves = int(length // (depth * 2.0)) + 1 depth = length / grooves / 2.0 @@ -719,11 +707,10 @@ class GrippingEdge(BaseEdge): self.edge(depth) self.corner(90) - def margin(self): + def margin(self) -> float: if self.settings.outset: return self.settings.depth - else: - return 0.0 + return 0.0 def __call__(self, length, **kw): if length == 0.0: @@ -735,21 +722,21 @@ class CompoundEdge(BaseEdge): """Edge composed of multiple different Edges""" description = "Compound Edge" - def __init__(self, boxes, types, lengths): - super(CompoundEdge, self).__init__(boxes, None) + def __init__(self, boxes, types, lengths) -> None: + super().__init__(boxes, None) self.types = [self.edges.get(edge, edge) for edge in types] self.lengths = lengths self.length = sum(lengths) - def startwidth(self): + def startwidth(self) -> float: return self.types[0].startwidth() - def endwidth(self): + def endwidth(self) -> float: return self.types[-1].endwidth() - def margin(self): - return max((e.margin() + e.startwidth() for e in self.types)) - self.types[0].startwidth() + def margin(self) -> float: + return max(e.margin() + e.startwidth() for e in self.types) - self.types[0].startwidth() def __call__(self, length, **kw): if length and abs(length - self.length) > 1E-5: @@ -767,12 +754,12 @@ class CompoundEdge(BaseEdge): ############################################################################# class Slot(BaseEdge): - """Edge with an slot to slid another pice through """ + """Edge with a slot to slide another piece through """ description = "Slot" - def __init__(self, boxes, depth): - super(Slot, self).__init__(boxes, None) + def __init__(self, boxes, depth) -> None: + super().__init__(boxes, None) self.depth = depth @@ -793,20 +780,20 @@ class SlottedEdge(BaseEdge): """Edge with multiple slots""" description = "Straight Edge with slots" - def __init__(self, boxes, sections, edge="e", slots=0): - super(SlottedEdge, self).__init__(boxes, Settings(boxes.thickness)) + def __init__(self, boxes, sections, edge: str = "e", slots: int = 0) -> None: + super().__init__(boxes, Settings(boxes.thickness)) self.edge = self.edges.get(edge, edge) self.sections = sections self.slots = slots - def startwidth(self): + def startwidth(self) -> float: return self.edge.startwidth() - def endwidth(self): + def endwidth(self) -> float: return self.edge.endwidth() - def margin(self): + def margin(self) -> float: return self.edge.margin() def __call__(self, length, **kw): @@ -844,12 +831,13 @@ Values: * edge_width : 1.0 : space below holes of FingerHoleEdge (multiples of thickness) * play : 0.0 : extra space to allow finger move in and out (multiples of thickness) * extra_length : 0.0 : extra material to grind away burn marks (multiples of thickness) + * bottom_lip : 0.0 : height of the bottom lips sticking out (multiples of thickness) FingerHoleEdge only! """ absolute_params = { - "style" : ("rectangular", "springs", "barbs", "snap"), + "style": ("rectangular", "springs", "barbs", "snap"), "surroundingspaces": 2.0, - "angle" : 90.0, + "angle": 90.0, } relative_params = { @@ -857,29 +845,31 @@ Values: "finger": 2.0, "width": 1.0, "edge_width": 1.0, - "play" : 0.0, - "extra_length" : 0.0, + "play": 0.0, + "extra_length": 0.0, + "bottom_lip": 0.0, } - def checkValues(self): + def checkValues(self) -> None: if abs(self.space + self.finger) < 0.1: raise ValueError("FingerJointSettings: space + finger must not be close to zero") - def edgeObjects(self, boxes, chars="fFh", add=True): + def edgeObjects(self, boxes, chars: str = "fFh", add: bool = True): edges = [FingerJointEdge(boxes, self), FingerJointEdgeCounterPart(boxes, self), FingerHoleEdge(boxes, self), - ] + ] return self._edgeObjects(edges, boxes, chars, add) -class FingerJointBase: - def calcFingers(self, length, bedBolts): - space, finger = self.settings.space, self.settings.finger - fingers = int((length - (self.settings.surroundingspaces - 1) * space) // - (space + finger)) +class FingerJointBase(ABC): + """Abstract base class for finger joint.""" + + def calcFingers(self, length: float, bedBolts) -> tuple[int, float]: + space, finger = self.settings.space, self.settings.finger # type: ignore + fingers = int((length - (self.settings.surroundingspaces - 1) * space) // (space + finger)) # type: ignore # shrink surrounding space up to half a thickness each side - if fingers == 0 and length > finger + 1.0 * self.settings.thickness: + if fingers == 0 and length > finger + 1.0 * self.settings.thickness: # type: ignore fingers = 1 if not finger: fingers = 0 @@ -893,22 +883,22 @@ class FingerJointBase: return fingers, leftover - def fingerLength(self, angle): + def fingerLength(self, angle: float) -> tuple[float, float]: # sharp corners - if angle >=90 or angle <= -90: - return self.settings.thickness + self.settings.extra_length, 0 + if angle >= 90 or angle <= -90: + return self.settings.thickness + self.settings.extra_length, 0.0 # type: ignore # inner blunt corners if angle < 0: - return (math.sin(math.radians(-angle)) * self.settings.thickness + - self.settings.extra_length), 0 + return (math.sin(math.radians(-angle)) * self.settings.thickness + self.settings.extra_length), 0 # type: ignore # 0 to 90 (blunt corners) - a = 90 - (180-angle) / 2.0 - fingerlength = self.settings.thickness * math.tan(math.radians(a)) - b = 90-2*a + a = 90 - (180 - angle) / 2.0 + fingerlength = self.settings.thickness * math.tan(math.radians(a)) # type: ignore + b = 90 - 2 * a spacerecess = -math.sin(math.radians(b)) * fingerlength - return fingerlength + self.settings.extra_length, spacerecess + return fingerlength + self.settings.extra_length, spacerecess # type: ignore + class FingerJointEdge(BaseEdge, FingerJointBase): """Finger joint edge """ @@ -916,35 +906,35 @@ class FingerJointEdge(BaseEdge, FingerJointBase): description = "Finger Joint" positive = True - def draw_finger(self, f, h, style, positive=True, firsthalf=True): + def draw_finger(self, f, h, style, positive: bool = True, firsthalf: bool = True) -> None: t = self.settings.thickness if positive: if style == "springs": self.polyline( - 0, -90, 0.8*h, (90, 0.2*h), - 0.1 * h, 90, 0.9*h, -180, 0.9*h, 90, - f - 0.6*h, - 90, 0.9*h, -180, 0.9*h, 90, 0.1*h, - (90, 0.2 *h), 0.8*h, -90) + 0, -90, 0.8 * h, (90, 0.2 * h), + 0.1 * h, 90, 0.9 * h, -180, 0.9 * h, 90, + f - 0.6 * h, + 90, 0.9 * h, -180, 0.9 * h, 90, 0.1 * h, + (90, 0.2 * h), 0.8 * h, -90) elif style == "barbs": - n = int((h-0.1*t) // (0.3*t)) + n = int((h - 0.1 * t) // (0.3 * t)) a = math.degrees(math.atan(0.5)) - l = 5**0.5 - poly = [h - n*0.3*t] + \ - ([-45, 0.1*2**0.5*t, 45+a, l*0.1*t, -a, 0] * n) + l = 5 ** 0.5 + poly = [h - n * 0.3 * t] + \ + ([-45, 0.1 * 2 ** 0.5 * t, 45 + a, l * 0.1 * t, -a, 0] * n) self.polyline( 0, -90, *poly, 90, f, 90, *reversed(poly), -90 ) elif style == "snap" and f > 1.9 * t: a12 = math.degrees(math.atan(0.5)) l12 = t / math.cos(math.radians(a12)) - d = 4*t - d2 = d + 1*t - a = math.degrees(math.atan((0.5*t)/(h+d2))) - l = (h+d2) / math.cos(math.radians(a)) - poly = [0, 90, d, -180, d+h, -90, 0.5*t, 90+a12, l12, 90-a12, - 0.5*t, 90-a, l, +a, 0, (-180, 0.1*t), h+d2, 90, f-1.7*t, 90-a12, l12, a12, h, -90, 0] + d = 4 * t + d2 = d + 1 * t + a = math.degrees(math.atan((0.5 * t) / (h + d2))) + l = (h + d2) / math.cos(math.radians(a)) + poly = [0, 90, d, -180, d + h, -90, 0.5 * t, 90 + a12, l12, 90 - a12, + 0.5 * t, 90 - a, l, +a, 0, (-180, 0.1 * t), h + d2, 90, f - 1.7 * t, 90 - a12, l12, a12, h, -90, 0] if firsthalf: poly = list(reversed(poly)) self.polyline(*poly) @@ -967,7 +957,7 @@ class FingerJointEdge(BaseEdge, FingerJointBase): # not enough space for normal fingers - use small rectangular one if (fingers == 0 and f and - leftover > 0.75*thickness and leftover > 4*play): + leftover > 0.75 * thickness and leftover > 4 * play): fingers = 1 f = leftover = leftover / 2.0 bedBolts = None @@ -980,8 +970,8 @@ class FingerJointEdge(BaseEdge, FingerJointBase): self.edge(leftover / 2.0, tabs=1) - l1,l2 = self.fingerLength(self.settings.angle) - h = l1-l2 + l1, l2 = self.fingerLength(self.settings.angle) + h = l1 - l2 d = (bedBoltSettings or self.bedBoltSettings)[0] @@ -996,21 +986,20 @@ class FingerJointEdge(BaseEdge, FingerJointBase): else: self.edge(s) self.draw_finger(f, h, style, - positive, i < fingers//2) + positive, i < fingers // 2) self.edge(leftover / 2.0, tabs=1) - def margin(self): + def margin(self) -> float: """ """ widths = self.fingerLength(self.settings.angle) if self.positive: if self.settings.style == "snap": return widths[0] - widths[1] + self.settings.thickness return widths[0] - widths[1] - else: - return 0 + return 0.0 - def startwidth(self): + def startwidth(self) -> float: widths = self.fingerLength(self.settings.angle) return widths[self.positive] @@ -1025,7 +1014,7 @@ class FingerJointEdgeCounterPart(FingerJointEdge): class FingerHoles(FingerJointBase): """Hole matching a finger joint edge""" - def __init__(self, boxes, settings): + def __init__(self, boxes, settings) -> None: self.boxes = boxes self.ctx = boxes.ctx self.settings = settings @@ -1040,7 +1029,6 @@ class FingerHoles(FingerJointBase): :param angle: (Default value = 90) :param bedBolts: (Default value = None) :param bedBoltSettings: (Default value = None) - """ with self.boxes.saved_context(): self.boxes.moveTo(x, y, angle) @@ -1051,7 +1039,7 @@ class FingerHoles(FingerJointBase): # not enough space for normal fingers - use small rectangular one if (fingers == 0 and f and - leftover > 0.75*self.settings.thickness and leftover > 4*p): + leftover > 0.75 * self.settings.thickness and leftover > 4 * p): fingers = 1 f = leftover = leftover / 2.0 bedBolts = None @@ -1067,19 +1055,20 @@ class FingerHoles(FingerJointBase): self.boxes.hole(pos - 0.5 * s, 0, d * 0.5) self.boxes.rectangularHole(pos + 0.5 * f, 0, - f+p, self.settings.width+p) + f + p, self.settings.width + p) + class FingerHoleEdge(BaseEdge): """Edge with holes for a parallel finger joint""" char = 'h' description = "Edge (parallel Finger Joint Holes)" - def __init__(self, boxes, fingerHoles=None, **kw): + def __init__(self, boxes, fingerHoles=None, **kw) -> None: settings = None if isinstance(fingerHoles, Settings): settings = fingerHoles fingerHoles = FingerHoles(boxes, settings) - super(FingerHoleEdge, self).__init__(boxes, settings, **kw) + super().__init__(boxes, settings, **kw) self.fingerHoles = fingerHoles or boxes.fingerHolesAt @@ -1089,12 +1078,23 @@ class FingerHoleEdge(BaseEdge): self.fingerHoles( 0, self.burn + dist + self.settings.thickness / 2, length, 0, bedBolts=bedBolts, bedBoltSettings=bedBoltSettings) + if self.settings.bottom_lip: + h = self.settings.bottom_lip + \ + self.fingerHoles.settings.edge_width + sp = self.boxes.spacing + self.moveTo(-sp / 2, -h - sp) + self.rectangularWall(length - 1.05 * self.boxes.thickness, h) self.edge(length, tabs=2) - def startwidth(self): + def startwidth(self) -> float: """ """ return self.fingerHoles.settings.edge_width + self.settings.thickness + def margin(self) -> float: + if self.settings.bottom_lip: + return self.settings.bottom_lip + self.fingerHoles.settings.edge_width + self.boxes.spacing + return 0.0 + class CrossingFingerHoleEdge(Edge): """Edge with holes for finger joints 90° above""" @@ -1102,15 +1102,19 @@ class CrossingFingerHoleEdge(Edge): description = "Edge (orthogonal Finger Joint Holes)" char = '|' - def __init__(self, boxes, height, fingerHoles=None, **kw): - super(CrossingFingerHoleEdge, self).__init__(boxes, None, **kw) + def __init__(self, boxes, height, fingerHoles=None, outset: float = 0.0, **kw) -> None: + super().__init__(boxes, None, **kw) self.fingerHoles = fingerHoles or boxes.fingerHolesAt self.height = height + self.outset = outset def __call__(self, length, **kw): - self.fingerHoles(length / 2.0, self.burn, self.height) - super(CrossingFingerHoleEdge, self).__call__(length) + self.fingerHoles(length / 2.0, self.outset + self.burn, self.height) + super().__call__(length) + + def startwidth(self) -> float: + return self.outset ############################################################################# @@ -1131,6 +1135,7 @@ Values: * height : 2.0 : height of the feet (multiples of thickness) * width : 4.0 : width of the feet (multiples of thickness) * holedistance : 1.0 : distance from finger holes to bottom edge (multiples of thickness) + * bottom_stabilizers : 0.0 : height of strips to be glued to the inside of bottom edges (multiples of thickness) """ @@ -1142,15 +1147,16 @@ Values: "height": 2.0, "width": 4.0, "holedistance": 1.0, + "bottom_stabilizers": 0.0, } - def checkValues(self): + def checkValues(self) -> None: if self.angle < 20: raise ValueError("StackableSettings: 'angle' is too small. Use value >= 20") if self.angle > 260: raise ValueError("StackableSettings: 'angle' is too big. Use value < 260") - def edgeObjects(self, boxes, chars="sSšŠ", add=True, fingersettings=None): + def edgeObjects(self, boxes, chars: str = "sSšŠ", add: bool = True, fingersettings=None): fingersettings = fingersettings or boxes.edges["f"].settings edges = [StackableEdge(boxes, self, fingersettings), StackableEdgeTop(boxes, self, fingersettings), @@ -1159,6 +1165,7 @@ Values: ] return self._edgeObjects(edges, boxes, chars, add) + class StackableBaseEdge(BaseEdge): """Edge for having stackable Boxes. The Edge creates feet on the bottom and has matching recesses on the top corners.""" @@ -1167,7 +1174,7 @@ class StackableBaseEdge(BaseEdge): description = "Abstract Stackable class" bottom = True - def __init__(self, boxes, settings, fingerjointsettings): + def __init__(self, boxes, settings, fingerjointsettings) -> None: super().__init__(boxes, settings) self.fingerjointsettings = fingerjointsettings @@ -1178,6 +1185,13 @@ class StackableBaseEdge(BaseEdge): l = r * math.sin(math.radians(s.angle)) p = 1 if self.bottom else -1 + if self.bottom and s.bottom_stabilizers: + with self.saved_context(): + sp = self.boxes.spacing + self.moveTo(-sp / 2, -s.height - sp) + self.rectangularWall(length - 1.05 * self.boxes.thickness, + s.bottom_stabilizers) + self.boxes.edge(s.width, tabs=1) self.boxes.corner(p * s.angle, r) self.boxes.corner(-p * s.angle, r) @@ -1189,11 +1203,18 @@ class StackableBaseEdge(BaseEdge): def _height(self): return self.settings.height + self.settings.holedistance + self.settings.thickness - def startwidth(self): + def startwidth(self) -> float: return self._height() if self.bottom else 0 - def margin(self): - return 0 if self.bottom else self.settings.height + def margin(self) -> float: + if self.bottom: + if self.settings.bottom_stabilizers: + return self.settings.bottom_stabilizers + self.boxes.spacing + else: + return 0 + else: + return self.settings.height + class StackableEdge(StackableBaseEdge): """Edge for having stackable Boxes. The Edge creates feet on the bottom @@ -1210,11 +1231,13 @@ class StackableEdge(StackableBaseEdge): length, 0) super().__call__(length, **kw) + class StackableEdgeTop(StackableBaseEdge): char = "S" description = "Stackable (top)" bottom = False + class StackableFeet(StackableBaseEdge): char = "š" description = "Stackable feet (bottom)" @@ -1222,12 +1245,13 @@ class StackableFeet(StackableBaseEdge): def _height(self): return self.settings.height + class StackableHoleEdgeTop(StackableBaseEdge): char = "Š" description = "Stackable edge with finger holes (top)" bottom = False - def startwidth(self): + def startwidth(self) -> float: return self.settings.thickness + self.settings.holedistance def __call__(self, length, **kw): @@ -1238,6 +1262,7 @@ class StackableHoleEdgeTop(StackableBaseEdge): length, 0) super().__call__(length, **kw) + ############################################################################# #### Hinges ############################################################################# @@ -1273,11 +1298,11 @@ Values: "grip_length": 0, } - def checkValues(self): + def checkValues(self) -> None: if self.axle / self.thickness < 0.1: raise ValueError("HingeSettings: 'axle' need to be at least 0.1 strong") - def edgeObjects(self, boxes, chars="iIjJkK", add=True): + def edgeObjects(self, boxes, chars: str = "iIjJkK", add: bool = True): edges = [ Hinge(boxes, self, 1), HingePin(boxes, self, 1), @@ -1288,12 +1313,13 @@ Values: ] return self._edgeObjects(edges, boxes, chars, add) + class Hinge(BaseEdge): char = 'i' description = "Straight edge with hinge eye" - def __init__(self, boxes, settings=None, layout=1): - super(Hinge, self).__init__(boxes, settings) + def __init__(self, boxes, settings=None, layout: int = 1) -> None: + super().__init__(boxes, settings) if not (0 < layout <= 3): raise ValueError("layout must be 1, 2 or 3 (got %i)" % layout) @@ -1302,57 +1328,64 @@ class Hinge(BaseEdge): self.char = "eijk"[layout] self.description = self.description + ('', ' (start)', ' (end)', ' (both ends)')[layout] - def margin(self): - return 3 * self.settings.thickness + def margin(self) -> float: + t: float = self.settings.thickness + if self.settings.style == "outset": + r = 0.5 * self.settings.axle + alpha = math.degrees(math.asin(0.5 * t / r)) + pos = math.cos(math.radians(alpha)) * r + return 1.5 * t + pos + else: # flush + return 0.5 * t + 0.5 * self.settings.axle + self.settings.hingestrength - def outset(self, _reversed=False): - t = self.settings.thickness + def outset(self, _reversed: bool = False) -> None: + t: float = self.settings.thickness r = 0.5 * self.settings.axle alpha = math.degrees(math.asin(0.5 * t / r)) pinl = (self.settings.axle ** 2 - self.settings.thickness ** 2) ** 0.5 * self.settings.pinwidth pos = math.cos(math.radians(alpha)) * r hinge = ( - 0, - 90 - alpha, 0, - (-360, r), 0, - 90 + alpha, + 0., + 90. - alpha, 0., + (-360., r), 0., + 90. + alpha, t, - 90, + 90., 0.5 * t, - (180, t + pos), 0, - (-90, 0.5 * t), 0 + (180., t + pos), 0., + (-90., 0.5 * t), 0. ) if _reversed: - hinge = reversed(hinge) + hinge = reversed(hinge) # type: ignore self.polyline(*hinge) self.boxes.rectangularHole(-pos, -0.5 * t, pinl, self.settings.thickness) else: self.boxes.rectangularHole(pos, -0.5 * t, pinl, self.settings.thickness) self.polyline(*hinge) - def outsetlen(self): + def outsetlen(self) -> float: t = self.settings.thickness r = 0.5 * self.settings.axle alpha = math.degrees(math.asin(0.5 * t / r)) pos = math.cos(math.radians(alpha)) * r - return 2 * pos + 1.5 * t + return 2.0 * pos + 1.5 * t - def flush(self, _reversed=False): + def flush(self, _reversed: bool = False) -> None: t = self.settings.thickness hinge = ( - 0, -90, + 0., -90., 0.5 * t, - (180, 0.5 * self.settings.axle + self.settings.hingestrength), 0, - (-90, 0.5 * t), 0 + (180., 0.5 * self.settings.axle + self.settings.hingestrength), 0., + (-90., 0.5 * t), 0. ) pos = 0.5 * self.settings.axle + self.settings.hingestrength pinl = (self.settings.axle ** 2 - self.settings.thickness ** 2) ** 0.5 * self.settings.pinwidth if _reversed: - hinge = reversed(hinge) + hinge = reversed(hinge) # type: ignore self.hole(0.5 * t + pos, -0.5 * t, 0.5 * self.settings.axle) self.boxes.rectangularHole(0.5 * t + pos, -0.5 * t, pinl, self.settings.thickness) else: @@ -1361,8 +1394,8 @@ class Hinge(BaseEdge): self.polyline(*hinge) - def flushlen(self): - return self.settings.axle + 2 * self.settings.hingestrength + 0.5 * self.settings.thickness + def flushlen(self) -> float: + return self.settings.axle + 2.0 * self.settings.hingestrength + 0.5 * self.settings.thickness def __call__(self, l, **kw): hlen = getattr(self, self.settings.style + 'len', self.outsetlen)() @@ -1381,8 +1414,8 @@ class HingePin(BaseEdge): char = 'I' description = "Edge with hinge pin" - def __init__(self, boxes, settings=None, layout=1): - super(HingePin, self).__init__(boxes, settings) + def __init__(self, boxes, settings=None, layout: int = 1) -> None: + super().__init__(boxes, settings) if not (0 < layout <= 3): raise ValueError("layout must be 1, 2 or 3 (got %i)" % layout) @@ -1391,47 +1424,45 @@ class HingePin(BaseEdge): self.char = "EIJK"[layout] self.description = self.description + ('', ' (start)', ' (end)', ' (both ends)')[layout] - def startwidth(self): + def startwidth(self) -> float: if self.layout & 1: - return 0 - else: - return self.settings.outset * self.boxes.thickness + return 0.0 + return self.settings.outset * self.boxes.thickness - def endwidth(self): + def endwidth(self) -> float: if self.layout & 2: - return 0 - else: - return self.settings.outset * self.boxes.thickness + return 0.0 + return self.settings.outset * self.boxes.thickness - def margin(self): + def margin(self) -> float: return self.settings.thickness - def outset(self, _reversed=False): - t = self.settings.thickness + def outset(self, _reversed: bool = False) -> None: + t: float = self.settings.thickness r = 0.5 * self.settings.axle alpha = math.degrees(math.asin(0.5 * t / r)) pos = math.cos(math.radians(alpha)) * r pinl = (self.settings.axle ** 2 - self.settings.thickness ** 2) ** 0.5 * self.settings.pinwidth - pin = (pos - 0.5 * pinl, -90, - t, 90, + pin = (pos - 0.5 * pinl, -90., + t, 90., pinl, - 90, + 90., t, - -90) + -90.) if self.settings.outset: - pin += ( + pin += ( # type: ignore pos - 0.5 * pinl + 1.5 * t, - -90, + -90., t, - 90, - 0, + 90., + 0., ) else: - pin += (pos - 0.5 * pinl,) + pin += (pos - 0.5 * pinl,) # type: ignore if _reversed: - pin = reversed(pin) + pin = reversed(pin) # type: ignore self.polyline(*pin) @@ -1443,32 +1474,31 @@ class HingePin(BaseEdge): if self.settings.outset: return 2 * pos + 1.5 * self.settings.thickness - else: - return 2 * pos + return 2 * pos - def flush(self, _reversed=False): - t = self.settings.thickness + def flush(self, _reversed: bool = False) -> None: + t: float = self.settings.thickness pinl = (self.settings.axle ** 2 - t ** 2) ** 0.5 * self.settings.pinwidth d = (self.settings.axle - pinl) / 2.0 - pin = (self.settings.hingestrength + d, -90, - t, 90, + pin = (self.settings.hingestrength + d, -90., + t, 90., pinl, - 90, + 90., t, - -90, d) + -90., d) if self.settings.outset: - pin += ( - 0, + pin += ( # type: ignore + 0., self.settings.hingestrength + 0.5 * t, - -90, + -90., t, - 90, - 0, + 90., + 0., ) if _reversed: - pin = reversed(pin) + pin = reversed(pin) # type: ignore self.polyline(*pin) @@ -1482,7 +1512,7 @@ class HingePin(BaseEdge): def __call__(self, l, **kw): plen = getattr(self, self.settings.style + 'len', self.outsetlen)() - glen = l * self.settings.grip_percentage + \ + glen = l * self.settings.grip_percentage / 100 + \ self.settings.grip_length if not self.settings.outset: @@ -1503,6 +1533,7 @@ class HingePin(BaseEdge): self.edge(l - plen - glen, tabs=2) getattr(self, self.settings.style, self.outset)(True) + ############################################################################# #### Chest Hinge ############################################################################# @@ -1523,42 +1554,42 @@ Values: """ relative_params = { - "pin_height" : 2.0, - "hinge_strength" : 1.0, - "play" : 0.1, - } - - absolute_params = { - "finger_joints_on_box" : False, - "finger_joints_on_lid" : False, + "pin_height": 2.0, + "hinge_strength": 1.0, + "play": 0.1, } - def checkValues(self): + absolute_params = { + "finger_joints_on_box": False, + "finger_joints_on_lid": False, + } + + def checkValues(self) -> None: if self.pin_height / self.thickness < 1.2: raise ValueError("ChestHingeSettings: 'pin_height' must be >= 1.2") def pinheight(self): - return ((0.9*self.pin_height)**2-self.thickness**2)**0.5 + return ((0.9 * self.pin_height) ** 2 - self.thickness ** 2) ** 0.5 - def edgeObjects(self, boxes, chars="oOpPqQ", add=True): + def edgeObjects(self, boxes, chars: str = "oOpPqQ", add: bool = True): edges = [ ChestHinge(boxes, self), - ChestHinge(boxes, self, 1), + ChestHinge(boxes, self, True), ChestHingeTop(boxes, self), - ChestHingeTop(boxes, self, 1), + ChestHingeTop(boxes, self, True), ChestHingePin(boxes, self), ChestHingeFront(boxes, self), ] return self._edgeObjects(edges, boxes, chars, add) -class ChestHinge(BaseEdge): +class ChestHinge(BaseEdge): description = "Edge with chest hinge" char = "o" - def __init__(self, boxes, settings=None, reversed=False): - super(ChestHinge, self).__init__(boxes, settings) + def __init__(self, boxes, settings=None, reversed: bool = False) -> None: + super().__init__(boxes, settings) self.reversed = reversed self.char = "oO"[reversed] @@ -1570,20 +1601,20 @@ class ChestHinge(BaseEdge): s = self.settings.hinge_strength pinh = self.settings.pinheight() if self.reversed: - self.hole(l+t, 0, p, tabs=4) - self.rectangularHole(l+0.5*t, -0.5*pinh, t, pinh) + self.hole(l + t, 0, p, tabs=4) + self.rectangularHole(l + 0.5 * t, -0.5 * pinh, t, pinh) else: - self.hole(-t, -s-p, p, tabs=4) - self.rectangularHole(-0.5*t, -s-p-0.5*pinh, t, pinh) + self.hole(-t, -s - p, p, tabs=4) + self.rectangularHole(-0.5 * t, -s - p - 0.5 * pinh, t, pinh) if self.settings.finger_joints_on_box: - final_segment = t-s - draw_rest_of_edge = lambda : self.edges["F"](l-p) + final_segment = t - s + draw_rest_of_edge = lambda: self.edges["F"](l - p) else: - final_segment = l+t-p-s - draw_rest_of_edge = lambda : None + final_segment = l + t - p - s + draw_rest_of_edge = lambda: None - poly = (0, -180, t, (270, p+s), 0, -90, final_segment) + poly = (0, -180, t, (270, p + s), 0, -90, final_segment) if self.reversed: draw_rest_of_edge() @@ -1592,30 +1623,29 @@ class ChestHinge(BaseEdge): self.polyline(*poly) draw_rest_of_edge() - def margin(self): + def margin(self) -> float: if self.reversed: - return 0*(self.settings.pin_height+self.settings.hinge_strength) - else: - return 1*(self.settings.pin_height+self.settings.hinge_strength) + return 0.0 + return 1 * (self.settings.pin_height + self.settings.hinge_strength) - def startwidth(self): + def startwidth(self) -> float: if self.reversed: - return self.settings.pin_height+self.settings.hinge_strength - return 0 + return self.settings.pin_height + self.settings.hinge_strength + return 0.0 - def endwidth(self): + def endwidth(self) -> float: if self.reversed: - return 0 - return self.settings.pin_height+self.settings.hinge_strength + return 0.0 + return self.settings.pin_height + self.settings.hinge_strength + class ChestHingeTop(ChestHinge): - - "Edge above a chest hinge" + """Edge above a chest hinge""" char = "p" - def __init__(self, boxes, settings=None, reversed=False): - super(ChestHingeTop, self).__init__(boxes, settings) + def __init__(self, boxes, settings=None, reversed: bool = False) -> None: + super().__init__(boxes, settings) self.reversed = reversed self.char = "oO"[reversed] @@ -1628,13 +1658,13 @@ class ChestHingeTop(ChestHinge): play = self.settings.play if self.settings.finger_joints_on_lid: - final_segment = t-s-play - draw_rest_of_edge = lambda : self.edges["F"](l-p) + final_segment = t - s - play + draw_rest_of_edge = lambda: self.edges["F"](l - p) else: - final_segment = l+t-p-s-play - draw_rest_of_edge = lambda : None + final_segment = l + t - p - s - play + draw_rest_of_edge = lambda: None - poly = (0, -180, t, -180, 0, (-90, p+s+play), 0, 90, final_segment) + poly = (0, -180, t, -180, 0, (-90, p + s + play), 0, 90, final_segment) if self.reversed: draw_rest_of_edge() @@ -1643,24 +1673,23 @@ class ChestHingeTop(ChestHinge): self.polyline(*poly) draw_rest_of_edge() - def startwidth(self): + def startwidth(self) -> float: if self.reversed: - return self.settings.play+self.settings.pin_height+self.settings.hinge_strength - return 0 + return self.settings.play + self.settings.pin_height + self.settings.hinge_strength + return 0.0 - def endwidth(self): + def endwidth(self) -> float: if self.reversed: - return 0 - return self.settings.play+self.settings.pin_height+self.settings.hinge_strength + return 0.0 + return self.settings.play + self.settings.pin_height + self.settings.hinge_strength - def margin(self): + def margin(self) -> float: if self.reversed: - return 0. - else: - return 1*(self.settings.play+self.settings.pin_height+self.settings.hinge_strength) + return 0.0 + return 1 * (self.settings.play + self.settings.pin_height + self.settings.hinge_strength) + class ChestHingePin(BaseEdge): - description = "Edge with pins for an chest hinge" char = "q" @@ -1673,28 +1702,28 @@ class ChestHingePin(BaseEdge): if self.settings.finger_joints_on_lid: middle_segment = [0] - draw_rest_of_edge = lambda : self.edges["F"](l+2*t) + draw_rest_of_edge = lambda: (self.edge(t), self.edges["F"](l), self.edge(t)) else: - middle_segment = [l+2*t,] - draw_rest_of_edge = lambda : None + middle_segment = [l + 2 * t, ] + draw_rest_of_edge = lambda: None - poly = [0, -90, s+p-pinh, -90, t, 90, pinh, 90,] + poly = [0, -90, s + p - pinh, -90, t, 90, pinh, 90, ] self.polyline(*poly) draw_rest_of_edge() self.polyline(*(middle_segment + list(reversed(poly)))) - def margin(self): - return (self.settings.pin_height+self.settings.hinge_strength) + def margin(self) -> float: + return (self.settings.pin_height + self.settings.hinge_strength) class ChestHingeFront(Edge): - description = "Edge opposing a chest hinge" char = "Q" - def startwidth(self): - return self.settings.pin_height+self.settings.hinge_strength + def startwidth(self) -> float: + return self.settings.pin_height + self.settings.hinge_strength + ############################################################################# #### Cabinet Hinge @@ -1719,43 +1748,43 @@ Values: """ absolute_params = { "bore": 3.2, - "eyes_per_hinge" : 5, - "hinges" : 2, - "style" : ("inside", "outside"), + "eyes_per_hinge": 5, + "hinges": 2, + "style": ("inside", "outside"), } relative_params = { "eye": 1.5, - "play" : 0.05, + "play": 0.05, "spacing": 2.0, } - def edgeObjects(self, boxes, chars="uUvV", add=True): + def edgeObjects(self, boxes, chars: str = "uUvV", add: bool = True): edges = [CabinetHingeEdge(boxes, self), CabinetHingeEdge(boxes, self, top=True), CabinetHingeEdge(boxes, self, angled=True), CabinetHingeEdge(boxes, self, top=True, angled=True), - ] + ] for e, c in zip(edges, chars): e.char = c return self._edgeObjects(edges, boxes, chars, add) + class CabinetHingeEdge(BaseEdge): """Edge with cabinet hinges""" char = "u" description = "Edge with cabinet hinges" - def __init__(self, boxes, settings=None, top=False, angled=False): - super(CabinetHingeEdge, self).__init__(boxes, settings) + def __init__(self, boxes, settings=None, top: bool = False, angled: bool = False) -> None: + super().__init__(boxes, settings) self.top = top self.angled = angled - self.char = "uUvV"[bool(top)+2*bool(angled)] + self.char = "uUvV"[bool(top) + 2 * bool(angled)] - def startwidth(self): + def startwidth(self) -> float: return self.settings.thickness if self.top and self.angled else 0.0 - def __poly(self): n = self.settings.eyes_per_hinge p = self.settings.play @@ -1771,29 +1800,29 @@ class CabinetHingeEdge(BaseEdge): if self.top: # start with space - poly = [spacing, 90, e+p] + poly = [spacing, 90, e + p] else: # start with hinge eye - poly = [spacing+p, 90, e+p, 0] + poly = [spacing + p, 90, e + p, 0] for i in range(n): if (i % 2) ^ self.top: # space if i == 0: - poly += [-90, t + 2*p, 90] + poly += [-90, t + 2 * p, 90] else: - poly += [90, t + 2*p, 90] + poly += [90, t + 2 * p, 90] else: # hinge eye - poly += [t-p, -90, t, -90, t-p] + poly += [t - p, -90, t, -90, t - p] if (n % 2) ^ self.top: # stopped with hinge eye - poly += [0, e+p, 90, p+spacing] + poly += [0, e + p, 90, p + spacing] else: # stopped with space - poly[-1:] = [-90, e+p, 90, 0+spacing ] + poly[-1:] = [-90, e + p, 90, 0 + spacing] - width = (t+p) * n + p + 2 * spacing + width = (t + p) * n + p + 2 * spacing return poly, width @@ -1815,20 +1844,20 @@ class CabinetHingeEdge(BaseEdge): hn = min(hn, int(l // width)) if hn == 1: - self.edge((l-width) / 2, tabs=2) + self.edge((l - width) / 2, tabs=2) for j in range(hn): for i in range(n): if not (i % 2) ^ self.top: - self.rectangularHole(self.settings.spacing+0.5*t+p+i*(t+p), e+2.5*t, t, t) + self.rectangularHole(self.settings.spacing + 0.5 * t + p + i * (t + p), e + 2.5 * t, t, t) self.polyline(*poly) if j < (hn - 1): - self.edge((l-hn*width) / (hn-1), tabs=2) + self.edge((l - hn * width) / (hn - 1), tabs=2) if hn == 1: - self.edge((l-width) / 2, tabs=2) + self.edge((l - width) / 2, tabs=2) - def parts(self, move=None): + def parts(self, move=None) -> None: e, b = self.settings.eye, self.settings.bore t = self.settings.thickness @@ -1836,18 +1865,18 @@ class CabinetHingeEdge(BaseEdge): pairs = n // 2 + 2 * (n % 2) if self.settings.style == "outside": - th = 2*e + 4*t - tw = n * (max(3*t, 2*e) + self.boxes.spacing) + th = 2 * e + 4 * t + tw = n * (max(3 * t, 2 * e) + self.boxes.spacing) else: - th = 4*e+3*t+self.boxes.spacing - tw = max(e, 2*t) * pairs + th = 4 * e + 3 * t + self.boxes.spacing + tw = max(e, 2 * t) * pairs if self.move(tw, th, move, True, label="hinges"): return if self.settings.style == "outside": - ax = max(t/2, e-t) - self.moveTo(t+ax) + ax = max(t / 2, e - t) + self.moveTo(t + ax) for i in range(n): if self.angled: if i > n // 2: @@ -1856,43 +1885,43 @@ class CabinetHingeEdge(BaseEdge): l = 5 * t + ax else: l = 3 * t + e - self.hole(0, e, b/2.0) - da = math.asin((t-ax) / e) + self.hole(0, e, b / 2.0) + da = math.asin((t - ax) / e) dad = math.degrees(da) - dy = e * (1-math.cos(da)) - self.polyline(0, (180-dad, e), 0, (-90+dad), dy+l-e, (90, t)) + dy = e * (1 - math.cos(da)) + self.polyline(0, (180 - dad, e), 0, (-90 + dad), dy + l - e, (90, t)) self.polyline(0, 90, t, -90, t, 90, t, 90, t, -90, t, -90, t, - 90, t, 90, (ax+t)-e, -90, l-3*t, (90, e)) - self.moveTo(2*max(e, 1.5*t) + self.boxes.spacing) + 90, t, 90, (ax + t) - e, -90, l - 3 * t, (90, e)) + self.moveTo(2 * max(e, 1.5 * t) + self.boxes.spacing) self.move(tw, th, move, label="hinges") return - if e <= 2*t: + if e <= 2 * t: if self.angled: - corner = [2*e-t, (90, 2*t - e), 0, -90, t, (90, e)] + corner = [2 * e - t, (90, 2 * t - e), 0, -90, t, (90, e)] else: - corner = [2*e, (90, 2*t)] + corner = [2 * e, (90, 2 * t)] else: - a = math.asin(2*t/e) + a = math.asin(2 * t / e) ang = math.degrees(a) - corner = [e*(1-math.cos(a))+2*t, -90+ang, 0, (180-ang, e)] - self.moveTo(max(e, 2*t)) + corner = [e * (1 - math.cos(a)) + 2 * t, -90 + ang, 0, (180 - ang, e)] + self.moveTo(max(e, 2 * t)) for i in range(n): - self.hole(0, e, b/2.0) + self.hole(0, e, b / 2.0) self.polyline(*[0, (180, e), 0, -90, t, 90, t, -90, t, -90, t, 90, t, 90, t, (90, t)] + corner) - self.moveTo(self.boxes.spacing, 4*e+3*t+self.boxes.spacing, 180) + self.moveTo(self.boxes.spacing, 4 * e + 3 * t + self.boxes.spacing, 180) if i % 2: - self.moveTo(2*max(e, 2*t) + 2*self.boxes.spacing) + self.moveTo(2 * max(e, 2 * t) + 2 * self.boxes.spacing) self.move(th, tw, move, label="hinges") + ############################################################################# #### Slide-on lid ############################################################################# -class LidSettings(FingerJointSettings): - +class SlideOnLidSettings(FingerJointSettings): """Settings for Slide-on Lids Note that edge_width below also determines how much the sides extend above the lid. @@ -1904,42 +1933,41 @@ Values: * second_pin : True : additional pin for better positioning * spring : "both" : position(s) of the extra locking springs in the lid * hole_width : 0 : width of the "finger hole" in mm - - """ - __doc__ += FingerJointSettings.__doc__ + __doc__ += FingerJointSettings.__doc__ or "" absolute_params = FingerJointSettings.absolute_params.copy() relative_params = FingerJointSettings.relative_params.copy() - relative_params.update( { + relative_params.update({ "play": 0.05, "finger": 3.0, "space": 2.0, - } ) + }) - absolute_params.update( { + absolute_params.update({ "second_pin": True, "spring": ("both", "none", "left", "right"), "hole_width": 0 - } ) + }) - def edgeObjects(self, boxes, chars=None, add=True): + def edgeObjects(self, boxes, chars=None, add: bool = True): edges = [LidEdge(boxes, self), LidHoleEdge(boxes, self), LidRight(boxes, self), LidLeft(boxes, self), LidSideRight(boxes, self), LidSideLeft(boxes, self), - ] + ] return self._edgeObjects(edges, boxes, chars, add) + class LidEdge(FingerJointEdge): char = "l" description = "Edge for slide on lid (back)" def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw): - hole_width = self.settings.hole_width; + hole_width = self.settings.hole_width if hole_width > 0: super().__call__((length - hole_width) / 2) GroovedEdgeBase.groove_arc(self, hole_width) @@ -1947,12 +1975,13 @@ class LidEdge(FingerJointEdge): else: super().__call__(length) + class LidHoleEdge(FingerHoleEdge): char = "L" description = "Edge for slide on lid (box back)" - def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw): - hole_width = self.settings.hole_width; + def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw) -> None: + hole_width = self.settings.hole_width if hole_width > 0: super().__call__((length - hole_width) / 2) self.edge(hole_width) @@ -1960,6 +1989,7 @@ class LidHoleEdge(FingerHoleEdge): else: super().__call__(length) + class LidRight(BaseEdge): char = "n" description = "Edge for slide on lid (right)" @@ -1974,59 +2004,58 @@ class LidRight(BaseEdge): spring = self.settings.spring in ("left", "both") if spring: - l = min(6*t, length - 2*t) + l = min(6 * t, length - 2 * t) a = 30 sqt = 0.4 * t / math.cos(math.radians(a)) sw = 0.5 * t - p = [0, 90, 1.5*t+sw, -90, l, (-180, 0.25*t), l-0.2*t, 90, sw, 90-a, sqt, 2*a, sqt, -a, length-t ] + p = [0, 90, 1.5 * t + sw, -90, l, (-180, 0.25 * t), l - 0.2 * t, 90, sw, 90 - a, sqt, 2 * a, sqt, -a, length - t] else: - p = [t, 90, t, -90, length-t] + p = [t, 90, t, -90, length - t] pin = self.settings.second_pin if pin: - pinl = 2*t - p[-1:] = [length-2*t-pinl, -90, t, 90, pinl, 90, t, -90, t] + pinl = 2 * t + p[-1:] = [length - 2 * t - pinl, -90, t, 90, pinl, 90, t, -90, t] if not self.rightside: p = list(reversed(p)) self.polyline(*p) - def startwidth(self): - if self.rightside: # or self.settings.second_pin: + def startwidth(self) -> float: + if self.rightside: # or self.settings.second_pin: return self.boxes.thickness - else: - return 0.0 + return 0.0 - def endwidth(self): - if not self.rightside: # or self.settings.second_pin: + def endwidth(self) -> float: + if not self.rightside: # or self.settings.second_pin: return self.boxes.thickness - else: - return 0.0 + return 0.0 - def margin(self): - if not self.rightside: # and not self.settings.second_pin: + def margin(self) -> float: + if not self.rightside: # and not self.settings.second_pin: return self.boxes.thickness - else: - return 0.0 + return 0.0 + class LidLeft(LidRight): char = "m" description = "Edge for slide on lid (left)" rightside = False + class LidSideRight(BaseEdge): char = "N" description = "Edge for slide on lid (box right)" rightside = True - def __call__(self, length, **kw): + def __call__(self, length, **kw): t = self.boxes.thickness s = self.settings.play pin = self.settings.second_pin edge_width = self.settings.edge_width - r = edge_width/3 + r = edge_width / 3 if self.rightside: spring = self.settings.spring in ("right", "both") @@ -2034,40 +2063,42 @@ class LidSideRight(BaseEdge): spring = self.settings.spring in ("left", "both") if spring: - p = [s, -90, t+s, -90, t+s, 90, edge_width-s/2, 90, length+t] + p = [s, -90, t + s, -90, t + s, 90, edge_width - s / 2, 90, length + t] else: - p = [t+s, -90, t+s, -90, 2*t+s, 90, edge_width-s/2, 90, length+t] + p = [t + s, -90, t + s, -90, 2 * t + s, 90, edge_width - s / 2, 90, length + t] if pin: - pinl = 2*t - p[-1:] = [p[-1]-1.5*t-2*pinl-r, (90, r), edge_width+t+s/2-r, -90, 2*pinl+s+0.5*t, -90, t+s, -90, - pinl-r, (90, r), edge_width-s/2-2*r, (90, r), pinl+t-s-r] + pinl = 2 * t + p[-1:] = [p[-1] - 1.5 * t - 2 * pinl - r, (90, r), edge_width + t + s / 2 - r, -90, 2 * pinl + s + 0.5 * t, -90, t + s, -90, + pinl - r, (90, r), edge_width - s / 2 - 2 * r, (90, r), pinl + t - s - r] holex = 0.6 * t - holey = -0.5*t + self.burn - s / 2 + holey = -0.5 * t + self.burn - s / 2 if self.rightside: p = list(reversed(p)) holex = length - holex - holey = edge_width + 0.5*t + self.burn + holey = edge_width + 0.5 * t + self.burn if spring: - self.rectangularHole(holex, holey, 0.4*t, t+2*s) + self.rectangularHole(holex, holey, 0.4 * t, t + 2 * s) self.polyline(*p) - def startwidth(self): + def startwidth(self) -> float: return self.boxes.thickness + self.settings.edge_width if self.rightside else -self.settings.play / 2 - def endwidth(self): + def endwidth(self) -> float: return self.boxes.thickness + self.settings.edge_width if not self.rightside else -self.settings.play / 2 - def margin(self): + def margin(self) -> float: return self.boxes.thickness + self.settings.edge_width + self.settings.play / 2 if not self.rightside else 0.0 + class LidSideLeft(LidSideRight): char = "M" description = "Edge for slide on lid (box left)" rightside = False + ############################################################################# #### Click Joints ############################################################################# @@ -2095,16 +2126,17 @@ Values: "bottom_radius": 0.1, } - def edgeObjects(self, boxes, chars="cC", add=True): + def edgeObjects(self, boxes, chars: str = "cC", add: bool = True): edges = [ClickConnector(boxes, self), ClickEdge(boxes, self)] return self._edgeObjects(edges, boxes, chars, add) + class ClickConnector(BaseEdge): char = "c" description = "Click on (bottom side)" - def hook(self, reverse=False): + def hook(self, reverse: bool = False) -> None: t = self.settings.thickness a = self.settings.angle d = self.settings.depth @@ -2155,7 +2187,7 @@ class ClickConnector(BaseEdge): return s * d * c + 2 * r - def finger(self, length): + def finger(self, length) -> None: t = self.settings.thickness self.polyline( 2 * t, @@ -2179,7 +2211,7 @@ class ClickConnector(BaseEdge): self.hook(reverse=True) self.edge(4 * t) - def margin(self): + def margin(self) -> float: return 2 * self.settings.thickness @@ -2187,10 +2219,10 @@ class ClickEdge(ClickConnector): char = "C" description = "Click on (top)" - def startwidth(self): + def startwidth(self) -> float: return self.boxes.thickness - def margin(self): + def margin(self) -> float: return 0.0 def __call__(self, length, **kw): @@ -2242,11 +2274,12 @@ Values: "radius": 0.2, } - def edgeObjects(self, boxes, chars="dD", add=True): + def edgeObjects(self, boxes, chars: str = "dD", add: bool = True): edges = [DoveTailJoint(boxes, self), DoveTailJointCounterPart(boxes, self)] return self._edgeObjects(edges, boxes, chars, add) + class DoveTailJoint(BaseEdge): """Edge with dove tail joints """ @@ -2290,7 +2323,7 @@ class DoveTailJoint(BaseEdge): self.edge((s.size + leftover) / 2.0 + diffx - l1, tabs=1) - def margin(self): + def margin(self) -> float: """ """ return self.settings.depth @@ -2302,7 +2335,7 @@ class DoveTailJointCounterPart(DoveTailJoint): positive = False - def margin(self): + def margin(self) -> float: return 0.0 @@ -2313,7 +2346,7 @@ Values: * absolute - * stretch : 1.05 : Hint of how much the flex part should be shortend + * stretch : 1.05 : Hint of how much the flex part should be shortened * relative (in multiples of thickness) @@ -2332,12 +2365,13 @@ Values: "stretch": 1.05, } - def checkValues(self): + def checkValues(self) -> None: if self.distance < 0.01: raise ValueError("Flex Settings: distance parameter must be > 0.01mm") if self.width < 0.1: raise ValueError("Flex Settings: width parameter must be > 0.1mm") + class FlexEdge(BaseEdge): """Edge with flex cuts - use straight edge for the opposing side""" char = 'X' @@ -2392,8 +2426,8 @@ class FlexEdge(BaseEdge): self.ctx.line_to(x, 0) self.ctx.translate(*self.ctx.get_current_point()) -class GearSettings(Settings): +class GearSettings(Settings): """Settings for rack (and pinion) edge Values: * absolute_params @@ -2405,30 +2439,30 @@ Values: """ absolute_params = { - "dimension" : 3.0, - "angle" : 20.0, - "profile_shift" : 20.0, - "clearance" : 0.0, - } + "dimension": 3.0, + "angle": 20.0, + "profile_shift": 20.0, + "clearance": 0.0, + } + + relative_params: dict[str, Any] = {} - relative_params = {} class RackEdge(BaseEdge): - char = "R" description = "Rack (and pinion) Edge" - def __init__(self, boxes, settings): - super(RackEdge, self).__init__(boxes, settings) + def __init__(self, boxes, settings) -> None: + super().__init__(boxes, settings) self.gear = gears.Gears(boxes) def __call__(self, length, **kw): params = self.settings.values.copy() params["draw_rack"] = True params["rack_base_height"] = -1E-36 - params["rack_teeth_length"] = int(length // params["dimension"]) - params["rack_base_tab"] = (length - (params["rack_teeth_length"]) * params["dimension"]) / 2.0 + params["rack_teeth_length"] = int(length // (params["dimension"] * math.pi)) + params["rack_base_tab"] = (length - (params["rack_teeth_length"]) * params["dimension"] * math.pi) / 2.0 s_tmp = self.boxes.spacing self.boxes.spacing = 0 self.moveTo(length, 0, 180) @@ -2436,11 +2470,11 @@ class RackEdge(BaseEdge): self.moveTo(0, 0, 180) self.boxes.spacing = s_tmp - def margin(self): + def margin(self) -> float: return self.settings.dimension * 1.1 -class RoundedTriangleEdgeSettings(Settings): +class RoundedTriangleEdgeSettings(Settings): """Settings for RoundedTriangleEdge Values: @@ -2457,71 +2491,72 @@ Values: """ absolute_params = { - "height" : 50., - "radius" : 30., - "r_hole" : 2., + "height": 50., + "radius": 30., + "r_hole": 2., } relative_params = { - "outset" : 0., + "outset": 0., } - def edgeObjects(self, boxes, chars="t", add=True): + def edgeObjects(self, boxes, chars: str = "t", add: bool = True): edges = [RoundedTriangleEdge(boxes, self), RoundedTriangleFingerHolesEdge(boxes, self)] return self._edgeObjects(edges, boxes, chars, add) + class RoundedTriangleEdge(Edge): """Makes an 'edge' with a rounded triangular bumpout and optional hole""" description = "Triangle for handle" char = "t" + def __call__(self, length, **kw): length += 2 * self.settings.outset r = self.settings.radius - if r > length / 2: + if r > length / 2: r = length / 2 - if length-2*r < self.settings.height: # avoid division by zero - angle = 90-math.degrees(math.atan( - (length-2*r)/(2*self.settings.height))) - l = self.settings.height / math.cos(math.radians(90-angle)) + if length - 2 * r < self.settings.height: # avoid division by zero + angle = 90 - math.degrees(math.atan( + (length - 2 * r) / (2 * self.settings.height))) + l = self.settings.height / math.cos(math.radians(90 - angle)) else: angle = math.degrees(math.atan( - 2*self.settings.height/(length-2*r))) - l = 0.5 * (length-2*r) / math.cos(math.radians(angle)) + 2 * self.settings.height / (length - 2 * r))) + l = 0.5 * (length - 2 * r) / math.cos(math.radians(angle)) if self.settings.outset: self.polyline(0, -180, self.settings.outset, 90) else: self.corner(-90) if self.settings.r_hole: - self.hole(self.settings.height, length/2., self.settings.r_hole) - self.corner(90-angle, r, tabs=1) + self.hole(self.settings.height, length / 2., self.settings.r_hole) + self.corner(90 - angle, r, tabs=1) self.edge(l, tabs=1) - self.corner(2*angle, r, tabs=1) + self.corner(2 * angle, r, tabs=1) self.edge(l, tabs=1) - self.corner(90-angle, r, tabs=1) + self.corner(90 - angle, r, tabs=1) if self.settings.outset: self.polyline(0, 90, self.settings.outset, -180) else: self.corner(-90) - def margin(self): + def margin(self) -> float: return self.settings.height + self.settings.radius -class RoundedTriangleFingerHolesEdge(RoundedTriangleEdge): +class RoundedTriangleFingerHolesEdge(RoundedTriangleEdge): char = "T" - def startwidth(self): + def startwidth(self) -> float: return self.settings.thickness - def __call__(self, length, **kw): - self.fingerHolesAt(0, 0.5*self.settings.thickness, length, 0) + def __call__(self, length, **kw): + self.fingerHolesAt(0, 0.5 * self.settings.thickness, length, 0) super().__call__(length, **kw) class HandleEdgeSettings(Settings): - """Settings for HandleEdge Values: @@ -2540,22 +2575,23 @@ Values: """ absolute_params = { - "height" : 20., - "radius" : 10., - "hole_width" : "40:40", - "hole_height" : 75., + "height": 20., + "radius": 10., + "hole_width": "40:40", + "hole_height": 75., "on_sides": True, } relative_params = { - "outset" : 1., + "outset": 1., } - def edgeObjects(self, boxes, chars="yY", add=True): + def edgeObjects(self, boxes, chars: str = "yY", add: bool = True): edges = [HandleEdge(boxes, self), HandleHoleEdge(boxes, self)] return self._edgeObjects(edges, boxes, chars, add) + # inspiration came from https://www.thingiverse.com/thing:327393 class HandleEdge(Edge): @@ -2569,9 +2605,9 @@ class HandleEdge(Edge): extra_height = self.extra_height * self.settings.thickness r = self.settings.radius - if r > length / 2: + if r > length / 2: r = length / 2 - if r > self.settings.height: + if r > self.settings.height: r = self.settings.height widths = argparseSections(self.settings.hole_width) @@ -2598,7 +2634,7 @@ class HandleEdge(Edge): slot_x += slotwidth / 2 with self.saved_context(): self.moveTo((self.settings.height / 2) + extra_height, slot_x, 0) - self.rectangularHole(0,0,slot_height,slotwidth,slot_height/2,True,True) + self.rectangularHole(0, 0, slot_height, slotwidth, slot_height / 2, True, True) slot_x += slotwidth / 2 + slot_offset + self.thickness + slot_offset self.edge(self.settings.height - r + extra_height, tabs=1) @@ -2612,18 +2648,19 @@ class HandleEdge(Edge): else: self.corner(-90) - def margin(self): + def margin(self) -> float: return self.settings.height + class HandleHoleEdge(HandleEdge): """Extends an 'edge' by adding a rounded bumpout with optional holes and holes for parallel finger joint""" description = "Handle with holes for parallel finger joint" char = "Y" extra_height = 1.0 - def __call__(self, length, **kw): - self.fingerHolesAt(0, -0.5*self.settings.thickness, length, 0) + def __call__(self, length, **kw): + self.fingerHolesAt(0, -0.5 * self.settings.thickness, length, 0) super().__call__(length, **kw) - def margin(self): - return self.settings.height + self.extra_height*self.settings.thickness + def margin(self) -> float: + return self.settings.height + self.extra_height * self.settings.thickness diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/extents.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/extents.py index 2cfb810..b2ab4a6 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/extents.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/extents.py @@ -1,44 +1,43 @@ class Extents: __slots__ = "xmin ymin xmax ymax".split() - def __init__(self,xmin=float('inf'),ymin=float('inf'),xmax=float('-inf'),ymax=float('-inf')): + def __init__(self, xmin: float = float('inf'), ymin: float = float('inf'), xmax: float = float('-inf'), ymax: float = float('-inf')) -> None: self.xmin = xmin self.ymin = ymin self.xmax = xmax self.ymax = ymax - def add(self,x,y): - self.xmin = min(self.xmin,x) - self.xmax = max(self.xmax,x) - self.ymin = min(self.ymin,y) - self.ymax = max(self.ymax,y) + def add(self, x: float, y: float) -> None: + self.xmin = min(self.xmin, x) + self.xmax = max(self.xmax, x) + self.ymin = min(self.ymin, y) + self.ymax = max(self.ymax, y) - def extend(self,l): - for x,y in l: - self.add(x,y) + def extend(self, l) -> None: + for x, y in l: + self.add(x, y) - def __add__(self,extent): - #todo: why can this happen? + def __add__(self, extent): + # todo: why can this happen? if extent == 0: - return Extents(self.xmin,self.ymin,self.xmax,self.ymax) + return Extents(self.xmin, self.ymin, self.xmax, self.ymax) return Extents( - min(self.xmin,extent.xmin),min(self.ymin,extent.ymin), - max(self.xmax,extent.xmax),max(self.ymax,extent.ymax) + min(self.xmin, extent.xmin), min(self.ymin, extent.ymin), + max(self.xmax, extent.xmax), max(self.ymax, extent.ymax) ) - def __radd__(self,extent): + def __radd__(self, extent): if extent == 0: - return Extents(self.xmin,self.ymin,self.xmax,self.ymax) + return Extents(self.xmin, self.ymin, self.xmax, self.ymax) return self.__add__(extent) - def get_width(self): - return self.xmax-self.xmin + @property + def width(self) -> float: + return self.xmax - self.xmin - def get_height(self): - return self.ymax-self.ymin + @property + def height(self) -> float: + return self.ymax - self.ymin - width = property(get_width) - height = property(get_height) - - def __repr__(self): + def __repr__(self) -> str: return f'Extents ({self.xmin},{self.ymin})-({self.xmax},{self.ymax})' diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/formats.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/formats.py index bf4c3b7..b49f196 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/formats.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/formats.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,15 +14,18 @@ # along with this program. If not, see . -import subprocess -import tempfile import os import shutil -from boxes.drawing import SVGSurface, PSSurface, LBRN2Surface, Context +import subprocess +import tempfile + +from boxes.drawing import Context, LBRN2Surface, PSSurface, SVGSurface + class Formats: pstoedit_candidates = ["/usr/bin/pstoedit", "pstoedit", "pstoedit.exe"] + ps2pdf_candidates = ["/usr/bin/ps2pdf", "ps2pdf", "ps2pdf.exe"] _BASE_FORMATS = ['svg', 'svg_Ponoko', 'ps', 'lbrn2'] @@ -32,11 +34,11 @@ class Formats: "svg_Ponoko": None, "ps": None, "lbrn2": None, - "dxf": "-flat 0.1 -f dxf:-mm".split(), - "gcode": "-f gcode".split(), - "plt": "-f plot-hpgl".split(), - "ai": "-f ps2ai".split(), - "pdf": "-f pdf".split(), + "dxf": "{pstoedit} -flat 0.1 -f dxf:-mm {input} {output}", + "gcode": "{pstoedit} -f gcode {input} {output}", + "plt": "{pstoedit} -f hpgl {input} {output}", + # "ai": "{pstoedit} -f ps2ai", + "pdf": "{ps2pdf} -dEPSCrop {input} {output}", } http_headers = { @@ -51,41 +53,53 @@ class Formats: # "" : [('Content-type', '')], } - def __init__(self): + def __init__(self) -> None: for cmd in self.pstoedit_candidates: self.pstoedit = shutil.which(cmd) if self.pstoedit: break + for cmd in self.ps2pdf_candidates: + self.ps2pdf = shutil.which(cmd) + if self.ps2pdf: + break def getFormats(self): if self.pstoedit: return sorted(self.formats.keys()) return self._BASE_FORMATS - def getSurface(self, fmt, filename): + def getSurface(self, fmt): if fmt in ("svg", "svg_Ponoko"): - surface = SVGSurface(filename) + surface = SVGSurface() elif fmt == "lbrn2": - surface = LBRN2Surface(filename) + surface = LBRN2Surface() else: - surface = PSSurface(filename) + surface = PSSurface() ctx = Context(surface) return surface, ctx - def convert(self, filename, fmt, metadata=None): + def convert(self, data, fmt): if fmt not in self._BASE_FORMATS: - fd, tmpfile = tempfile.mkstemp(dir=os.path.dirname(filename)) - cmd = [self.pstoedit] + self.formats[fmt] + [filename, tmpfile] - err = subprocess.call(cmd) + fd, tmpfile = tempfile.mkstemp() + os.write(fd, data.getvalue()) + os.close(fd) + fd2, outfile = tempfile.mkstemp() - if err: + cmd = self.formats[fmt].format( + pstoedit=self.pstoedit, + ps2pdf=self.ps2pdf, + input=tmpfile, + output=outfile).split() + + result = subprocess.run(cmd) + os.unlink(tmpfile) + if result.returncode: # XXX show stderr output - try: - os.unlink(tmpfile) - except: - pass - raise ValueError("Conversion failed. pstoedit returned %i" % err) + raise ValueError("Conversion failed. pstoedit returned %i\n\n %s" % (result.returncode, result.stderr)) + data = open(outfile, 'rb') + os.unlink(outfile) + os.close(fd2) - os.rename(tmpfile, filename) + return data diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/gears.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/gears.py index c131eea..55c5f20 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/gears.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/gears.py @@ -1,51 +1,47 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- -"" +# Copyright (C) 2007 Aaron Spike (aaron @ ekips.org) +# Copyright (C) 2007 Tavmjong Bah (tavmjong @ free.fr) +# Copyright (C) https://cnc-club.ru/forum/viewtopic.php?f=33&t=434&p=2594#p2500 +# Copyright (C) 2014 Jürgen Weigert (juewei@fabmail.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# 2014-03-20 jw@suse.de 0.2 Option --accuracy=0 for automatic added. +# 2014-03-21 sent upstream: https://bugs.launchpad.net/inkscape/+bug/1295641 +# 2014-03-21 jw@suse.de 0.3 Fixed center of rotation for gears with odd number of teeth. +# 2014-04-04 juewei 0.7 Revamped calc_unit_factor(). +# 2014-04-05 juewei 0.7a Correctly positioned rack gear. +# The geometry above the meshing line is wrong. +# 2014-04-06 juewei 0.7b Undercut detection added. Reference: +# https://web.archive.org/web/20140801105942/https://nptel.ac.in/courses/IIT-MADRAS/Machine_Design_II/pdf/2_2.pdf +# Manually merged https://github.com/jnweiger/inkscape-gears-dev/pull/15 +# 2014-04-07 juewei 0.7c Manually merged https://github.com/jnweiger/inkscape-gears-dev/pull/17 +# 2014-04-09 juewei 0.8 Fixed https://github.com/jnweiger/inkscape-gears-dev/issues/19 +# Ring gears are ready for production now. Thanks neon22 for driving this. +# Profile shift implemented (Advanced Tab), fixing +# https://github.com/jnweiger/inkscape-gears-dev/issues/9 +# 2015-05-29 juewei 0.9 ported to inkscape 0.91 +# AttributeError: 'module' object inkex has no attribute 'uutounit +# Fixed https://github.com/jnweiger/inkscape-gears-dev -''' -Copyright (C) 2007 Aaron Spike (aaron @ ekips.org) -Copyright (C) 2007 Tavmjong Bah (tavmjong @ free.fr) -Copyright (C) http://cnc-club.ru/forum/viewtopic.php?f=33&t=434&p=2594#p2500 -Copyright (C) 2014 Jürgen Weigert (juewei@fabmail.org) +from math import acos, asin, ceil, cos, degrees, pi, radians, sin, sqrt, tan +from os import devnull # for debugging -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -2014-03-20 jw@suse.de 0.2 Option --accuracy=0 for automatic added. -2014-03-21 sent upstream: https://bugs.launchpad.net/inkscape/+bug/1295641 -2014-03-21 jw@suse.de 0.3 Fixed center of rotation for gears with odd number of teeth. -2014-04-04 juewei 0.7 Revamped calc_unit_factor(). -2014-04-05 juewei 0.7a Correctly positioned rack gear. - The geometry above the meshing line is wrong. -2014-04-06 juewei 0.7b Undercut detection added. Reference: - http://nptel.ac.in/courses/IIT-MADRAS/Machine_Design_II/pdf/2_2.pdf - Manually merged https://github.com/jnweiger/inkscape-gears-dev/pull/15 -2014-04-07 juewei 0.7c Manually merged https://github.com/jnweiger/inkscape-gears-dev/pull/17 -2014-04-09 juewei 0.8 Fixed https://github.com/jnweiger/inkscape-gears-dev/issues/19 - Ring gears are ready for production now. Thanks neon22 for driving this. - Profile shift implemented (Advanced Tab), fixing - https://github.com/jnweiger/inkscape-gears-dev/issues/9 -2015-05-29 juewei 0.9 ported to inkscape 0.91 - AttributeError: 'module' object inkex has no attribute 'uutounit - Fixed https://github.com/jnweiger/inkscape-gears-dev -''' - -from os import devnull # for debugging -from math import pi, cos, sin, tan, radians, degrees, ceil, asin, acos, sqrt two_pi = 2 * pi import argparse -from boxes.vectors import kerf, vdiff, vlength + +from boxes.vectors import vdiff, vlength __version__ = '0.9' @@ -57,12 +53,11 @@ def linspace(a,b,n): return [a+x*(b-a)/(n-1) for x in range(0,n)] def involute_intersect_angle(Rb, R): - " " Rb, R = float(Rb), float(R) return (sqrt(R**2 - Rb**2) / (Rb)) - (acos(Rb / R)) def point_on_circle(radius, angle): - " return xy coord of the point at distance radius from origin at angle " + """ return xy coord of the point at distance radius from origin at angle """ x = radius * cos(angle) y = radius * sin(angle) return (x, y) @@ -105,7 +100,7 @@ def have_undercut(teeth, pitch_angle=20.0, k=1.0): ## gather all basic gear calculations in one place def gear_calculations(num_teeth, circular_pitch, pressure_angle, clearance=0, ring_gear=False, profile_shift=0.): """ Put base calcs for spur/ring gears in one place. - - negative profile shifting helps against undercut. + - negative profile shifting helps against undercut. """ diametral_pitch = pi / circular_pitch pitch_diameter = num_teeth / diametral_pitch @@ -140,7 +135,7 @@ def generate_rack_points(tooth_count, pitch, addendum, pressure_angle, - involute on a circle of infinite radius is a simple linear ramp - - the meshing circle touches at y = 0, + - the meshing circle touches at y = 0, - the highest elevation of the teeth is at y = +addendum - the lowest elevation of the teeth is at y = -addendum-clearance - the base_height extends downwards from the lowest elevation. @@ -177,7 +172,7 @@ def generate_rack_points(tooth_count, pitch, addendum, pressure_angle, points.append((x-tasc, base_top)) points.append((x+tas, -addendum)) points.append((x+spacing-tas, -addendum)) - points.append((x+spacing+tasc, base_top)) + points.append((x+spacing+tasc, base_top)) x += pitch # add base on RHS @@ -221,7 +216,7 @@ def generate_spur_points(teeth, base_radius, pitch_radius, outer_radius, root_ra pitch2 = c + half_thick_angle base2 = pitch2 + pitch_to_base_angle - offsetangles2 = [ base2 - x for x in angles] + offsetangles2 = [ base2 - x for x in angles] points2 = [ point_on_circle( radii[i], offsetangles2[i]) for i in range(0,len(radii)) ] points_on_outer_radius = [ point_on_circle(outer_radius, x) for x in linspace(offsetangles1[-1], offsetangles2[-1], accuracy_circular) ] @@ -263,14 +258,14 @@ class OptionParser(argparse.ArgumentParser): class Gears(): - def __init__(self, boxes, **kw): + def __init__(self, boxes, **kw) -> None: # an alternate way to get debug info: # could use inkex.debug(string) instead... - try: - self.tty = open("/dev/tty", 'w') - except: - self.tty = open(devnull, 'w') # '/dev/null' for POSIX, 'nul' for Windows. - # print >>self.tty, "gears-dev " + __version__ + #try: + # self.tty = open("/dev/tty", 'w') + #except: + # self.tty = open(devnull, 'w') # '/dev/null' for POSIX, 'nul' for Windows. + # # print >>self.tty, "gears-dev " + __version__ self.boxes = boxes self.OptionParser = OptionParser() @@ -280,7 +275,7 @@ class Gears(): help="Number of teeth") self.OptionParser.add_option("-s", "--system", - action="store", type="string", + action="store", type="string", dest="system", default='MM', help="Select system: 'CP' (Cyclic Pitch (default)), 'DP' (Diametral Pitch), 'MM' (Metric Module)") @@ -308,7 +303,7 @@ class Gears(): self.OptionParser.add_option("-A", "--accuracy", action="store", type="int", dest="accuracy", default=0, - help="Accuracy of involute: automatic: 5..20 (default), best: 20(default), medium 10, low: 5; good acuracy is important with a low tooth count") + help="Accuracy of involute: automatic: 5..20 (default), best: 20(default), medium 10, low: 5; good accuracy is important with a low tooth count") # Clearance: Radial distance between top of tooth on one gear to bottom of gap on another. self.OptionParser.add_option("", "--clearance", action="store", type="float", @@ -316,7 +311,7 @@ class Gears(): help="Clearance between bottom of gap of this gear and top of tooth of another") self.OptionParser.add_option("", "--annotation", - action="store", type="inkbool", + action="store", type="inkbool", dest="annotation", default=False, help="Draw annotation text") @@ -356,7 +351,7 @@ class Gears(): help="Active tab. Not used now.") self.OptionParser.add_option("-x", "--centercross", - action="store", type="inkbool", + action="store", type="inkbool", dest="centercross", default=False, help="Draw cross in center") @@ -366,7 +361,7 @@ class Gears(): help="Draw pitch circle (for mating)") self.OptionParser.add_option("-r", "--draw-rack", - action="store", type="inkbool", + action="store", type="inkbool", dest="drawrack", default=False, help="Draw rack gear instead of spur gear") @@ -386,35 +381,16 @@ class Gears(): help="Length of tabs on ends of rack") self.OptionParser.add_option("", "--undercut-alert", - action="store", type="inkbool", + action="store", type="inkbool", dest="undercut_alert", default=False, help="Let the user confirm a warning dialog if undercut occurs. This dialog also shows helpful hints against undercut") - def drawPoints(self, lines, kerfdir=1, close=True): - - if not lines: - return - - if kerfdir != 0: - lines = kerf(lines, self.boxes.burn*kerfdir, closed=close) - - self.boxes.ctx.save() - self.boxes.ctx.move_to(*lines[0]) - - for x, y in lines[1:]: - self.boxes.ctx.line_to(x, y) - - if close: - self.boxes.ctx.line_to(*lines[0]) - self.boxes.ctx.restore() - def calc_circular_pitch(self): - """ We use math based on circular pitch. - """ + """We use math based on circular pitch.""" dimension = self.options.dimension if self.options.system == 'CP': # circular pitch circular_pitch = dimension * 25.4 - elif self.options.system == 'DP': # diametral pitch + elif self.options.system == 'DP': # diametral pitch circular_pitch = pi * 25.4 / dimension elif self.options.system == 'MM': # module (metric) circular_pitch = pi * dimension @@ -461,7 +437,7 @@ class Gears(): collision = True else: mount_radius = mount_hole/2 + adj_factor # small fix - messages.append("Mount support too small. Auto increased to %2.2f%s." % (mount_radius/unit_factor*2, unit_label)) + messages.append(f"Mount support too small. Auto increased to {mount_radius/unit_factor*2:2.2f}{unit_label}.") # then check to see if cross-over on spoke width for i in range(spoke_count): @@ -470,7 +446,7 @@ class Gears(): if spoke_width >= angle * mount_radius: adj_factor = 1.2 # wrong value. its probably one of the points distances calculated below mount_radius += adj_factor - messages.append("Too many spokes. Increased Mount support by %2.3f%s" % (adj_factor/unit_factor, unit_label)) + messages.append(f"Too many spokes. Increased Mount support by {adj_factor/unit_factor:2.3f}{unit_label}") # check for collision with outer rim if r_outer <= mount_radius: @@ -512,7 +488,7 @@ class Gears(): return messages def sizes(self, **kw): - self.options = self.OptionParser.parse_args(["--%s=%s" % (name,value) for name, value in kw.items()]) + self.options = self.OptionParser.parse_args([f"--{name}={value}" for name, value in kw.items()]) # Pitch (circular pitch): Length of the arc from one tooth to the next) # Pitch diameter: Diameter of pitch circle. pitch = self.calc_circular_pitch() @@ -564,26 +540,26 @@ class Gears(): self.boxes.moveTo(r+0.5*spoke_width+self.boxes.burn, 0, 90) self.boxes.corner(360, r+0.5*spoke_width) - + self.boxes.ctx.restore() self.boxes.move(width, width, move) def __call__(self, teeth_only=False, move="", callback=None, **kw): """ Calculate Gear factors from inputs. - - Make list of radii, angles, and centers for each tooth and + - Make list of radii, angles, and centers for each tooth and iterate through them - Turn on other visual features e.g. cross, rack, annotations, etc """ - self.options = self.OptionParser.parse_args(["--%s=%s" % (name,value) for name, value in kw.items()]) + self.options = self.OptionParser.parse_args([f"--{name}={value}" for name, value in kw.items()]) warnings = [] # list of extra messages to be shown in annotations - # calculate unit factor for units defined in dialog. + # calculate unit factor for units defined in dialog. unit_factor = 1 # User defined options teeth = self.options.teeth # Angle of tangent to tooth at circular pitch wrt radial line. - angle = self.options.angle - # Clearance: Radial distance between top of tooth on one gear to + angle = self.options.angle + # Clearance: Radial distance between top of tooth on one gear to # bottom of gap on another. clearance = self.options.clearance * unit_factor mount_hole = self.options.mount_hole * unit_factor @@ -600,7 +576,7 @@ class Gears(): accuracy_involute = 20 # Number of points of the involute curve accuracy_circular = 9 # Number of points on circular parts if self.options.accuracy is not None: - if self.options.accuracy == 0: + if self.options.accuracy == 0: # automatic if teeth < 10: accuracy_involute = 20 elif teeth < 30: accuracy_involute = 12 @@ -634,8 +610,8 @@ class Gears(): self.boxes.moveTo(width/2.0, base_height+addendum, -180) if base_height < 0: points = points[1:-1] - self.drawPoints(points, close=base_height >= 0) - self.drawPoints(guide_points, kerfdir=0) + self.boxes.drawPoints(points, close=base_height >= 0) + self.boxes.drawPoints(guide_points, kerfdir=0) self.boxes.move(width, height, move) return @@ -666,7 +642,7 @@ class Gears(): if not teeth_only: self.boxes.moveTo(width/2, height/2) self.boxes.cc(callback, None, 0, 0) - self.drawPoints(points) + self.boxes.drawPoints(points) # Spokes if not teeth_only and not self.options.internal_ring: # only draw internals if spur gear msg = self.generate_spokes(root_radius, spoke_width, spoke_count, mount_radius, mount_hole, @@ -711,11 +687,11 @@ class Gears(): notes.extend(warnings) #notes.append('Document (%s) scale conversion = %2.4f' % (self.document.getroot().find(inkex.addNS('namedview', 'sodipodi')).get(inkex.addNS('document-units', 'inkscape')), unit_factor)) notes.extend(['Teeth: %d CP: %2.4f(%s) ' % (teeth, pitch / unit_factor, self.options.units), - 'DP: %2.3f Module: %2.4f(mm)' % (25.4 * pi / pitch, pitch), + f'DP: {25.4 * pi / pitch:2.3f} Module: {pitch:2.4f}(mm)', 'Pressure Angle: %2.2f degrees' % (angle), - 'Pitch diameter: %2.3f %s' % (pitch_radius * 2 / unit_factor, self.options.units), - 'Outer diameter: %2.3f %s' % (outer_dia / unit_factor, self.options.units), - 'Base diameter: %2.3f %s' % (base_radius * 2 / unit_factor, self.options.units)#, + f'Pitch diameter: {pitch_radius * 2 / unit_factor:2.3f} {self.options.units}', + f'Outer diameter: {outer_dia / unit_factor:2.3f} {self.options.units}', + f'Base diameter: {base_radius * 2 / unit_factor:2.3f} {self.options.units}'#, #'Addendum: %2.4f %s' % (addendum / unit_factor, self.options.units), #'Dedendum: %2.4f %s' % (dedendum / unit_factor, self.options.units) ]) @@ -731,10 +707,3 @@ class Gears(): if not teeth_only: self.boxes.move(width, height, move) - -if __name__ == '__main__': - e = Gears() - e.affect() - -# Notes - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/__init__.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/__init__.py index add5b03..89de2fd 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/__init__.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/__init__.py @@ -1,34 +1,42 @@ -import pkgutil -import inspect +from __future__ import annotations + import importlib +import inspect +import os +import pkgutil +from types import ModuleType +from typing import Any + import boxes ui_groups_by_name = {} + class UIGroup: - def __init__(self, name, title=None, description="", image=""): + def __init__(self, name: str, title: str | None = None, description: str = "", image: str = "") -> None: self.name = name self.title = title or name self.description = description self._image = image - self.generators = [] + self.generators: list[Any] = [] # register ui_groups_by_name[name] = self - def add(self, box): + def add(self, box) -> None: self.generators.append(box) - self.generators.sort(key=lambda b:getattr(b, '__name__', None) or b.__class__.__name__) + self.generators.sort(key=lambda b: getattr(b, '__name__', None) or b.__class__.__name__) @property - def thumbnail(self): + def thumbnail(self) -> str: return self._image and f"{self._image}-thumb.jpg" @property - def image(self): + def image(self) -> str: return self._image and f"{self._image}.jpg" -ui_groups = [ + +ui_groups: list[UIGroup] = [ UIGroup("Box", "Boxes", image="UniversalBox"), UIGroup("FlexBox", "Boxes with flex", image="RoundedBox"), UIGroup("Tray", "Trays and Drawer Inserts", image="TypeTray"), @@ -38,31 +46,35 @@ ui_groups = [ UIGroup("Part", "Parts and Samples", image="BurnTest"), UIGroup("Misc", image="TrafficLight"), UIGroup("Unstable", description="Generators are still untested or need manual adjustment to be useful."), - ] +] -def getAllBoxGenerators(): + +def getAllBoxGenerators() -> dict[str, type[boxes.Boxes]]: generators = {} - for importer, modname, ispkg in pkgutil.walk_packages( - path=__path__, - prefix=__name__+'.'): + path = __path__ + if "BOXES_GENERATOR_PATH" in os.environ: + path.extend(os.environ.get("BOXES_GENERATOR_PATH", "").split(":")) + for importer, modname, ispkg in pkgutil.walk_packages(path=path, prefix=__name__ + '.'): module = importlib.import_module(modname) if module.__name__.split('.')[-1].startswith("_"): continue for k, v in module.__dict__.items(): if v is boxes.Boxes: continue - if (inspect.isclass(v) and issubclass(v, boxes.Boxes) and - v.__name__[0] != '_'): + if inspect.isclass(v) and issubclass(v, boxes.Boxes) and v.__name__[0] != '_': generators[modname + '.' + v.__name__] = v return generators -def getAllGeneratorModules(): + +def getAllGeneratorModules() -> dict[str, ModuleType]: generators = {} + path = __path__ + if "BOXES_GENERATOR_PATH" in os.environ: + path.extend(os.environ.get("BOXES_GENERATOR_PATH", "").split(":")) for importer, modname, ispkg in pkgutil.walk_packages( - path=__path__, - prefix=__name__+'.', + path=path, + prefix=__name__ + '.', onerror=lambda x: None): module = importlib.import_module(modname) generators[modname.split('.')[-1]] = module return generators - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/_swtemplate.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/_swtemplate.py index 941b5ac..a185bfe 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/_swtemplate.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/_swtemplate.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,14 +13,17 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +# mypy: ignore-errors + from boxes import * + class SlatwallXXX(Boxes): # Change class name! """DESCRIPTION""" ui_group = "SlatWall" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/_template.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/_template.py index 9f19d12..a99cb19 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/_template.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/_template.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,22 +15,27 @@ from boxes import * + class BOX(Boxes): # Change class name! """DESCRIPTION""" ui_group = "Unstable" # see ./__init__.py for names - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) # Uncomment the settings for the edge types you use # use keyword args to set default values # self.addSettingsArgs(edges.FingerJointSettings, finger=1.0,space=1.0) + # self.addSettingsArgs(edges.DoveTailSettings) # self.addSettingsArgs(edges.StackableSettings) # self.addSettingsArgs(edges.HingeSettings) - # self.addSettingsArgs(edges.LidSettings) + # self.addSettingsArgs(edges.SlideOnLidSettings) # self.addSettingsArgs(edges.ClickSettings) # self.addSettingsArgs(edges.FlexSettings) + # self.addSettingsArgs(edges.HandleEdgeSettings) + # self.addSettingsArgs(edges.RoundedTriangleEdgeSettings) + # self.addSettingsArgs(edges.MountingSettings) # remove cli params you do not need self.buildArgParser(x=100, sx="3*50", y=100, sy="3*50", h=100, hi=0) @@ -51,8 +55,7 @@ class BOX(Boxes): # Change class name! space = 10, finger=10, width=self.thickness) p = edges.FingerJointEdge(self, s) - p.char = "a" # 'a', 'A', 'b' and 'B' is reserved for beeing used within generators + p.char = "a" # 'a', 'A', 'b' and 'B' is reserved for being used within generators self.addPart(p) # render your parts here - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/abox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/abox.py index 93e1b0e..4e32ea5 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/abox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/abox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,22 +14,21 @@ # along with this program. If not, see . from boxes import * +from boxes.lids import LidSettings + class ABox(Boxes): """A simple Box""" description = "This box is kept simple on purpose. If you need more features have a look at the UniversalBox." - + ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) + self.addSettingsArgs(LidSettings) self.buildArgParser("x", "y", "h", "outside", "bottom_edge") - #self.argparser.add_argument( - # "--lid", action="store", type=str, default="default (none)", - # choices=("default (none)", "chest", "flat"), - # help="additional lid (for straight top_edge only)") def render(self): x, y, h = self.x, self.y, self.h @@ -53,7 +51,7 @@ class ABox(Boxes): if self.bottom_edge != "e": self.rectangularWall(x, y, "ffff", move="up") - #self.drawAddOnLid(x, y, self.lid) + self.lid(x, y) self.rectangularWall(x, h, [b, sideedge, t3, sideedge], ignore_widths=[1, 6], move="right only") @@ -61,5 +59,3 @@ class ABox(Boxes): ignore_widths=[1, 6], move="up") self.rectangularWall(y, h, [b, "f", t4, "f"], ignore_widths=[1, 6], move="up") - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/agricolainsert.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/agricolainsert.py index ec5bdb7..3b1a8a6 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/agricolainsert.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/agricolainsert.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- # Copyright (C) 2020 Guillaume Collic # # This program is free software: you can redistribute it and/or modify @@ -17,11 +15,10 @@ import math from functools import partial + from boxes import Boxes, edges -from .dividertray import ( - SlotDescriptionsGenerator, - DividerSlotsEdge, -) + +from .dividertray import DividerSlotsEdge, SlotDescriptionsGenerator class AgricolaInsert(Boxes): @@ -127,7 +124,7 @@ protruding underneath. """ - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=1.0) @@ -654,7 +651,7 @@ protruding underneath. def render_upper_token_trays(self, tray_inner_height, box_width): """ - Upper level : multiple trays for each ressource + Upper level : multiple trays for each resource (beside horses which are on the lower level) """ tray_height = tray_inner_height + self.thickness @@ -780,7 +777,7 @@ class MoorBoxSideEdge(edges.BaseEdge): Edge for the sides of the moor tiles box """ - def __init__(self, boxes, corner_length, corner_height, lower_corner): + def __init__(self, boxes, corner_length, corner_height, lower_corner) -> None: super().__init__(boxes, None) self.corner_height = corner_height self.lower_corner = lower_corner @@ -799,11 +796,11 @@ class MoorBoxSideEdge(edges.BaseEdge): else: self.polyline(length) - def startwidth(self): + def startwidth(self) -> float: return self.corner_height - def endwidth(self): - return 0 if self.lower_corner else self.corner_height + def endwidth(self) -> float: + return 0.0 if self.lower_corner else self.corner_height class MoorBoxHoleEdge(edges.BaseEdge): @@ -811,7 +808,7 @@ class MoorBoxHoleEdge(edges.BaseEdge): Edge which does the notches for the moor tiles box """ - def __init__(self, boxes, height, corner_height, lower_corner): + def __init__(self, boxes, height, corner_height, lower_corner) -> None: super().__init__(boxes, None) self.height = height self.corner_height = corner_height @@ -851,11 +848,11 @@ class MoorBoxHoleEdge(edges.BaseEdge): ) self.polyline(*full_polyline) - def startwidth(self): + def startwidth(self) -> float: return self.corner_height - def endwidth(self): - return 0 if self.lower_corner else self.corner_height + def endwidth(self) -> float: + return 0.0 if self.lower_corner else self.corner_height class BedHeadEdge(edges.BaseEdge): @@ -863,7 +860,7 @@ class BedHeadEdge(edges.BaseEdge): Edge which does the head side of the Agricola player box """ - def __init__(self, boxes, hole_depth): + def __init__(self, boxes, hole_depth) -> None: super().__init__(boxes, None) self.hole_depth = hole_depth @@ -894,7 +891,7 @@ class Bed2SidesEdge(edges.BaseEdge): The next edge should be a NoopEdge """ - def __init__(self, boxes, bed_length, full_head_length, full_foot_height): + def __init__(self, boxes, bed_length, full_head_length, full_foot_height) -> None: super().__init__(boxes, None) self.bed_length = bed_length self.full_head_length = full_head_length @@ -926,7 +923,7 @@ class NoopEdge(edges.BaseEdge): Edge which does nothing, not even turn or move. """ - def __init__(self, boxes): + def __init__(self, boxes) -> None: super().__init__(boxes, None) def __call__(self, length, **kw): diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/airpurifier.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/airpurifier.py new file mode 100644 index 0000000..c308c7b --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/airpurifier.py @@ -0,0 +1,171 @@ +# Copyright (C) 2013-2016 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class AirPurifier(Boxes): + """Housing for the Nukit Open Air Purifier""" + + ui_group = "Misc" + + description = """See [Nukit Open Air Purifier](https://github.com/opennukit/Nukit-Open-Air-Purifier/) +""" + + fan_holes = { + 40.: 32.5, + 60.: 50, + 80.: 71.5, + 92.: 82.5, + 120.: 105, + 140.: 125, + } + + def __init__(self) -> None: + Boxes.__init__(self) + + self.addSettingsArgs(edges.FingerJointSettings) + self.addSettingsArgs(edges.DoveTailSettings, size=2.0, depth=1) + + self.buildArgParser(x=498., y=496.) + + self.argparser.add_argument( + "--filter_height", action="store", type=float, default=46.77, + help="height of the filter along the flow direction (in mm)") + self.argparser.add_argument( + "--rim", action="store", type=float, default=30., + help="rim around the filter holing it in place (in mm)") + self.argparser.add_argument( + "--fan_diameter", action="store", type=float, default=140., + choices=list(self.fan_holes.keys()), + help="diameter of the fans (in mm)") + self.argparser.add_argument( + "--filters", action="store", type=int, default=2, + choices=(1, 2), + help="Filters on both sides or only one") + self.argparser.add_argument( + "--split_frames", action="store", type=BoolArg(), default=True, + help="Split frame pieces into four thin rectangles to save material") + self.argparser.add_argument( + "--fans_left", action="store", type=int, default=-1, + help="number of fans on the left side (-1 for maximal number)") + self.argparser.add_argument( + "--fans_right", action="store", type=int, default=-1, + help="number of fans on the right side (-1 for maximal number)") + self.argparser.add_argument( + "--fans_top", action="store", type=int, default=0, + help="number of fans on the top side (-1 for maximal number)") + self.argparser.add_argument( + "--fans_bottom", action="store", type=int, default=0, + help="number of fans on the bottom side (-1 for maximal number)") + self.argparser.add_argument( + "--screw_holes", action="store", type=float, default=5., + help="diameter of the holes for screwing in the fans (in mm)") + + def fanCB(self, n, h, l, fingerHoles=True, split_frames=False): + fh = self.filter_height + t = self.thickness + r = self.rim + + def cb(): + if fingerHoles: + heights = [fh + t/2] + if self.filters > 1: + heights.append(h - fh - t/2) + for h_ in heights: + if split_frames: + self.fingerHolesAt(0, h_, r, 0) + self.fingerHolesAt(r, h_, l-2*r, 0) + self.fingerHolesAt(l-r, h_, r, 0) + else: + self.fingerHolesAt(0, h_, l, 0) + + max_n = int((l-20) // (self.fan_diameter + 10)) + if n == -1: + n_ = max_n + else: + n_ = min(max_n, n) + + if n_ == 0: + return + w = (l-20) / n_ + x = 10 + w / 2 + delta = self.fan_holes[self.fan_diameter] / 2 + if self.filters==2: + posy = h / 2 + else: + posy = (h + t + fh) / 2 + + for i in range(n_): + posx = x+i*w + self.hole(posx, posy, d=self.fan_diameter-4) + for dx in [-delta, delta]: + for dy in [-delta, delta]: + self.hole(posx + dx, posy + dy, d=self.screw_holes) + + return cb + + def render(self): + x, y, d = self.x, self.y, self.fan_diameter + t = self.thickness + r = self.rim + + y = self.y = y - t # shorten by one thickness as we use the wall space + + fh = self.filter_height + h = d + 2 + self.filters * (fh + t) + + + self.rectangularWall(x, d, "ffff", callback=[ + self.fanCB(self.fans_top, d, x, False)], label="top", move="up") + self.rectangularWall(x, h, "ffff", callback=[ + self.fanCB(self.fans_bottom, h, x)], label="bottom", move="up") + + be = te = edges.CompoundEdge(self, "fff", (r, y - 2*r, r)) \ + if self.split_frames else "f" + + if self.filters==2: + le = edges.CompoundEdge(self, "EFE", (fh + t, d+2, fh + t)) + else: + le = edges.CompoundEdge(self, "FE", (d+2, fh + t)) + te = "f" + + for fans in (self.fans_left, self.fans_right): + self.rectangularWall( + y, h, [be, "h", te, le], + callback=[self.fanCB(fans, h, y, split_frames=self.split_frames)], + move="up") + + + if self.split_frames: + e = edges.CompoundEdge(self, "DeD", (r, x - 2*r, r)) + for _ in range(self.filters): + self.rectangularWall(x, r, ["E", "h", e, "h"], move="up") + self.rectangularWall(y - 2*r, r, "hded", move="up") + self.rectangularWall(y - 2*r, r, "hded", move="up") + self.rectangularWall(x, r, [e, "h", "h", "h"], move="up") + + self.rectangularWall(x, r, ["F", "f", e, "f"], move="up") + self.rectangularWall(y - 2*r, r, "fded", move="up") + self.rectangularWall(y - 2*r, r, "fded", move="up") + self.rectangularWall(x, r, [e, "f", "f", "f"], move="up") + else: + for _ in range(self.filters): + self.rectangularWall(x, y, "Ffff", callback=[ + lambda:self.rectangularHole(x/2, y/2, x - r, y - r, r=10)], move="up") + self.rectangularWall(x, y, "Ehhh", callback=[ + lambda:self.rectangularHole(x/2, y/2, x - r, y - r, r=10)], move="up") + if self.filters==1: + self.rectangularWall(x, y, "hhhh", move="up") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/alledges.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/alledges.py index 63454bf..6f31f92 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/alledges.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/alledges.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2018 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,18 +15,19 @@ from boxes import * + class AllEdges(Boxes): """Showing all edge types""" ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.addSettingsArgs(edges.StackableSettings) self.addSettingsArgs(edges.HingeSettings) - self.addSettingsArgs(edges.LidSettings) + self.addSettingsArgs(edges.SlideOnLidSettings) self.addSettingsArgs(edges.ClickSettings) self.addSettingsArgs(edges.FlexSettings) self.addSettingsArgs(edges.HandleEdgeSettings) @@ -43,7 +43,7 @@ class AllEdges(Boxes): chars.reverse() self.moveTo(0, 10*t) - + for c in chars: with self.saved_context(): self.move(0, 0, "", True) @@ -56,6 +56,5 @@ class AllEdges(Boxes): self.move(0, 0, "") self.moveTo(0, 3*t + self.edges[c].spacing()) - self.text("%s - %s" % (c, self.edges[c].description)) + self.text(f"{c} - {self.edges[c].description}") self.moveTo(0, 12*t) - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/angledbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/angledbox.py index f9dbb2c..35e86f2 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/angledbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/angledbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,15 +14,14 @@ # along with this program. If not, see . from boxes import * -import math -import copy + class AngledBox(Boxes): """Box with both ends cornered""" ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.buildArgParser("x", "y", "h", "outside", "bottom_edge") @@ -38,17 +36,17 @@ class AngledBox(Boxes): def floor(self, x, y, n, edge='e', hole=None, move=None, callback=None, label=""): r, h, side = self.regularPolygon(2*n+2, h=y/2.0) t = self.thickness - + if n % 2: lx = x - 2 * h + side else: lx = x - 2 * r + side - + edge = self.edges.get(edge, edge) tx = x + 2 * edge.spacing() ty = y + 2 * edge.spacing() - + if self.move(tx, ty, move, before=True): return @@ -59,7 +57,7 @@ class AngledBox(Boxes): hr, hh, hside = self.regularPolygon(2*n+2, h=y/2.0-t) dx = side - hside hlx = lx - dx - + self.moveTo(dx/2.0, t+edge.spacing()) for i, l in enumerate(([hlx] + ([hside] * n))* 2): self.edge(l) @@ -96,12 +94,12 @@ class AngledBox(Boxes): t = self.thickness r, hp, side = self.regularPolygon(2*n+2, h=y/2.0) - + if n % 2: lx = x - 2 * hp + side else: lx = x - 2 * r + side - + fingerJointSettings = copy.deepcopy(self.edges["f"].settings) fingerJointSettings.setValues(self.thickness, angle=360./(2 * (n+1))) fingerJointSettings.edgeObjects(self, chars="gGH") @@ -126,22 +124,18 @@ class AngledBox(Boxes): if j == 0 or n % 2: self.rectangularWall(lx, h, move="right", edges=b+"GfG" if fingers else b+"GeG", - label="wall {}".format(cnt)) + label=f"wall {cnt}") else: self.rectangularWall(lx, h, move="right", edges=b+"gfg" if fingers else b+"geg", - label="wall {}".format(cnt)) + label=f"wall {cnt}") for i in range(n): cnt += 1 if (i+j*((n+1)%2)) % 2: # reverse for second half if even n self.rectangularWall(side, h, move="right", edges=b+"GfG" if fingers else b+"GeG", - label="wall {}".format(cnt)) + label=f"wall {cnt}") else: self.rectangularWall(side, h, move="right", edges=b+"gfg" if fingers else b+"geg", - label="wall {}".format(cnt)) - - - - + label=f"wall {cnt}") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/angledcutjig.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/angledcutjig.py index 1457989..08e5ebf 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/angledcutjig.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/angledcutjig.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,13 @@ from boxes import * + class AngledCutJig(Boxes): # Change class name! """Jig for making angled cuts in a laser cutter""" ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=1.) @@ -59,7 +59,6 @@ class AngledCutJig(Boxes): # Change class name! lambda:self.fingerHolesAt(0, 4.5*t, l2, 0), None, lambda:self.fingerHolesAt(0, 4.5*t, l2, 0), None], move="right") - + self.rectangularTriangle(x, th, "fef", num=2, move="up") self.rectangularTriangle(20, th2, "fef", num=2, move="up") - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/arcade.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/arcade.py index 66183ef..37c6008 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/arcade.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/arcade.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,10 +15,11 @@ from boxes import * + class Arcade(Boxes): """Desktop Arcade Machine""" - - def __init__(self): + + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.argparser.add_argument( @@ -43,18 +43,18 @@ class Arcade(Boxes): tw, th = y+2*r+(self.front+t) * math.sin(math.radians(15)), h+2*r+(self.topback+t)/2**0.5 if self.move(tw, th, move, True): return - + self.moveTo(r+(self.front+t) * math.sin(math.radians(15)), 0) with self.saved_context(): self.moveTo(0, r) self.polyline(y, 90, h, 45, self.topback+t, 90, self.top+2*t, 90, 100, -90, self.monitor_height, -30, self.keyboard_depth+2*t, 90, self.front+t, 75) - + self.fingerHolesAt(10, r+t/2, self.bottom, 0) self.polyline(y, (90, r)) self.fingerHolesAt(0.5*t, r+t/2, self.back, 0) self.fingerHolesAt(h-40-40, r+t/2, self.back, 0) - + self.polyline(h, (45, r)) self.fingerHolesAt(0, r+t/2, self.topback, 0) self.fingerHolesAt(self.topback+t/2, r+t, self.top, 90) @@ -66,7 +66,7 @@ class Arcade(Boxes): self.polyline(self.keyboard_depth-d_30+2*t, (90, r), self.front+t, (75, r)) self.move(tw, th, move) - + def keyboard(self): # Add holes for the joystick and buttons here pass @@ -74,7 +74,7 @@ class Arcade(Boxes): def speakers(self): self.hole(self.width/4., 50, 40) self.hole(self.width*3/4., 50, 40) - + def render(self): width = self.width t = self.thickness @@ -92,7 +92,7 @@ class Arcade(Boxes): h = self.h = ((self.monitor_height-self.topback+self.top+1*t+100) / 2**0.5 + + (self.keyboard_depth+2*t)*math.sin(math.radians(15)) + (self.front+t) * math.cos(math.radians(15))) - + self.bottom = y-40-0.5*t self.backwall = h-40 @@ -114,5 +114,3 @@ class Arcade(Boxes): # Sides self.side(move="up") self.side(move="up") - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/atreus21.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/atreus21.py index 386cb50..eee3b5a 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/atreus21.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/atreus21.py @@ -1,9 +1,7 @@ """Generator for a split atreus keyboard.""" -from copy import deepcopy +from boxes import Boxes, restore -from boxes import Boxes, Color, holeCol, restore, boolarg -from boxes.edges import FingerJointSettings from .keyboard import Keyboard @@ -14,11 +12,11 @@ class Atreus21(Boxes, Keyboard): half_btn = btn_size / 2 border = 6 - def __init__(self): + def __init__(self) -> None: super().__init__() self.add_common_keyboard_parameters( # By default, columns from Atreus 21 - default_columns_definition='4@3/4@6/4@11/4@5/4@0/1@{}'.format(self.btn_size * 0.5) + default_columns_definition=f'4@3/4@6/4@11/4@5/4@0/1@{self.btn_size * 0.5}' ) def render(self): @@ -87,7 +85,7 @@ class Atreus21(Boxes, Keyboard): @restore def half(self, hole_cb=None, reverse=False): - if hole_cb == None: + if hole_cb is None: hole_cb = self.key self.moveTo(self.half_btn, self.half_btn) self.apply_callback_on_columns( diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/basedbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/basedbox.py index 32baf1e..065a5f7 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/basedbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/basedbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -26,11 +25,11 @@ class BasedBox(Boxes): Use a vector graphics program (like Inkscape) to add holes or adjust the base plate. The width of the "brim" can also be adjusted with the **edge_width** parameter in the **Finger Joints Settings**. - + See ClosedBox for variant without a base. """ - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.buildArgParser("x", "y", "h", "outside") @@ -53,6 +52,3 @@ See ClosedBox for variant without a base. self.rectangularWall(x, y, "ffff", move="right", label="Top") self.rectangularWall(x, y, "hhhh", label="Base") - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bayonetbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bayonetbox.py index 5dc2874..136178e 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bayonetbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bayonetbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,15 +15,16 @@ from boxes import * + class BayonetBox(Boxes): """Round box made from layers with twist on top""" description = """Glue together - all outside rings to the bottom, all inside rings to the top.""" ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) - + self.argparser.add_argument( "--diameter", action="store", type=float, default=50., help="Diameter of the box in mm") @@ -65,7 +65,7 @@ class BayonetBox(Boxes): if self.move(d, d, move, True): return self.moveTo(d/2, d/2) - + self.alignmentHoles(inner=True) self.hole(0, 0, r=d/2 - 2.5*t) self.moveTo(d/2 - 1.5*t, 0, -90) @@ -102,14 +102,14 @@ class BayonetBox(Boxes): l = self.lugs a = 180 / l - + self.hole(0, 0, r=d/2 - 2.5*t) self.hole(0, 0, r=d/2 - 1.5*t) self.alignmentHoles(inner=True, outer=True) self.moveTo(d/2 - 1.5*t, 0, -90) for i in range(l): - self.polyline(0, (-1.3*a, r-1.5*t+p), 0, 90, 0.5*t, -90, 0, (-0.7*a, r-t+p), 0, -90, 0.5*t, 90) + self.polyline(0, (-1.3*a, r-1.5*t+p), 0, 90, 0.5*t, -90, 0, (-0.7*a, r-t+p), 0, -90, 0.5*t, 90) def render(self): @@ -119,8 +119,8 @@ class BayonetBox(Boxes): if not self.outside: self.diameter = d = d - 3*t - - + + self.parts.disc(d, callback=lambda: self.alignmentHoles(outer=True), move="right") self.parts.disc(d, callback=lambda: (self.alignmentHoles(outer=True), self.hole(0, 0, d/2-1.5*t)), move="right") self.parts.disc(d, callback=self.lowerCB, move="right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bintray.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bintray.py index 113a6cf..f872be3 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bintray.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bintray.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,7 +14,7 @@ # along with this program. If not, see . from boxes import * -import math, copy + class BinFrontEdge(edges.BaseEdge): char = "B" @@ -26,7 +25,7 @@ class BinFrontEdge(edges.BaseEdge): self.corner(-a1) for i, l in enumerate(self.settings.sy): self.edges["e"](l* (f**2+(1-f)**2)**0.5) - self.corner(a2) + self.corner(a2) self.edges["f"](l*f*2**0.5) if i < len(self.settings.sy)-1: if self.char == "B": @@ -37,7 +36,7 @@ class BinFrontEdge(edges.BaseEdge): else: self.corner(-45) - def margin(self): + def margin(self) -> float: return max(self.settings.sy) * self.settings.front class BinFrontSideEdge(BinFrontEdge): @@ -48,14 +47,14 @@ class BinTray(Boxes): ui_group = "Shelf" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser("sx", "sy", "h", "outside", "hole_dD") self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=0.5) self.argparser.add_argument( "--front", action="store", type=float, default=0.4, - help="fraction of bin height covert with slope") - + help="fraction of bin height covered with slope") + def xSlots(self): posx = -0.5 * self.thickness for x in self.sx[:-1]: @@ -73,7 +72,7 @@ class BinTray(Boxes): for x in self.sx: self.fingerHolesAt(posy, posx, x) posx += x + self.thickness - + def addMount(self): ds = self.hole_dD[0] @@ -96,7 +95,7 @@ class BinTray(Boxes): for x in self.sx[:-1]: posx += x + self.thickness self.fingerHolesAt(posx, 0, self.hi) - + def frontHoles(self, i): def CB(): posx = -0.5 * self.thickness @@ -119,7 +118,7 @@ class BinTray(Boxes): x = sum(self.sx) + self.thickness * (len(self.sx) - 1) y = sum(self.sy) + self.thickness * (len(self.sy) - 1) - + h = self.h hi = self.hi = h t = self.thickness diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/birdhouse.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/birdhouse.py index 45f9c2c..e363822 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/birdhouse.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/birdhouse.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2022 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,18 +15,20 @@ from boxes import * + class BirdHouse(Boxes): """Simple Bird House""" - ui_group = "Unstable" # "Misc" + ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, finger=10.0,space=10.0) - - # remove cli params you do not need self.buildArgParser(x=200, y=200, h=200) + self.argparser.add_argument( + "--roof_overhang", action="store", type=float, default=0.4, + help="overhang as fraction of the roof length") def side(self, x, h, edges="hfeffef", callback=None, move=None): angles = (90, 0, 45, 90, 45, 0, 90) @@ -36,7 +37,7 @@ class BirdHouse(Boxes): lengths = (x, h, t, roof, roof, t, h) edges = [self.edges.get(e, e) for e in edges] - edges.append(edges[0]) # wrap arround + edges.append(edges[0]) # wrap around tw = x + edges[1].spacing() + edges[-2].spacing() th = h + x/2 + t + edges[0].spacing() + max(edges[3].spacing(), edges[4].spacing()) @@ -48,8 +49,32 @@ class BirdHouse(Boxes): for i in range(7): self.cc(callback, i, y=self.burn+edges[i].startwidth()) edges[i](lengths[i]) - self.edgeCorner(edges[i], edges[i+1], angles[i]) - + self.edgeCorner(edges[i], edges[i+1], angles[i]) + + self.move(tw, th, move) + + def roof(self, x, h, overhang, edges="eefe", move=None): + t = self.thickness + edges = [self.edges.get(e, e) for e in edges] + + tw = x + 2*t + 2*overhang + edges[1].spacing() + edges[3].spacing() + th = h + 2*t + overhang + edges[0].spacing() + edges[2].spacing() + + if self.move(tw, th, move, True): + return + + self.moveTo(overhang + edges[3].spacing(), edges[0].margin()) + edges[0](x + 2*t) + self.corner(90, overhang) + edges[1](h + 2*t) + self.edgeCorner(edges[1], edges[2]) + self.fingerHolesAt(overhang + 0.5*t, edges[2].startwidth(), h, 90) + self.fingerHolesAt(x + overhang + 1.5*t, edges[2].startwidth(), h, 90) + edges[2](x + 2*t + 2*overhang) + self.edgeCorner(edges[2], edges[3]) + edges[3](h + 2*t) + self.corner(90, overhang) + self.move(tw, th, move) def side_hole(self, width): @@ -61,15 +86,16 @@ class BirdHouse(Boxes): x, y, h = self.x, self.y, self.h roof = 2**0.5 * x / 2 + overhang = roof * self.roof_overhang cbx = [lambda: self.side_hole(x)] cby = [lambda: self.side_hole(y)] - + self.side(x, h, callback=cbx, move="right") self.side(x, h, callback=cbx, move="right") self.rectangularWall(y, h, "hFeF", callback=cby, move="right") self.rectangularWall(y, h, "hFeF", callback=cby, move="right") self.rectangularWall(x, y, "ffff", move="right") - self.edges["h"].settings.setValues(self.thickness, relative=False, edge_width=0.1*roof) - self.flangedWall(y, roof, "ehfh", r=0.2*roof, flanges=[0.2*roof], move="right") - self.flangedWall(y, roof, "ehFh", r=0.2*roof, flanges=[0.2*roof], move="right") + self.edges["h"].settings.setValues(self.thickness, relative=False, edge_width=overhang) + self.roof(y, roof, overhang, "eefe", move="right") + self.roof(y, roof, overhang, "eeFe", move="right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bottlestack.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bottlestack.py index 27f7c03..fb96e6f 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bottlestack.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bottlestack.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2020 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,19 +15,20 @@ from boxes import * + class BottleStack(Boxes): """Stack bottles in a fridge""" - description = """When rendered with the "double" option the parts with the double slots get connected the shorter beams in the asymetrical slots. + description = """When rendered with the "double" option the parts with the double slots get connected the shorter beams in the asymmetrical slots. Without the "double" option the stand is a bit more narrow. """ ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) - + self.argparser.add_argument( "--diameter", action="store", type=float, default=80, help="diameter of the bottles in mm") @@ -41,7 +41,7 @@ Without the "double" option the stand is a bit more narrow. self.argparser.add_argument( "--double", action="store", type=boolarg, default=True, help="two pieces that can be combined to up to double the width") - + def front(self, h_sides, offset=0, move=None): @@ -49,7 +49,7 @@ Without the "double" option the stand is a bit more narrow. a = 60 nr = self.number r1 = self.diameter / 2.0 # bottle - r2 = r1 / math.cos(math.radians(90-a)) - r1 # inbetween + r2 = r1 / math.cos(math.radians(90-a)) - r1 # in between if self.double: r3 = 1.5*t # upper corners else: @@ -58,10 +58,10 @@ Without the "double" option the stand is a bit more narrow. h_extra = 1*t h_s = h_sides - t p = 0.05*t # play - + tw, th = nr * r1 * 2 + 2*r3, h + 2*t - + if self.move(tw, th, move, True): return @@ -116,26 +116,26 @@ Without the "double" option the stand is a bit more narrow. if open_sides: h_extra -= h_s - + self.polyline(0, 90, h_extra+h-r3, (90, r3)) for i in range(nr): self.polyline(0, (a, r2), 0, (-2*a, r1), 0, (a, r2)) self.polyline(0, (90, r3), h_extra+h-r3, 90) - + self.move(tw, th, move) def side(self, l, h, short=False, move=None): t = self.thickness short = bool(short) - + tw, th = l + 2*t - 4*t*short, h if self.move(tw, th, move, True): return self.moveTo(t, 0) - + self.polyline(l-3*t*short) if short: end = [90, h-t, 90, t, -90, t, 90] @@ -153,7 +153,7 @@ Without the "double" option the stand is a bit more narrow. nr = self.number h_sides = 2*t pieces = 2 if self.double else 1 - + for offset in range(pieces): self.front(h_sides, offset, move="up") self.front(h_sides, offset, move="up") @@ -161,5 +161,3 @@ Without the "double" option the stand is a bit more narrow. for short in range(pieces): for i in range(nr+1): self.side(d, h_sides, short, move="up") - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bottletag.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bottletag.py index 32c917b..5d6a704 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bottletag.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/bottletag.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -22,7 +21,7 @@ class BottleTag(Boxes): ui_group = "Misc" # see ./__init__.py for names - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser() @@ -86,4 +85,3 @@ class BottleTag(Boxes): with self.saved_context(): self.moveTo(0, 0, -90) self.edge(0.5) - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/breadbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/breadbox.py index 6376f45..492f02c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/breadbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/breadbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2022 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,17 +15,18 @@ from boxes import * + class BreadBox(Boxes): """A BreadBox with a gliding door""" - ui_group = "Unstable" # "FlexBox" + ui_group = "FlexBox" description = """Beware of the rolling shutter effect! Use wax on sliding surfaces. """ def side(self, l, h, r, move=None): t = self.thickness - + if self.move(l+2*t, h+2*t, move, True): return @@ -52,7 +52,7 @@ class BreadBox(Boxes): self.edges["f"](l/2) self.polyline(0, 90) self.edges["f"](h) - + self.move(l+2*t, h+2*t, move) def cornerRadius(self, r, two=False, move=None): @@ -80,7 +80,7 @@ class BreadBox(Boxes): self.polyline(l/2-r, (90, r-1.5*t), h-r, 90, t, 90, h-r, (-90, r-2.5*t), l/2-r, 90, t, 90) self.moveTo(-t-s, t+s) self.polyline(l/2-r, (90, r-1.5*t), h-r, 90, t, 90, h-r, (-90, r-2.5*t), l/2-r, 90, t, 90) - + self.move(tw, th, move) def door(self, l, h, move=None): @@ -93,7 +93,7 @@ class BreadBox(Boxes): self.polyline(0, 90, h, 90, l, 90, h, 90) self.move(l, h, move) - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=0.5) @@ -107,7 +107,7 @@ class BreadBox(Boxes): def render(self): x, y, h, r = self.x, self.y, self.h, self.radius self.n = n = 3 - + if not r: self.radius = r = h / 2 self.radius = r = min(r, h/2) @@ -123,11 +123,11 @@ class BreadBox(Boxes): self.rectangularWall(x, y, "FEFF", move="right") self.rectangularWall(x/2, y, "FeFF", move="right") - + self.door(x/2 + h - 2*r + 0.5*math.pi*r + 2*t, y-0.2*t, move="right") self.rectangularWall(2*t, y-2.2*t, edges="eeef", move="right") - + a = 90. / n ls = 2*math.sin(math.radians(a/2)) * (r-2.5*t) @@ -137,9 +137,9 @@ class BreadBox(Boxes): self.rectangularWall(h-r, y, "fbfe", move="right") - + self.rectangularWall(ls, y, "fafB", move="right") - + for i in range(n-2): self.rectangularWall(ls, y, "fafA", move="right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/burntest.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/burntest.py index 7051f8c..b291f3d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/burntest.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/burntest.py @@ -1,5 +1,5 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi +# Copyright (C) 2024 Seth Fischer # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,10 +14,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from datetime import date + from boxes import * + class BurnTest(Boxes): """Test different burn values""" + description = """This generator will make shapes that you can use to select optimal value for burn parameter for other generators. After burning try to attach sides with the same value and use best fitting one on real projects. @@ -34,41 +38,78 @@ See also LBeam that can serve as compact BurnTest and FlexTest for testing flex ui_group = "Part" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.buildArgParser(x=100) self.argparser.add_argument( - "--step", action="store", type=float, default=0.01, - help="increases in burn value between the sides") + "--step", + action="store", + type=float, + default=0.01, + help="increases in burn value between the sides", + ) self.argparser.add_argument( - "--pairs", action="store", type=int, default=2, - help="number of pairs (each testing four burn values)") - + "--pairs", + action="store", + type=int, + default=2, + help="number of pairs (each testing four burn values)", + ) + self.argparser.add_argument( + "--date", + action="store", + type=boolarg, + default=False, + help="add current date etching to each piece", + ) + self.argparser.add_argument( + "--id", + action="store", + type=str, + default="", + help="add identifier etching to each piece", + ) def render(self): + font = { + "fontsize": 12.5 * self.x / 100 if self.x < 81 else 10, + "align": "center", + "color": Color.ETCHING, + } + font_meta = font.copy() + font_meta["fontsize"] = font["fontsize"] * 0.75 + + today = date.today().strftime("%Y-%m-%d") + x, s = self.x, self.step t = self.thickness - - fsize = 12.5 * self.x / 100 if self.x < 81 else 10 self.moveTo(t, t) for cnt in range(self.pairs): - for i in range(4): - self.text("%.3fmm" % self.burn, x/2, t, fontsize = fsize, align="center", color=Color.ETCHING) + self.text("%.3fmm" % self.burn, x / 2, t, **font) + if self.date and i == 3: + self.text(today, x / 2, 20, **font_meta) + if self.id and i == 1: + self.text(self.id, x / 2, 20, **font_meta) self.edges["f"](x) self.corner(90) self.burn += s - - self.burn -= 4*s - self.moveTo(x+2*t+self.spacing, -t) + self.burn -= 4 * s + self.moveTo(x + 2 * t + self.spacing, -t) + for i in range(4): - self.text("%.3fmm" % self.burn, x/2, t, fontsize = fsize, align="center", color=Color.ETCHING) + self.text("%.3fmm" % self.burn, x / 2, t, **font) + if self.date and i == 3: + self.text(today, x / 2, 20, **font_meta) + if self.id and i == 1: + self.text(self.id, x / 2, 20, **font_meta) self.edges["F"](x) self.polyline(t, 90, t) self.burn += s - self.moveTo(x+2*t+self.spacing, t) + + self.moveTo(x + 2 * t + self.spacing, t) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/can_storage.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/can_storage.py index 58238a0..1c19aa1 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/can_storage.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/can_storage.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -8,14 +7,14 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERself.canHightANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . from boxes import * -import math + class FrontEdge(edges.BaseEdge): char = "a" @@ -24,7 +23,7 @@ class FrontEdge(edges.BaseEdge): x = math.ceil( ((self.canDiameter * 0.5 + 2 * self.thickness) * math.sin(math.radians(self.chuteAngle))) / self.thickness) if self.top_edge != "e": self.corner(90, self.thickness) - self.edge(0.5 * self.canDiameter) + self.edge(0.5 * self.canDiameter) self.corner(-90, 0.25 * self.canDiameter) else: self.moveTo(-self.burn, self.canDiameter + self.thickness, -90) @@ -72,7 +71,7 @@ class BarrierEdge(edges.BaseEdge): self.corner(90,self.thickness/2) self.edge(0.2*length) - def startwidth(self): + def startwidth(self) -> float: return self.boxes.thickness class CanStorage(Boxes): @@ -83,13 +82,13 @@ for AA batteries: ![CanStorage for AA batteries](static/samples/CanStorageAA.jpg) -for canned tomatos: +for canned tomatoes: """ ui_group = "Misc" - - def __init__(self): + + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, finger=2.0, space=2.0, surroundingspaces=0.0) @@ -110,15 +109,15 @@ for canned tomatos: "--canDiameter", action="store", type=float, default=75, help="outer diameter of the cans to be stored (in mm)") self.argparser.add_argument( - "--canHight", action="store", type=float, default=110, - help="hight of the cans to be stored (in mm)") + "--canHeight", action="store", type=float, default=110, + help="height of the cans to be stored (in mm)") self.argparser.add_argument( "--canNum", action="store", type=int, default=12, help="number of cans to be stored") self.argparser.add_argument( "--chuteAngle", action="store", type=float, default=5.0, help="slope angle of the chutes") - + def DrawPusher(self, dbg = False): with self.saved_context(): if dbg == False: @@ -132,9 +131,9 @@ for canned tomatos: self.edge(self.thickness) self.corner(-90) self.edge(0.25*self.pusherA) - + self.corner(90-self.chuteAngle) - + self.edge(0.25*self.pusherB) self.corner(-90) self.edge(self.thickness) @@ -147,54 +146,54 @@ for canned tomatos: self.corner(90+self.pusherAngle+self.chuteAngle) self.edge(self.pusherC) - + def cb_top_chute(self, nr): if nr == 0: # fill with holes border = [ - (0, 0), - (self.top_chute_depth, 0), - (self.top_chute_depth, 0.2 * self.width - self.thickness), - (self.top_chute_depth - self.thickness, 0.2 * self.width), - (self.top_chute_depth - 1.5*self.canDiameter, 0.2 * self.width), - (self.top_chute_depth - 1.5*self.canDiameter, 0.8 * self.width), - (self.top_chute_depth - self.thickness, 0.8 * self.width), - (self.top_chute_depth, 0.8 * self.width + self.thickness), - (self.top_chute_depth, self.width), - (0, self.width), + (0, 0), + (self.top_chute_depth, 0), + (self.top_chute_depth, 0.2 * self.width - self.thickness), + (self.top_chute_depth - self.thickness, 0.2 * self.width), + (self.top_chute_depth - 1.5*self.canDiameter, 0.2 * self.width), + (self.top_chute_depth - 1.5*self.canDiameter, 0.8 * self.width), + (self.top_chute_depth - self.thickness, 0.8 * self.width), + (self.top_chute_depth, 0.8 * self.width + self.thickness), + (self.top_chute_depth, self.width), + (0, self.width), ] if self.fillHoles_fill_pattern != "no fill": self.fillHoles( - pattern="hbar", + pattern="hbar", border=border, max_radius = min(2*self.thickness, self.fillHoles_hole_max_radius) if self.fillHoles_fill_pattern in ["hbar", "vbar"] else min(2*self.thickness, self.width/30), hspace=min(2*self.thickness, self.fillHoles_space_between_holes) if self.fillHoles_fill_pattern in ["hbar", "vbar"] else min(2*self.thickness, self.width/20), bspace=min(2*self.thickness, self.fillHoles_space_to_border) if self.fillHoles_fill_pattern in ["hbar", "vbar"] else min(2*self.thickness, self.width/20), bar_length=self.fillHoles_bar_length, max_random=self.fillHoles_max_random, - ) - + ) + def cb_top(self, nr): if nr == 0: # fill with holes border = [ - (0, 0), - (self.depth, 0), - (self.depth, self.width), - (0, self.width), + (0, 0), + (self.depth, 0), + (self.depth, self.width), + (0, self.width), ] if self.fillHoles_fill_pattern != "no fill": self.fillHoles( - pattern="hbar", + pattern="hbar", border=border, max_radius = min(2*self.thickness, self.fillHoles_hole_max_radius) if self.fillHoles_fill_pattern in ["hbar", "vbar"] else min(2*self.thickness, self.width/30), hspace=min(2*self.thickness, self.fillHoles_space_between_holes) if self.fillHoles_fill_pattern in ["hbar", "vbar"] else min(2*self.thickness, self.width/20), bspace=min(2*self.thickness, self.fillHoles_space_to_border) if self.fillHoles_fill_pattern in ["hbar", "vbar"] else min(2*self.thickness, self.width/20), bar_length=self.fillHoles_bar_length, max_random=self.fillHoles_max_random, - ) + ) def cb_bottom_chute(self, nr): if nr == 1: @@ -202,7 +201,7 @@ for canned tomatos: self.rectangularHole(self.width*0.85-0.5*self.thickness, 0.25*self.pusherA, self.thickness, 0.5*self.pusherA, center_x=False, center_y=False) self.rectangularHole(self.width*0.5 -0.5*self.thickness, 0.25*self.pusherA, self.thickness, 0.5*self.pusherA, center_x=False, center_y=False) self.rectangularHole(self.width*0.15-0.5*self.thickness, 0.25*self.pusherA, self.thickness, 0.5*self.pusherA, center_x=False, center_y=False) - + def cb_back(self, nr): if nr == 1: @@ -211,7 +210,7 @@ for canned tomatos: self.rectangularHole(self.width*0.5 -0.5*self.thickness, self.thickness + self.depth * math.tan(math.radians(self.chuteAngle)) + 0.25*self.pusherB, self.thickness, 0.5*self.pusherB + self.thickness, center_x=False, center_y=False) self.rectangularHole(self.width*0.15-0.5*self.thickness, self.thickness + self.depth * math.tan(math.radians(self.chuteAngle)) + 0.25*self.pusherB, self.thickness, 0.5*self.pusherB + self.thickness, center_x=False, center_y=False) - + def cb_sides(self, nr): if nr == 0: # for debugging only @@ -238,7 +237,7 @@ for canned tomatos: self.moveTo(self.depth-self.pusherA, self.thickness + (self.depth-self.pusherA) * math.tan(math.radians(self.chuteAngle))) self.moveTo(0,0,self.chuteAngle) self.DrawPusher(True) - + with self.saved_context(): # draw cans, top row self.moveTo(0, self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height + 0.5 * self.thickness, -self.chuteAngle) @@ -264,47 +263,47 @@ for canned tomatos: self.fingerHolesAt(0, 0, self.barrier_height, 0) # fill with holes border = [ - (2*self.thickness, 0.5*self.thickness + 2*self.thickness * math.tan(math.radians(self.chuteAngle)) + 0.5*self.thickness/math.cos(math.radians(self.chuteAngle))), - (self.depth, self.thickness + self.depth * math.tan(math.radians(self.chuteAngle))), - (self.depth, self.height), + (2*self.thickness, 0.5*self.thickness + 2*self.thickness * math.tan(math.radians(self.chuteAngle)) + 0.5*self.thickness/math.cos(math.radians(self.chuteAngle))), + (self.depth, self.thickness + self.depth * math.tan(math.radians(self.chuteAngle))), + (self.depth, self.height), (self.thickness + 0.75 * self.canDiameter, self.height), (self.thickness + 0.75 * self.canDiameter, 0.5*self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height + self.thickness - (self.thickness + 0.75 * self.canDiameter) * math.tan(math.radians(self.chuteAngle)) + 0.5*self.thickness/math.cos(math.radians(self.chuteAngle))), (self.top_chute_depth * math.cos(math.radians(self.chuteAngle)), self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height + self.thickness - (self.top_chute_depth) * math.sin(math.radians(self.chuteAngle))), (self.top_chute_depth * math.cos(math.radians(self.chuteAngle)), self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height - (self.top_chute_depth) * math.sin(math.radians(self.chuteAngle))), (self.thickness + 0.75 * self.canDiameter, 1.5*self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height - (self.thickness + 0.75 * self.canDiameter) * math.tan(math.radians(self.chuteAngle)) - 0.5*self.thickness/math.cos(math.radians(self.chuteAngle))), (self.thickness + 0.75 * self.canDiameter, 2*self.thickness + self.barrier_height ), - (2*self.thickness, 2*self.thickness + self.barrier_height), + (2*self.thickness, 2*self.thickness + self.barrier_height), ] self.fillHoles( - pattern=self.fillHoles_fill_pattern, - border=border, - max_radius=self.fillHoles_hole_max_radius, - hspace=self.fillHoles_space_between_holes, - bspace=self.fillHoles_space_to_border, - min_radius=self.fillHoles_hole_min_radius, + pattern=self.fillHoles_fill_pattern, + border=border, + max_radius=self.fillHoles_hole_max_radius, + hspace=self.fillHoles_space_between_holes, + bspace=self.fillHoles_space_to_border, + min_radius=self.fillHoles_hole_min_radius, style=self.fillHoles_hole_style, bar_length=self.fillHoles_bar_length, max_random=self.fillHoles_max_random, ) - + def render(self): self.chuteAngle = self.chuteAngle - + self.pusherAngle = 30 # angle of pusher self.pusherA = 0.75 * self.canDiameter # length of pusher self.pusherB = self.pusherA / math.sin(math.radians(180 - (90+self.chuteAngle) - self.pusherAngle)) * math.sin(math.radians(self.pusherAngle)) self.pusherC = self.pusherA / math.sin(math.radians(180 - (90+self.chuteAngle) - self.pusherAngle)) * math.sin(math.radians(90+self.chuteAngle)) - + self.addPart(FrontEdge(self, self)) self.addPart(TopChuteEdge(self, self)) self.addPart(BarrierEdge(self, self)) - + if self.canDiameter < 8 * self.thickness: self.edges["f"].settings.setValues(self.thickness, True, finger=1.0) self.edges["f"].settings.setValues(self.thickness, True, space=1.0) self.edges["f"].settings.setValues(self.thickness, True, surroundingspaces=0.0) - + if self.canDiameter < 4 * self.thickness: raise ValueError("Can diameter has to be at least 4 times the material thickness!") @@ -316,29 +315,31 @@ for canned tomatos: self.top_chute_depth = (self.depth - 1.1 * self.canDiameter) / math.cos(math.radians(self.chuteAngle)) self.bottom_chute_height = max((self.depth - 1.1 * self.canDiameter) * math.sin(math.radians(self.chuteAngle)), 0.1 * self.canDiameter) self.bottom_chute_depth = self.depth / math.cos(math.radians(self.chuteAngle)) - self.barrier_height = 0.25 * self.canDiameter - + self.barrier_height = min( + 0.25 * self.canDiameter, + self.bottom_chute_height + self.top_chute_height - self.thickness) + if (self.top_chute_depth + self.bottom_chute_height - self.thickness) < (self.barrier_height + self.canDiameter * 0.1): self.bottom_chute_height = self.barrier_height + self.canDiameter * 0.1 + self.thickness - self.top_chute_depth - + self.height = self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height + 0.5 * self.thickness + self.canDiameter + 1.5 * self.thickness # measurements from bottom to top - self.width = 0.01 * self.canHight + self.canHight + 0.01 * self.canHight + self.width = 0.01 * self.canHeight + self.canHeight + 0.01 * self.canHeight edgs = self.bottom_edge + "h" + self.top_edge + "a" - + # render your parts here self.rectangularWall(self.depth, self.height, edges=edgs, callback=self.cb_sides, move="up", label="right") self.rectangularWall(self.depth, self.height, edges=edgs, callback=self.cb_sides, move="up mirror", label="left") - + self.rectangularWall(self.bottom_chute_depth, self.width, "fefe", callback=self.cb_bottom_chute, move="up", label="bottom chute") self.rectangularWall(self.top_chute_depth, self.width, "fbfe", callback=self.cb_top_chute, move="up", label="top chute") self.rectangularWall(self.barrier_height, self.width, "fAfe", move="right", label="barrier") self.rectangularWall(self.height, self.width, "fefe", callback=self.cb_back, move="up", label="back") self.rectangularWall(self.barrier_height, self.width, "fefe", move="left only", label="invisible") - + if self.top_edge != "e": self.rectangularWall(self.depth, self.width, "fefe", callback=self.cb_top, move="up", label="top") - + pusherH = self.pusherB * math.cos(math.radians(self.chuteAngle)) + self.thickness pusherV = self.pusherC * math.cos(math.radians(self.chuteAngle)) + self.thickness @@ -362,4 +363,3 @@ for canned tomatos: self.rectangularWall(self.edges["š"].settings.width+5*self.thickness, self.edges["š"].settings.height-4*self.burn, "eeee", move="right", label="Stabilizer 3") self.rectangularWall(self.edges["š"].settings.width+5*self.thickness, self.edges["š"].settings.height-4*self.burn, "eeee", move="right", label="Stabilizer 4") self.text("Glue a stabilizer on the inside of each bottom\nside stacking foot for lateral stabilization.",3 ,0 , fontsize=4, color=Color.ANNOTATIONS) - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/carbonfilter.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/carbonfilter.py new file mode 100644 index 0000000..ebf2c8c --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/carbonfilter.py @@ -0,0 +1,240 @@ +# Copyright (C) 2013-2023 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class CarbonFilter(Boxes): + """Compact filter for activated char coal pellets""" + + description = """The filter does not include the top rim. You need some rectangular wooden strip about 2-3cm in size to glue around. The x and y are without this rim and should be about 5 cm smaller that the nominal size. + +The following sizes are currently hard coded: + +* Height of rails on top: 50mm +* Borders on top: 40mm +* Char coal width (horizontal): 40mm +* Bottom width: 40 + 20 + 40 mm + +For assembly it is important that all bottom plates are the same way up. This allows the ribs of adjacent pockets to pass beside each other. + +There are three type of ribs: + +* Those with staight tops go in the middle of the bottom plates +* Those pointier angle go at the outer sides and meet with the side bars +* The less pointy go at all other sides of the bottom plates that will end up on the inside + +The last two types of ribs do not have finger joints on the outside but still need to be glued to the top beam of the adjacent pocket or the left or right side bar. +""" + + ui_group = "Misc" + + def __init__(self) -> None: + Boxes.__init__(self) + + self.addSettingsArgs(edges.FingerJointSettings) + self.buildArgParser(x=550, y=550, h=250) + self.argparser.add_argument( + "--pockets", action="store", type=int, default=3, + help="number of V shaped filter pockets") + self.argparser.add_argument( + "--ribs", action="store", type=int, default=12, + help="number of ribs to hold the bottom and the mesh") + + def sideCB(self): + x, y, h = self.x, self.y, self.h + t = self.thickness + p = self.pockets + + posx = t + w = self.w + a = self.a + + self.fingerHolesAt(t/2, h, 50, -90) + self.fingerHolesAt(x-t/2, h, 50, -90) + + for i in range(p): + self.fingerHolesAt(posx + t/2, h, 50, -90+a) + self.fingerHolesAt(posx + 40 + t/2, h, 50, -90+a) + self.fingerHolesAt(posx + w - t/2, h, 50, -90-a) + self.fingerHolesAt(posx + w - 40 - t/2, h, 50, -90-a) + + self.fingerHolesAt(posx + w/2 -50 + t, 3.5*t, 100 - 2*t, 0) + + posx += w + + def bottomCB(self): + t = self.thickness + for i in range(self.ribs): + self.fingerHolesAt((i+1) * self.y / (self.ribs + 1) - 1.5 * t, + 0, 4*t, 90) + self.fingerHolesAt((i+1) * self.y / (self.ribs + 1) - 1.5 * t, + 40 - t, 20, 90) + + def topRailCB(self): + t = self.thickness + for i in range(self.ribs): + self.fingerHolesAt((i+1) * self.y / (self.ribs + 1) - 1.5 * t, + 0, 30, 90) + + def innerRibs(self, n, move=None): + + x, y, h = self.x, self.y, self.h + t = self.thickness + a = self.a + a_ = math.radians(a) + + l = (h-4*t) / math.cos(a_) - 0.5 * t * math.sin(a_) + + tw = n * (20 + self.spacing) + l * math.sin(a_) + th = h-3*t - 20 * math.cos(a_) + self.spacing + + if self.move(tw, th, move, True): + return + + self.moveTo(0, t) + + for i in range(n): + self.edges["f"](20) + self.polyline(0, 90-a, l - 50, 90, t, -90) + self.edges["f"](30) + self.polyline(0, 90 + a, 20-t, 90 - a, l-20 + t * math.sin(a_), + 90+a) + + self.moveTo(20 + self.spacing) + self.ctx.stroke() + + self.move(tw, th, move, label="Inner ribs") + + def sideHolders(self, n, move=None): + + x, y, h = self.x, self.y, self.h + t = self.thickness + a = self.a + a_ = math.radians(a) + + l = (h-4*t) / math.cos(a_) - 0.5 * t * math.sin(a_) - 50 + + tw = n * (10 + self.spacing) + l * math.sin(a_) + th = h - 4*t - 50 + + if self.move(tw, th, move, True): + return + + for i in range(n): + self.polyline(10, 90-a, l, 90+a, 10, 90-a, l, 90+a) + self.ctx.stroke() + self.moveTo(10+self.spacing) + + self.move(tw, th, move, label="Inner ribs") + + def topStabilizers(self, n, move=None): + t = self.thickness + + l = 2* (self.h-60) * math.sin(math.radians(self.a)) - 20 + tw = n * (6*t + self.spacing) + th = l + 4*t + + if self.move(tw, th, move, True): + return + + self.moveTo(t) + for i in range(n): + for j in range(2): + self.polyline(0, 90, 2*t, -90, t, -90, 2*t, 90, 3*t, (90, t), + l+2*t, (90, t)) + self.ctx.stroke() + self.moveTo(6*t + self.spacing) + + self.move(tw, th, move, label="Inner ribs") + + def outerRibs(self, n, n_edge, move=None): + + x, y, h = self.x, self.y, self.h + t = self.thickness + a = self.a + a_ = math.radians(a) + + l = (h-4*t) / math.cos(a_) + 0.5 * t * math.sin(a_) + + dl = (20 - t) * (math.tan(math.pi/2 - 2*a_) + math.sin(a_)) + dll = (20 - t) * (1 / math.sin(2*a_)) + + dl2 = (20 - t) * (math.tan(math.pi/2 - a_) + math.sin(a_)) + dll2 = (20 - t) * (1 / math.sin(a_)) + + tw = (n // 2) * (40 + t) + l * math.sin(a_) + th = h + 5*t + + if self.move(tw, th, move, True): + return + + self.moveTo(2*t) + + for i in range(n): + self.polyline(0*t + 20, (90, 2*t), 2*t, -a) + if i < n_edge: + self.polyline(l - dl2 - t * math.sin(a_), a, + dll2, 180 - a, 20) + else: + self.polyline(l - dl - t * math.sin(a_), 2*a, + dll, 180 - 2*a, 20) + self.edges["f"](30) + self.polyline(0, -90, t, 90, l - 50, a, t, -90) + self.edges["f"](4*t) + self.polyline(0, 90, 1*t, (90, 2*t)) + + self.moveTo(t + 40)# + self.spacing) + if i + 1 == n // 2: + self.moveTo(2*t+0.7*self.spacing, h + 5*t, 180) + self.ctx.stroke() + + self.move(tw, th, move, label="Outer ribs") + + def render(self): + # adjust to the variables you want in the local scope + x, y, h = self.x, self.y, self.h + + self.y = y = self.adjustSize(y) + + t = self.thickness + + self.w = (x - 2*t) / self.pockets + self.a = math.degrees(math.atan((self.w - 100) / 2 / (h - 4*t))) + + # sides + for i in range(2): + self.rectangularWall(x, h, callback=[self.sideCB], move="up") + for i in range(2): + self.rectangularWall(y, 50, "efef", label="Sides", move="up") + + # top rails + for i in range(self.pockets * 4): + self.rectangularWall(y, 50, "efef", + callback=[self.topRailCB], + label="Top rails", move="up") + + # bottoms + w = 100 - 2*t + for i in range(self.pockets): + self.rectangularWall( + y, w, "efef", + callback=[self.bottomCB, None, self.bottomCB], + label="bottom plate", move="up") + + self.innerRibs(self.pockets * self.ribs * 2, move="up") + self.outerRibs(self.pockets * self.ribs * 2, self.ribs * 2, move="up") + self.sideHolders(self.pockets * 8, move="up") + self.topStabilizers(min(3, self.ribs) *self.pockets) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/cardbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/cardbox.py index ee01b08..377b9b9 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/cardbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/cardbox.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # Copyright (C) 2018 jens persson +# Copyright (C) 2023 Manuel Lohoff # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from boxes import edges, Boxes +from boxes import BoolArg, Boxes, edges class InsetEdgeSettings(edges.Settings): @@ -42,13 +42,14 @@ class FingerHoleEdgeSettings(edges.Settings): """Settings for FingerHoleEdge""" absolute_params = { "wallheight": 0, + "fingerholedepth": 0, } class FingerHoleEdge(edges.BaseEdge): """An edge with room to get your fingers around cards""" def __call__(self, length, **kw): - depth = self.settings.wallheight-self.thickness-10 + depth = self.settings.fingerholedepth-10 self.edge(length/2-10, tabs=2) self.corner(90) self.edge(depth, tabs=2) @@ -59,107 +60,191 @@ class FingerHoleEdge(edges.BaseEdge): class CardBox(Boxes): - """Box for storage of playing cards""" + """Box for storage of playing cards, with versatile options""" ui_group = "Box" description = """ -#### Building instructions +### Description +Versatile Box for Storage of playing cards. Multiple different styles of storage are supported, e.g. a flat storage or a trading card deck box style storage. See images for ideas. +#### Building instructions Place inner walls on floor first (if any). Then add the outer walls. Glue the two walls without finger joins to the inside of the side walls. Make sure there is no squeeze out on top, as this is going to form the rail for the lid. -Add the top of the rails to the sides and the grip rail to the lid. - +Add the top of the rails to the sides (front open) or to the back and front (right side open) and the grip rail to the lid. Details of the lid and rails - ![Details](static/samples/CardBox-detail.jpg) - Whole box (early version still missing grip rail on the lid): """ - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) + self.addSettingsArgs(edges.FingerJointSettings) - self.buildArgParser(h=30) + self.buildArgParser(y=68, h=92, outside=False, sx="65*4") self.argparser.add_argument( - "--cardwidth", action="store", type=float, default=65, - help="Width of the cards") + "--openingdirection", action="store", type=str, default="front", + choices=['front', 'right'], + help="Direction in which the lid slides open. Lid length > Lid width recommended.") self.argparser.add_argument( - "--cardheight", action="store", type=float, default=90, - help="Height of the cards") + "--fingerhole", action="store", type=str, default="regular", + choices=['regular', 'deep', 'custom'], + help="Depth of cutout to grab the cards") self.argparser.add_argument( - "--num", action="store", type=int, default=2, - help="number of compartments") + "--fingerhole_depth", action="store", type=float, default=20, + help="Depth of cutout if fingerhole is set to 'custom'. Disabled otherwise.") + self.argparser.add_argument( + "--add_lidtopper", action="store", type=BoolArg(), default=False, + help="Add an additional lid topper for optical reasons and customisation" + ) @property + def fingerholedepth(self): + if self.fingerhole == 'custom': + return self.fingerhole_depth + elif self.fingerhole == 'regular': + a = self.h/4 + if a < 35: + return a + else: + return 35 + elif self.fingerhole == 'deep': + return self.h-self.thickness-10 + + #inner dimensions of surrounding box (disregarding inlays) + @property + def boxhight(self): + if self.outside: + return self.h - 3 * self.thickness + return self.h + @property def boxwidth(self): - return self.num * (self.cardwidth + self.thickness) + self.thickness + return (len(self.sx) + 1) * self.thickness + sum(self.sx) + @property + def boxdepth(self): + if self.outside: + return self.y - 2 * self.thickness + if self.openingdirection == 'right': + return self.y + 2 * self.thickness + return self.y def divider_bottom(self): t = self.thickness - c = self.cardwidth - y = self.cardheight + sx = self.sx + y = self.boxdepth - for i in range(1, self.num): - self.fingerHolesAt(0.5*t + (c+t)*i, 0, y, 90) + pos = 0.5 * t + for i in sx[:-1]: + pos += i + t + self.fingerHolesAt(pos, 0, y, 90) def divider_back_and_front(self): t = self.thickness - c = self.cardwidth - y = self.h - for i in range(1, self.num): - self.fingerHolesAt(0.5*t + (c+t)*i, 0, y, 90) + sx = self.sx + y = self.boxhight + + pos = 0.5 * t + for i in sx[:-1]: + pos += i + t + self.fingerHolesAt(pos, 0, y, 90) def render(self): - h = self.h t = self.thickness + h = self.boxhight x = self.boxwidth - y = self.cardheight + y = self.boxdepth + sx = self.sx s = InsetEdgeSettings(thickness=t) p = InsetEdge(self, s) p.char = "a" self.addPart(p) - s = FingerHoleEdgeSettings(thickness=t, wallheight=h) + s = FingerHoleEdgeSettings(thickness=t, wallheight=h, fingerholedepth=self.fingerholedepth) p = FingerHoleEdge(self, s) p.char = "A" self.addPart(p) - with self.saved_context(): - self.rectangularWall(x-t*.2, y, "eeFe", move="right", label="Lid") - self.rectangularWall(x, y, "ffff", callback=[self.divider_bottom], - move="right", label="Bottom") - self.rectangularWall(x, y, "eEEE", move="up only") - self.rectangularWall(x-t*.2, t, "fEeE", move="up", label="Lid Lip") + if self.openingdirection == 'right': + with self.saved_context(): + self.rectangularWall(x, y-t*.2, "eFee", move="right", label="Lid") + self.rectangularWall(x, y, "ffff", callback=[self.divider_bottom], + move="right", label="Bottom") + self.rectangularWall(x, y, "eEEE", move="up only") + self.rectangularWall(x, t, "feee", move="up", label="Lip Front") + self.rectangularWall(x, t, "eefe", move="up", label="Lip Back") - with self.saved_context(): - self.rectangularWall(x, h+t, "FFEF", + with self.saved_context(): + self.rectangularWall(x, h+t, "FfFf", callback=[self.divider_back_and_front], move="right", label="Back") - self.rectangularWall(x, h+t, "FFaF", + self.rectangularWall(x, h+t, "FfFf", callback=[self.divider_back_and_front], - move="right", + move="right", label="Front") - self.rectangularWall(x, h+t, "EEEE", move="up only") + self.rectangularWall(x, h+t, "EEEE", move="up only") + with self.saved_context(): + self.rectangularWall(y, h+t, "FFEF", move="right", label="Outer Side Left") + self.rectangularWall(y, h+t, "FFaF", move="right", label="Outer Side Right") + self.rectangularWall(y, h+t, "fFfF", move="up only") - with self.saved_context(): - self.rectangularWall(y, h+t, "FfFf", move="right", label="Outer Side Left") - self.rectangularWall(y, h+t, "FfFf", move="right", label="Outer Side Right") - self.rectangularWall(y, h+t, "fFfF", move="up only") + with self.saved_context(): + self.rectangularWall(y, h, "Aeee", move="right", label="Inner Side Left") + self.rectangularWall(y, h, "Aeee", move="right", label="Inner Side Right") + self.rectangularWall(y, h, "eAee", move="up only") - with self.saved_context(): - self.rectangularWall(y, h, "Aeee", move="right", label="Inner Side Left") - self.rectangularWall(y, h, "Aeee", move="right", label="Inner Side Right") - self.rectangularWall(y, h, "eAee", move="up only") + with self.saved_context(): + self.rectangularWall(y-t*.2, t, "fEeE", move="right", label="Lid Lip") + self.rectangularWall(y, t*2, "efee", move="up only") - with self.saved_context(): - self.rectangularWall(y, t, "eefe", move="right", label="Lip Left") - self.rectangularWall(y, t, "feee", move="right", label="Lip Right") - self.rectangularWall(y, t*2, "efee", move="up only") + for i in range(len(sx) - 1): + self.rectangularWall(h, y, "fAff", move="right", label="Divider") - for i in range(self.num - 1): - self.rectangularWall(h, y, "fAff", move="right", label="Divider") + for c in sx: + self.rectangularWall(c, h, "eeee", move="right", label="Front inlay") + self.rectangularWall(c, h, "eeee", move="right", label="Back inlay") + if self.add_lidtopper: + self.rectangularWall(x, y - 2*t, "eeee", move="right", label="Lid topper") + + elif self.openingdirection == 'front': + with self.saved_context(): + self.rectangularWall(x - t * .2, y, "eeFe", move="right", label="Lid") + self.rectangularWall(x, y, "ffff", callback=[self.divider_bottom], + move="right", label="Bottom") + self.rectangularWall(x, y, "eEEE", move="up only") + self.rectangularWall(x - t * .2, t, "fEeE", move="up", label="Lid Lip") + + with self.saved_context(): + self.rectangularWall(x, h + t, "FFEF", + callback=[self.divider_back_and_front], + move="right", + label="Back") + self.rectangularWall(x, h + t, "FFaF", + callback=[self.divider_back_and_front], + move="right", + label="Front") + self.rectangularWall(x, h + t, "EEEE", move="up only") + + with self.saved_context(): + self.rectangularWall(y, h + t, "FfFf", move="right", label="Outer Side Left") + self.rectangularWall(y, h + t, "FfFf", move="right", label="Outer Side Right") + self.rectangularWall(y, h + t, "fFfF", move="up only") + + with self.saved_context(): + self.rectangularWall(y, h, "Aeee", move="right", label="Inner Side Left") + self.rectangularWall(y, h, "Aeee", move="right", label="Inner Side Right") + self.rectangularWall(y, h, "eAee", move="up only") + + with self.saved_context(): + self.rectangularWall(y, t, "eefe", move="right", label="Lip Left") + self.rectangularWall(y, t, "feee", move="right", label="Lip Right") + self.rectangularWall(y, t * 2, "efee", move="up only") + + for i in range(len(sx) - 1): + self.rectangularWall(h, y, "fAff", move="right", label="Divider") + + if self.add_lidtopper: + self.rectangularWall(x, y - 2 * t, "eeee", move="right", label="Lid topper (optional)") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/cardholder.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/cardholder.py index 1fa6caf..bcefe39 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/cardholder.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/cardholder.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2021 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -22,9 +21,10 @@ class CardHolder(Boxes): ui_group = "Shelf" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) + self.addSettingsArgs(edges.StackableSettings) self.addSettingsArgs(edges.GroovedSettings) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=1.0) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/castle.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/castle.py index a91dfd9..61d40db 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/castle.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/castle.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -18,13 +17,13 @@ from boxes import * class Castle(Boxes): - "Castle tower with two walls" + """Castle tower with two walls""" description = """This was done as a table decoration. May be at some point in the future someone will create a proper castle with towers and gates and walls that can be attached in multiple configurations.""" ui_group = "Unstable" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) @@ -43,6 +42,3 @@ with towers and gates and walls that can be attached in multiple configurations. self.rectangularWall(w1_x, w1_h, "efpe", move="right") self.rectangularWall(w2_x, w2_h, "efpe", move="right") - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/closedbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/closedbox.py index 4dced41..95dd6bc 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/closedbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/closedbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -28,7 +27,7 @@ plate. See BasedBox for variant with a base.""" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.buildArgParser("x", "y", "h", "outside") @@ -56,6 +55,3 @@ See BasedBox for variant with a base.""" self.rectangularWall(x, y, "ffff", bedBolts=[d2, d3, d2, d3], move="right", label="Top") self.rectangularWall(x, y, "ffff", bedBolts=[d2, d3, d2, d3], label="Bottom") - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/coffeecapsulesholder.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/coffeecapsulesholder.py index bc9fc09..b9b5617 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/coffeecapsulesholder.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/coffeecapsulesholder.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- # Copyright (C) 2021 Guillaume Collic # # This program is free software: you can redistribute it and/or modify @@ -16,6 +14,7 @@ # along with this program. If not, see . import math + from boxes import Boxes, boolarg @@ -27,10 +26,10 @@ class CoffeeCapsuleHolder(Boxes): ui_group = "Misc" description = """ - You can store your coffee capsule near your expresso machine with this. It works both vertically, or upside down under a shelf. + You can store your coffee capsule near your espresso machine with this. It works both vertically, or upside down under a shelf. """ - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.argparser.add_argument( "--columns", diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/coinbanksafe.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/coinbanksafe.py new file mode 100644 index 0000000..8124689 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/coinbanksafe.py @@ -0,0 +1,223 @@ +# Copyright (C) 2024 Oliver Jensen +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class CoinBankSafe(Boxes): + """A piggy-bank designed to look like a safe.""" + + description =''' +Make sure not to discard the circle cutouts from the lid, base, and door. They are all needed. + +![Closed](static/samples/CoinBankSafe-closed.jpg) + +![Open](static/samples/CoinBankSafe-open.jpg) + +Assemble the locking pins like this: wiggle-disc, number-disc, doorhole-disc, spacer-disc, D-disc. +Glue the first three in place, but do not glue the last two. +Leaving them unglued will allow you change the code, and to remove the pin from the door. + +![Pins](static/samples/CoinBankSafe-pins.jpg) + +''' + + ui_group = "Misc" + + def __init__(self) -> None: + Boxes.__init__(self) + self.addSettingsArgs(edges.FingerJointSettings) + self.buildArgParser("x", "y", "h") + self.argparser.add_argument( + "--slotlength", action="store", type=float, default=30, + help="Length of the coin slot in mm") + self.argparser.add_argument( + "--slotwidth", action="store", type=float, default=4, + help="Width of the coin slot in mm") + self.argparser.add_argument( + "--handlelength", action="store", type=float, default=8, + help="Length of handle in multiples of thickness") + self.argparser.add_argument( + "--handleclearance", action="store", type=float, default=1.5, + help="Clearance of handle in multiples of thickness") + + def drawNumbers(self, radius, cover): + fontsize = 0.8 * (radius - cover) + for num in range(8): + angle = num*45 + x = (cover + fontsize *0.4) * math.sin(math.radians(angle)) + y = (cover + fontsize *0.4) * math.cos(math.radians(angle)) + self.text(str(num+1), align="center middle", fontsize=fontsize, angle=-angle, color=[1,0,0], + y=y, x=x) + + def lockPin(self, layers, move=None): + t = self.thickness + cutout_width = t/3 + barb_length = t + base_length = layers * t + base_width = t + total_length = base_length + barb_length + total_width = base_width + cutout_width * 0.5 + cutout_angle = math.degrees(math.atan(cutout_width / base_length)) + cutout_length = math.sqrt(cutout_width**2 + base_length**2) + + #self.rectangularWall(5*t, t) + + if self.move(total_length, total_width, move, True): + return + + self.edge(total_length) + self.corner(90) + self.edge(base_width * 1/3) + self.corner(90) + self.edge(base_length) + self.corner(180+cutout_angle, 0) + self.edge(cutout_length) + self.corner(90-cutout_angle) + self.edge(cutout_width * 1.5) + self.corner(90) + self.edge(barb_length) + self.corner(90) + self.corner(-90, cutout_width * 0.5) + self.edge(base_length - cutout_width * 0.5) + self.corner(90) + self.edge(t) + self.corner(90) + + self.move(total_length, total_width, move) + + + def render(self): + x, y, h = self.x, self.y, self.h + t = self.thickness + + slot_length = self.slotlength + slot_width = self.slotwidth + + handle_length = self.handlelength * t + handle_clearance = self.handleclearance * t + + # lock parameters + big_radius = 2.25 * t + small_radius = 1.4 * t + doorhole_radius = 1.25 * t + spacing = 1 + + # side walls + with self.saved_context(): + self.rectangularWall(x, h, "seFf", move="mirror right") + self.rectangularWall(y, h, "sFFF", move="right") + + # wall with holes for the locking bar + self.rectangularWall( + x, h, "sfFe", ignore_widths=[3,4,7,8], + callback=[lambda: self.fingerHolesAt(2.75*t, 0, h, 90)], + move="mirror right") + + # locking bar + self.moveTo(0, self.edges['s'].spacing() + t) + self.rectangularWall(1.33*t, h, "eeef", move="right") + # door + door_clearance = .1 * t # amount to shave off of the door width so it can open + before_hinge = 1.25 * t - door_clearance + after_hinge = y - 2.25 * t - door_clearance + self.moveTo(self.spacing/2, -t) + self.polyline( + after_hinge, -90, t, 90, t, 90, t, -90, before_hinge, 90, + h, 90, + before_hinge, -90, t, 90, t, 90, t, -90, after_hinge, 90, + h, 90) + num_dials = 3 + space_under_dials = 6*big_radius + space_not_under_dials = h - space_under_dials + dial_spacing = space_not_under_dials / (num_dials + 1) + if dial_spacing < 1 : + min_height = 6*big_radius + 4 + raise ValueError(f"With thickness {t}, h must be at least {min_height} to fit the dials.") + + for pos_y in (h/2, + h/2 - (2*big_radius + dial_spacing), + h/2 + (2*big_radius + dial_spacing)): + self.hole(3*t - door_clearance, pos_y, doorhole_radius) + self.rectangularHole(3*t - door_clearance, pos_y, t, t) + self.rectangularHole(y/2 - door_clearance, h/2, t, handle_length / 2) + + self.rectangularWall(x, h, "seff", move="up only") + + # top + self.rectangularWall( + y, x, "efff", callback=[ + lambda: self.rectangularHole(y/2, x/2, slot_length, slot_width), + lambda: (self.hole(1.75*t, 1.75*t, 1.15*t), + self.rectangularHole(1.75*t, 1.75*t, t, t))], + label="top", move="right") + + # bottom + self.rectangularWall( + y, x, "efff", callback=[ + lambda: (self.hole(1.75*t, 1.75*t, 1.15*t), + self.rectangularHole(1.75*t, 1.75*t, t, t))], + label="bottom", move="right") + + def holeCB(): + self.rectangularHole(0, 0, t, t) + self.moveTo(0, 0, 45) + self.rectangularHole(0, 0, t, t) + + # locks + with self.saved_context(): + self.partsMatrix(3, 1, "right", self.parts.disc, 2*big_radius, + callback=lambda: (self.drawNumbers(big_radius, small_radius), self.rectangularHole(0, 0, t, t))) + self.partsMatrix(3, 1, "right", self.parts.disc, 2*big_radius, + dwidth=0.8,callback=holeCB) + self.partsMatrix( + 3, 1, "right", self.parts.disc, 2*small_radius, + callback=lambda:self.rectangularHole(0, 0, t, t)) + self.partsMatrix( + 3, 1, "right", self.parts.wavyKnob, 2*small_radius, + callback=lambda:self.rectangularHole(0, 0, t, t)) + + self.partsMatrix(3, 1, "up only", self.parts.disc, 2*big_radius) + + # lock pins + with self.saved_context(): + self.lockPin(5, move="up") + self.lockPin(5, move="up") + self.lockPin(5, move="up") + self.lockPin(5, move="right only") + + # handle + self.moveTo(0) + handle_curve_radius = 0.2 * t + self.moveTo(t * 2.5) + self.polyline( + 0, + (90, handle_curve_radius), + handle_length - 2 * handle_curve_radius, + (90, handle_curve_radius), + handle_clearance - handle_curve_radius, + 90, + handle_length / 4, + -90, + t, + 90, + handle_length / 2, + 90, + t, + -90, + handle_length / 4, + 90, + handle_clearance - handle_curve_radius, + ) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/coindisplay.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/coindisplay.py index d76eec8..b98190d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/coindisplay.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/coindisplay.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,6 +15,7 @@ from boxes import * + class CoinHolderSideEdge(edges.BaseEdge): char = "B" def __call__(self, length, **kw): @@ -33,7 +33,7 @@ class CoinHolderSideEdge(edges.BaseEdge): self.polyline(0, 90+a, a_l2, -90) - def margin(self): + def margin(self) -> float: return self.settings.coin_plate_x class CoinDisplay(Boxes): @@ -41,7 +41,7 @@ class CoinDisplay(Boxes): ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) @@ -112,4 +112,3 @@ class CoinDisplay(Boxes): self.rectangularWall(self.coin_plate, self.coin_plate, "efef", move="down", label="Coin Plate", callback=[self.coinCutout]) self.rectangularWall(self.coin_plate, self.coin_showcase_h, "fFeF", move="down", label="CoinSide3") - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/concaveknob.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/concaveknob.py index 9e7b0b9..19cbe8d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/concaveknob.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/concaveknob.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2017 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,13 @@ from boxes import * + class ConcaveKnob(Boxes): """Round knob serrated outside for better gripping""" - + ui_group = "Part" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) # Add non default cli params if needed (see argparse std lib) @@ -36,7 +36,7 @@ class ConcaveKnob(Boxes): help="Amount of circumference used for non convex parts") self.argparser.add_argument( "--angle", action="store", type=float, default=70., - help="Angle between convex and concave parts") + help="Angle between convex and concave parts") self.argparser.add_argument( "--bolthole", action="store", type=float, default=6., help="Diameter of the bolt hole (mm)") @@ -61,5 +61,3 @@ class ConcaveKnob(Boxes): move="right") self.parts.concaveKnob(self.diameter, self.serrations, self.rounded, self.angle) - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/console.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/console.py index 83a835d..09105ee 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/console.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/console.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,24 @@ from boxes import * + class Console(Boxes): """Console with slanted panel""" ui_group = "Box" - def __init__(self): + description = """ + +Console Arcade Stick + +![Front](static/samples/ConsoleArcadeStickFront.jpg) +![Back](static/samples/ConsoleArcadeStickBack.jpg) +![Inside](static/samples/ConsoleArcadeStickInside.jpg) + +Keyboard enclosure: +""" + + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=.5) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/console2.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/console2.py index 772e2d8..300a85c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/console2.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/console2.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2020 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,6 +15,7 @@ from boxes import * + class Console2(Boxes): """Console with slanted panel and service hatches""" @@ -25,7 +25,7 @@ class Console2(Boxes): This box is designed as a housing for electronic projects. It has hatches that can be re-opened with simple tools. It intentionally cannot be opened with bare hands - if build with thin enough material. #### Caution -There is a chance that the latches of the back wall or the back wall itself interfere with the front panel or it's mounting frame/lips. The generator does not check for this. So depending on the variant choosen you might need to make the box deeper (increase y parameter) or the panel angle steeper (increase angle parameter) until there is enough room. +There is a chance that the latches of the back wall or the back wall itself interfere with the front panel or it's mounting frame/lips. The generator does not check for this. So depending on the variant chosen you might need to make the box deeper (increase y parameter) or the panel angle steeper (increase angle parameter) until there is enough room. It's also possible that the frame of the panel interferes with the floor if the hi parameter is too small. @@ -40,7 +40,7 @@ If the panel is removable you need to add the springs with the tabs to the side ![Back wall details](static/samples/Console2-panel-detail.jpg) -If space is tight you may consider not glueing the cross pieces in place and remove them after the glue-up. This may prevent the latches of the back wall and the panel from interfereing with each other. +If space is tight you may consider not gluing the cross pieces in place and remove them after the glue-up. This may prevent the latches of the back wall and the panel from interfering with each other. The variant using finger joints only has the two side lips without the cross bars. @@ -51,7 +51,7 @@ The latches at the back wall lock in place when closed. To open them they need t To remove the panel you have to press in the four tabs at the side. It is easiest to push them in and then pull the panel up a little bit so the tabs stay in. """ - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=.5) @@ -99,7 +99,7 @@ To remove the panel you have to press in the four tabs at the side. It is easies if self.move(tw, th, move, True): return - + self.moveTo(0, 1.2*t) self.polyline(t, -90, .2*t, 90, 2*t, -90, t, 90, t, 90, t, -90, 3*t, 90, t, -90, t, 90, t, 90, 2*t, 90, 0.5*t, @@ -194,7 +194,7 @@ To remove the panel you have to press in the four tabs at the side. It is easies t = self.thickness bottom = self.edges.get(bottom, bottom) - + tw = borders[0] + 2* self.edges["f"].spacing() th = borders[-2] + bottom.spacing() + self.edges["f"].spacing() if self.move(tw, th, move, True): @@ -202,7 +202,7 @@ To remove the panel you have to press in the four tabs at the side. It is easies d1 = t * math.cos(math.radians(self.angle)) d2 = t * math.sin(math.radians(self.angle)) - + self.moveTo(t, 0) bottom(borders[0]) self.corner(90) @@ -229,9 +229,9 @@ To remove the panel you have to press in the four tabs at the side. It is easies else: self.edges["f"](borders[-2]+bottom.startwidth()) self.corner(borders[-1]) - + self.move(tw, th, move, label=label) - + def render(self): x, y, h = self.x, self.y, self.h t = self.thickness diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/desksign.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/desksign.py new file mode 100644 index 0000000..44c4395 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/desksign.py @@ -0,0 +1,86 @@ +# Copyright (C) 2013-2014 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class Desksign(Boxes): + """Simple diagonal plate with stands to show name or message.""" + + description = """Text to be engraved can be generated by inputting the label and fontsize fields. + height represents the area that can be used for writing text, does not match the actual + height when standing. Generated text is put in the center. Currently only a single + line of text is supported.""" + + ui_group = "Misc" + + def __init__(self) -> None: + Boxes.__init__(self) + self.addSettingsArgs(edges.FingerJointSettings) + self.addSettingsArgs(edges.StackableSettings, width=2.0) # used for feet + + self.argparser.add_argument( + "--width", action="store", type=float, default=150, + help="plate width in mm (excluding holes)") + self.argparser.add_argument( + "--height", action="store", type=float, default=80, + help="plate height in mm") + self.argparser.add_argument( + "--angle", action="store", type=float, default=60, + help="plate angle in degrees (90 is vertical)") + self.argparser.add_argument( + "--label", action="store", type=str, default="", + help="optional text to engrave (leave blank to omit)") + self.argparser.add_argument( + "--fontsize", action="store", type=float, default=20, + help="height of text") + self.argparser.add_argument( + "--feet", action="store", type=boolarg, default=False, + help="add raised feet") + self.argparser.add_argument( + "--mirror", action="store", type=boolarg, default=True, + help="mirrors one of the stand so the same side of the material can be placed on the outside") + + def render(self): + width = self.width + height = self.height + angle = self.angle + feet = self.feet + mirror = self.mirror + t = self.thickness + + if not (0 < angle and angle < 90): + raise ValueError("angle has to between 0 and 90 degrees") + + base = math.cos(math.radians(angle)) * height + h = math.sin(math.radians(angle)) * height + + label = self.label + fontsize = self.fontsize + + if label and fontsize: + self.rectangularWall(width, height, "eheh", move="right", callback=[ + lambda: self.text("%s" % label, width/2, (height-fontsize)/2, + fontsize = fontsize, align="center", color=Color.ETCHING)]) # add text + else: + self.rectangularWall(width, height, "eheh", move="right") # front + + # stands at back/side + edge = "šef" if feet else "eef" + if mirror: + self.rectangularTriangle(base, h, edge, num=1, move="right") + self.rectangularTriangle(base, h, edge, num=1, move="mirror right") + else: + self.rectangularTriangle(base, h, edge, num=2, move="right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dicebox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dicebox.py index e80b2b9..e646011 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dicebox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dicebox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2022 Erik Snider (SniderThanYou@gmail.com) # # This program is free software: you can redistribute it and/or modify @@ -22,7 +21,7 @@ class DiceBox(Boxes): ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs( edges.FingerJointSettings, diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dinrailbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dinrailbox.py index 79b0905..ed9b2b3 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dinrailbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dinrailbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2020 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -19,12 +18,12 @@ from boxes import * class DinRailEdge(edges.FingerHoleEdge): - def __init__(self, boxes, settings, width=35.0, offset=0.0): + def __init__(self, boxes, settings, width=35.0, offset=0.0) -> None: super().__init__(boxes, settings) self.width = width self.offset = offset - def startwidth(self): + def startwidth(self) -> float: return 8 + self.settings.thickness def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw): @@ -40,7 +39,7 @@ class DinRailEdge(edges.FingerHoleEdge): w+0.25, -90, 1, 30, 5*2*3**-.5, 60, (l-w)/2+o-3.25) - + class DinRailBox(Boxes): """Box for DIN rail used in electrical junction boxes""" @@ -61,8 +60,8 @@ class DinRailBox(Boxes): 4+1.25, 90, 4.5, -90, t+4, -90, 2, 90, l-.8*t-9, 90, 2, -90, 5+t, 90, 4, 90) self.move(tw, th, move) - - def __init__(self): + + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=.8) @@ -114,7 +113,7 @@ class DinRailBox(Boxes): lambda:self.fingerHolesAt(.55*t, .05*t, y-.1*t, 90), None, lambda:self.fingerHolesAt(.55*t, .05*t, y-.1*t, 90), None], move="right", label="Lid") - + self.lid_lip(y-.1*t, move="rotated right") self.lid_lip(y-.1*t, move="rotated right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/discrack.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/discrack.py index 2d73a1c..f40adc1 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/discrack.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/discrack.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# coding: utf-8 # Copyright (C) 2019 chrysn # # This program is free software: you can redistribute it and/or modify @@ -15,10 +13,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from __future__ import division, unicode_literals +from math import cos, pi, sin, sqrt from boxes import * -from math import sqrt, pi, sin, cos + def offset_radius_in_square(squareside, angle, outset): """From the centre of a square, rotate by an angle relative to the @@ -34,7 +32,7 @@ def offset_radius_in_square(squareside, angle, outset): 10.0 >>> offset_radius_in_square(20, 0, 5) 10.0 - >>> # Without offset, it's half squre length divided by cos(angle) -- at + >>> # Without offset, it's half square length divided by cos(angle) -- at >>> # least before it hits the next wall >>> offset_radius_in_square(20, 15, 0) # doctest:+ELLIPSIS 10.35276... @@ -79,7 +77,7 @@ class DiscRack(Boxes): ui_group = "Shelf" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser(sx="20*10") @@ -166,21 +164,15 @@ class DiscRack(Boxes): def word_thickness(length): if length > 0: - return "very thin (%.2g mm at a thickness of %.2g mm)" % ( - length, self.thickness) + return f"very thin ({length:.2g} mm at a thickness of {self.thickness:.2g} mm)" if length < 0: return "absent" if self.rear_outset < self.thickness: - warnings.append("Rear upper constraint is %s. Consider increasing" - " the disc outset parameter, or move the angle away from 45°." - % word_thickness(self.rear_outset) - ) + warnings.append("Rear upper constraint is %s. Consider increasing the disc outset parameter, or move the angle away from 45°." % word_thickness(self.rear_outset)) if self.lower_outset < self.thickness: - warnings.append("Lower front constraint is %s. Consider increasing" - " the disc outset parameter, or move the angle away from 45°." - % word_thickness(self.lower_outset)) + warnings.append("Lower front constraint is %s. Consider increasing the disc outset parameter, or move the angle away from 45°." % word_thickness(self.lower_outset)) # Are the discs supported where the grids meet? @@ -189,26 +181,23 @@ class DiscRack(Boxes): inner_reardistance = r * self.lower_factor - self.rear_halfslit if inner_lowerdistance < 0 or inner_reardistance < 0: - warnings.append("Corner is inside the disc radios, discs would not" - " be supported. Consider increasing the factor parameters.") + warnings.append("Corner is inside the disc radios, discs would not be supported. Consider increasing the factor parameters.") # Won't the type-H edge on the rear side make the whole contraption # wiggle? max_slitlengthplush = offset_radius_in_square( self.outer, self.angle, r * self.rear_factor + self.thickness) - slitlengthplush = self.rear_halfslit + self.thickness * ( 1 + \ + slitlengthplush = self.rear_halfslit + self.thickness * ( 1 + self.edgesettings['FingerJoint']['edge_width']) if slitlengthplush > max_slitlengthplush: - warnings.append("Joint would protrude from lower box edge. Consider" - " increasing the the disc outset parameter, or move the" - " angle away from 45°.") + warnings.append("Joint would protrude from lower box edge. Consider increasing the the disc outset parameter, or move the angle away from 45°.") # Can the discs be removed at all? # Does not need explicit checking, for Thales' theorem tells us that at - # the point wher there is barely support in the corner, three contact - # points on the circle form just a demicircle and the discs can be + # the point where there is barely support in the corner, three contact + # points on the circle form just a semicircle and the discs can be # inserted/removed. When we keep the other contact points and move the # slits away from the corner, the disc gets smaller and thus will fit # through the opening that is as wide as the diameter of the largest diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dispenser.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dispenser.py index 4091fa7..c624b04 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dispenser.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dispenser.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,6 +15,7 @@ from boxes import * + class FrontEdge(edges.BaseEdge): """An edge with room to get your fingers around cards""" def __call__(self, length, **kw): @@ -38,10 +38,10 @@ class Dispenser(Boxes): description = """Set *bottomheight* to 0 for a wall mounting variant. Please add mounting holes yourself.""" - + ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) @@ -81,7 +81,7 @@ Please add mounting holes yourself.""" self.rectangularWall(x, y, "ffff", move="up only") - + if hb: frontedge = edges.CompoundEdge(self, "Ef", (hb+t+hs, h-hs)) self.rectangularWall( @@ -113,4 +113,3 @@ Please add mounting holes yourself.""" label="Left front", move="right") self.rectangularWall(x/3, h-hs, "eee" + se, label="Right front", move="mirror right") - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/display.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/display.py index 686cc2a..3f4e429 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/display.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/display.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,14 +14,14 @@ # along with this program. If not, see . from boxes import * -import math + class Display(Boxes): - """Diplay for flyers or leaflets""" + """Display for flyers or leaflets""" ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser(x=150., h=200.0) @@ -51,4 +50,3 @@ class Display(Boxes): self.polyline(x-2*r, (90-a, r), oh, (90+a, r), x-2*r+2*math.sin(math.radians(a))*oh, (90+a, r), oh, (90-a, r)) - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/displaycase.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/displaycase.py index d185dd6..5ec363d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/displaycase.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/displaycase.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # Copyright (C) 2018 Alexander Bulimov # @@ -23,7 +22,7 @@ class DisplayCase(Boxes): ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.buildArgParser("x", "y", "h", "outside") @@ -58,4 +57,3 @@ class DisplayCase(Boxes): self.flangedWall(x, y, "FFFF", flanges=[self.overhang] * 4, move="right", label="Top") self.flangedWall(x, y, "FFFF", flanges=[self.overhang] * 4, label="Bottom") - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/displayshelf.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/displayshelf.py index 411388c..5a9eeba 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/displayshelf.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/displayshelf.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,69 +15,182 @@ from boxes import * -class DisplayShelf(Boxes): # change class name here and below + +class DisplayShelf(Boxes): """Shelf with slanted floors""" - + ui_group = "Shelf" - def __init__(self): + # arguments/properties + num: int + x: float + y: float + h: float + angle: float + thickness: float + radians: float + sl: float + front_wall_height: float + include_back: bool + slope_top: bool + outside: bool + divider_wall_height: float + + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) - self.buildArgParser(x=400, y=100, h=300, outside=True) + self.buildArgParser(sx="400", y=100, h=300, outside=True) self.argparser.add_argument( - "--num", action="store", type=int, default=3, + "--num", action="store", type=int, default=3, help="number of shelves") self.argparser.add_argument( - "--front", action="store", type=float, default=20.0, + "--front_wall_height", action="store", type=float, default=20.0, help="height of front walls") self.argparser.add_argument( - "--angle", action="store", type=float, default=30.0, + "--angle", action="store", type=float, default=30.0, help="angle of floors (negative values for slanting backwards)") + self.argparser.add_argument( + "--include_back", action="store", type=boolarg, default=False, + help="Include panel on the back of the shelf") + self.argparser.add_argument( + "--slope_top", action="store", type=boolarg, default=False, + help="Slope the sides and the top by front wall height") + self.argparser.add_argument( + "--divider_wall_height", action="store", type=float, default=20.0, + help="height of divider walls") - def side(self): - + def generate_finger_holes(self): t = self.thickness - a = math.radians(self.angle) - - hs = (self.sl+t) * math.sin(a) + math.cos(a) * t - + a = self.radians + hs = (self.sl + t) * math.sin(a) + math.cos(a) * t for i in range(self.num): - pos_x = abs(0.5*t*math.sin(a)) - pos_y = hs - math.cos(a)*0.5*t + i * (self.h-abs(hs)) / (self.num - 0.5) + pos_x = abs(0.5 * t * math.sin(a)) + pos_y = hs - math.cos(a) * 0.5 * t + i * (self.h - abs(hs)) / (self.num - 0.5) if a < 0: pos_y += -math.sin(a) * self.sl - self.fingerHolesAt(pos_x, pos_y, self.sl, -self.angle) - pos_x += math.cos(-a) * (self.sl+0.5*t) + math.sin(a)*0.5*t - pos_y += math.sin(-a) * (self.sl+0.5*t) + math.cos(a)*0.5*t - self.fingerHolesAt(pos_x, pos_y, self.front, 90-self.angle) + pos_x += math.cos(-a) * (self.sl + 0.5 * t) + math.sin(a) * 0.5 * t + pos_y += math.sin(-a) * (self.sl + 0.5 * t) + math.cos(a) * 0.5 * t + self.fingerHolesAt(pos_x, pos_y, self.front_wall_height, 90 - self.angle) + + def generate_sloped_sides(self, width, height): + top_segment_height = height / self.num + a = self.radians + + # Maximum size to cut out + vertical_cut = top_segment_height - self.front_wall_height + hypotenuse = vertical_cut / math.sin(a) + horizontal_cut = math.sqrt((hypotenuse ** 2) - (vertical_cut ** 2)) + + if horizontal_cut > width: + # Shrink the cut to keep the full height + horizontal_cut = width - 1 # keep a 1mm edge on the top + vertical_cut = horizontal_cut * math.tan(a) + hypotenuse = math.sqrt((horizontal_cut ** 2) + (vertical_cut ** 2)) + + top = width - horizontal_cut + front = height - vertical_cut + + borders = [width, 90, front, 90 - self.angle, hypotenuse, self.angle, top, 90, height, 90] + edges = 'eeeef' if self.include_back else 'e' + self.polygonWall(borders, edge=edges, callback=[self.generate_finger_holes], move="up", label="left side") + self.polygonWall(borders, edge=edges, callback=[self.generate_finger_holes], move="up", label="right side") + + def generate_rectangular_sides(self, width, height): + edges = "eeee" + if self.include_back: + edges = "eeef" + self.rectangularWall(width, height, edges, callback=[self.generate_finger_holes], move="up", label="left side") + self.rectangularWall(width, height, edges, callback=[self.generate_finger_holes], move="up", label="right side") + + def generate_shelve_finger_holes(self): + t = self.thickness + pos_x = -0.5 * t + for x in self.sx[:-1]: + pos_x += x + t + self.fingerHolesAt(pos_x, 0, self.sl, 90) + + def generate_front_lip_finger_holes(self): + t = self.thickness + height = self.front_wall_height + if self.divider_wall_height < height: + height = self.divider_wall_height + pos_x = -0.5 * t + for x in self.sx[:-1]: + pos_x += x + t + self.fingerHolesAt(pos_x, 0, height, 90) + + def generate_shelves(self): + if self.front_wall_height: + for i in range(self.num): + self.rectangularWall( + self.x, + self.sl, + "ffef", + callback=[self.generate_shelve_finger_holes], + move="up", + label=f"shelf {i + 1}" + ) + self.rectangularWall( + self.x, + self.front_wall_height, + "Ffef", + callback=[self.generate_front_lip_finger_holes], + move="up", + label=f"front lip {i + 1}" + ) + else: + for i in range(self.num): + self.rectangularWall( + self.x, + self.sl, + "Efef", + callback=[self.generate_shelve_finger_holes], + move="up", + label=f"shelf {i + 1}" + ) + + def generate_dividers(self): + edges_ = "feee" + if self.front_wall_height: + edges_ = "ffee" + if self.divider_wall_height > self.front_wall_height: + edges_ = [ + "f", + edges.CompoundEdge(self, "fe", [self.front_wall_height, self.divider_wall_height - self.front_wall_height]), + "e", + "e" + ] + + for i in range(self.num): + for j in range(len(self.sx) -1): + self.rectangularWall(self.sl, self.divider_wall_height, edges_, move="up", label=f"divider {j + 1} for shelf {i + 1}") def render(self): # adjust to the variables you want in the local scope - x, y, h = self.x, self.y, self.h - f = self.front - t = self.thickness + sx, y, h = self.sx, self.y, self.h + front = self.front_wall_height + thickness = self.thickness if self.outside: - x = self.adjustSize(x) - + self.sx = sx = self.adjustSize(sx) + if self.include_back: + self.y = y = self.adjustSize(y, False) - a = math.radians(self.angle) + self.x = x = sum(sx) + thickness * (len(sx) - 1) + self.radians = a = math.radians(self.angle) + self.sl = (y - (thickness * (math.cos(a) + abs(math.sin(a)))) - max(0, math.sin(a) * front)) / math.cos(a) - self.sl = sl = (y - (t * (math.cos(a) + abs(math.sin(a)))) - max(0, math.sin(a) * f)) / math.cos(a) - # render your parts here - self.rectangularWall(y, h, callback=[self.side], move="up") - self.rectangularWall(y, h, callback=[self.side], move="up") - - if f: - for i in range(self.num): - self.rectangularWall(x, sl, "ffef", move="up") - self.rectangularWall(x, f, "Ffef", move="up") + if self.slope_top: + self.generate_sloped_sides(y, h) else: - for i in range(self.num): - self.rectangularWall(x, sl, "Efef", move="up") + self.generate_rectangular_sides(y, h) + self.generate_shelves() + self.generate_dividers() + if self.include_back: + self.rectangularWall(x, h, "eFeF", label="back wall", move="up") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dividertray.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dividertray.py index e60162f..e3d28ec 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dividertray.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/dividertray.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,9 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from functools import partial -from boxes import Boxes, edges, boolarg +from __future__ import annotations + import math +from functools import partial + +from boxes import Boxes, boolarg, edges, lids class NotchSettings(edges.Settings): @@ -78,7 +79,7 @@ Adding '0:' at the start of the sy parameter adds a slot at the very back. Addin There are 4 different sets of dividers rendered: -* With asymetric tabs so the tabs fit on top of each other +* With asymmetric tabs so the tabs fit on top of each other * With tabs of half wall thickness that can go side by side * With tabs of a full wall thickness * One single divider spanning across all columns @@ -88,10 +89,11 @@ You will likely need to cut each of the dividers you want multiple times. ui_group = "Tray" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.addSettingsArgs(edges.HandleEdgeSettings) + self.addSettingsArgs(lids.LidSettings) self.buildArgParser("sx", "sy", "h", "outside") self.addSettingsArgs(SlotSettings) self.addSettingsArgs(NotchSettings) @@ -192,6 +194,8 @@ You will likely need to cut each of the dividers you want multiple times. side_wall_length, self.h, [be, "f", se, "f"], move="up", label="Sidepiece " + str(_ + 1) ) + self.lid(facing_wall_length, side_wall_length) + # Switch to right side of the file self.ctx.restore() self.rectangularWall( @@ -215,7 +219,7 @@ You will likely need to cut each of the dividers you want multiple times. # Dividers divider_height = ( - # h, with angle adjustement + # h, with angle adjustment self.h / math.cos(math.radians(self.Slot_angle)) # removing what exceeds in the width of the divider - self.thickness * math.tan(math.radians(self.Slot_angle)) @@ -227,7 +231,7 @@ You will likely need to cut each of the dividers you want multiple times. first_tab_width=self.thickness if self.left_wall else 0, second_tab_width=self.thickness if self.right_wall else 0 ) - for tabs, asymetric_tabs in [(self.thickness, None), + for tabs, asymmetric_tabs in [(self.thickness, None), (self.thickness / 2, None), (self.thickness, 0.5),]: with self.saved_context(): @@ -238,47 +242,30 @@ You will likely need to cut each of the dividers you want multiple times. "right", first_tab_width=tabs if self.left_wall or i>0 else 0, second_tab_width=tabs if self.right_wall or i<(len(self.sx) - 1) else 0, - asymetric_tabs=asymetric_tabs, + asymmetric_tabs=asymmetric_tabs, ) - if asymetric_tabs: + if asymmetric_tabs: self.moveTo(-tabs, self.spacing) self.generate_divider(self.sx, divider_height, "up only") if self.debug: debug_info = ["Debug"] + debug_info.append(f"Slot_edge_outer_length:{slot_descriptions.total_length() + 2 * self.thickness:.2f}") debug_info.append( - "Slot_edge_outer_length:{0:.2f}".format( - slot_descriptions.total_length() + 2 * self.thickness - ) - ) - debug_info.append( - "Slot_edge_inner_lengths:{0}".format( + "Slot_edge_inner_lengths:{}".format( str.join( "|", [ - "{0:.2f}".format(e.usefull_length()) - for e in slot_descriptions.get_straigth_edges() + f"{e.useful_length():.2f}" + for e in slot_descriptions.get_straight_edges() ], ) ) ) - debug_info.append( - "Face_edge_outer_length:{0:.2f}".format( - facing_wall_length - + self.thickness * sum([self.left_wall, self.right_wall]) - ) - ) - debug_info.append( - "Face_edge_inner_lengths:{0}".format( - str.join("|", ["{0:.2f}".format(e) for e in self.sx]) - ) - ) - debug_info.append("Tray_height:{0:.2f}".format(self.h)) - debug_info.append( - "Content_height:{0:.2f}".format( - self.h / math.cos(math.radians(self.Slot_angle)) - ) - ) + debug_info.append(f"Face_edge_outer_length:{facing_wall_length + self.thickness * sum([self.left_wall, self.right_wall]):.2f}") + debug_info.append("Face_edge_inner_lengths:{}".format(str.join("|", [f"{e:.2f}" for e in self.sx]))) + debug_info.append(f"Tray_height:{self.h:.2f}") + debug_info.append(f"Content_height:{self.h / math.cos(math.radians(self.Slot_angle)):.2f}") self.text(str.join("\n", debug_info), x=5, y=5, align="bottom left") def generate_finger_holes(self, length): @@ -290,7 +277,7 @@ You will likely need to cut each of the dividers you want multiple times. def generate_divider( self, widths, height, move, first_tab_width=0, second_tab_width=0, - asymetric_tabs=None): + asymmetric_tabs=None): total_width = sum(widths) + (len(widths)-1) * self.thickness + first_tab_width + second_tab_width if self.move(total_width, height, move, True): @@ -298,12 +285,12 @@ You will likely need to cut each of the dividers you want multiple times. play = self.Divider_play left_tab_height = right_tab_height = self.Slot_depth - if asymetric_tabs: - left_tab_height = left_tab_height * asymetric_tabs - play - right_tab_height = right_tab_height * (1-asymetric_tabs) + if asymmetric_tabs: + left_tab_height = left_tab_height * asymmetric_tabs - play + right_tab_height = right_tab_height * (1 - asymmetric_tabs) # Upper: first tab width - if asymetric_tabs: + if asymmetric_tabs: self.moveTo(first_tab_width - play) else: self.edge(first_tab_width - play) @@ -352,7 +339,7 @@ You will likely need to cut each of the dividers you want multiple times. right_tab_height, 90 ) - if asymetric_tabs: + if asymmetric_tabs: self.polyline( first_tab_width - play, -90, @@ -365,13 +352,13 @@ You will likely need to cut each of the dividers you want multiple times. class SlottedEdgeDescriptions: - def __init__(self): - self.descriptions = [] + def __init__(self) -> None: + self.descriptions: list[str] = [] - def add(self, description): + def add(self, description: str) -> None: self.descriptions.append(description) - def get_straigth_edges(self): + def get_straight_edges(self): return [x for x in self.descriptions if isinstance(x, StraightEdgeDescription)] def get_last_edge(self): @@ -382,10 +369,10 @@ class SlottedEdgeDescriptions: compensation = actual_length - target_length compensation_ratio = compensation / sum( - [d.asked_length for d in self.get_straigth_edges()] + [d.asked_length for d in self.get_straight_edges()] ) - for edge in self.get_straigth_edges(): + for edge in self.get_straight_edges(): edge.outside_ratio = 1 - compensation_ratio def total_length(self): @@ -399,21 +386,14 @@ class StraightEdgeDescription: round_edge_compensation=0, outside_ratio=1, angle_compensation=0, - ): + ) -> None: self.asked_length = asked_length self.round_edge_compensation = round_edge_compensation self.outside_ratio = outside_ratio self.angle_compensation = angle_compensation - def __repr__(self): - return ( - "StraightEdgeDescription({0}, round_edge_compensation={1}, angle_compensation={2}, outside_ratio={3})" - ).format( - self.asked_length, - self.round_edge_compensation, - self.angle_compensation, - self.outside_ratio, - ) + def __repr__(self) -> str: + return f"StraightEdgeDescription({self.asked_length}, round_edge_compensation={self.round_edge_compensation}, angle_compensation={self.angle_compensation}, outside_ratio={self.outside_ratio})" def tracing_length(self): """ @@ -425,7 +405,7 @@ class StraightEdgeDescription: + self.angle_compensation ) - def usefull_length(self): + def useful_length(self): """ Part of the length which might be used by the content of the tray """ @@ -433,7 +413,7 @@ class StraightEdgeDescription: class Memoizer(dict): - def __init__(self, computation): + def __init__(self, computation) -> None: self.computation = computation def __missing__(self, key): @@ -447,17 +427,15 @@ class SlotDescription: def __init__( self, width, depth=20, angle=0, radius=0, start_radius=None, end_radius=None - ): + ) -> None: self.depth = depth self.width = width - self.start_radius = radius if start_radius == None else start_radius - self.end_radius = radius if end_radius == None else end_radius + self.start_radius = radius if start_radius is None else start_radius + self.end_radius = radius if end_radius is None else end_radius self.angle = angle - def __repr__(self): - return "SlotDescription({0}, depth={1}, angle={2}, start_radius={3}, end_radius={4})".format( - self.width, self.depth, self.angle, self.start_radius, self.end_radius - ) + def __repr__(self) -> str: + return f"SlotDescription({self.width}, depth={self.depth}, angle={self.angle}, start_radius={self.start_radius}, end_radius={self.end_radius})" def _div_by_cos(self): return SlotDescription._div_by_cos_cache[self.angle] @@ -495,14 +473,14 @@ class SlotDescription: def corrected_start_depth(self): """ - Returns the depth of the straigth part of the slot starting side + Returns the depth of the straight part of the slot starting side """ extra_depth = self._depth_angle_correction() return self.depth + max(0, extra_depth) - self.round_edge_start_correction() def corrected_end_depth(self): """ - Returns the depth of the straigth part of the slot ending side + Returns the depth of the straight part of the slot ending side """ extra_depth = self._depth_angle_correction() return self.depth + max(0, -extra_depth) - self.round_edge_end_correction() @@ -555,7 +533,7 @@ class SlotDescriptionsGenerator: # Add this slot descriptions.add(slot) - # Add the straigth edge after this slot + # Add the straight edge after this slot descriptions.add( StraightEdgeDescription(l, slot.round_edge_end_correction()) ) @@ -573,7 +551,7 @@ class DividerNotchesEdge(edges.BaseEdge): description = "Edge with multiple notches for easier access to dividers" - def __init__(self, boxes, sx): + def __init__(self, boxes, sx) -> None: super().__init__(boxes, None) @@ -618,7 +596,7 @@ class DividerSlotsEdge(edges.BaseEdge): description = "Edge with multiple angled rounded slots for dividers" - def __init__(self, boxes, descriptions): + def __init__(self, boxes, descriptions) -> None: super().__init__(boxes, None) @@ -634,7 +612,7 @@ class DividerSlotsEdge(edges.BaseEdge): elif isinstance(description, StraightEdgeDescription): self.do_straight_edge(description) - # rounding errors might accumulates : + # rounding errors might accumulate : # restore context and redo the move straight self.ctx.restore() self.moveTo(length) @@ -656,7 +634,7 @@ class DividerSlotsEdge(edges.BaseEdge): (90 + slot.angle, slot.end_radius), ) - # rounding errors might accumulates : + # rounding errors might accumulate : # restore context and redo the move straight self.ctx.restore() self.moveTo(slot.tracing_length()) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/doubleflexdoorbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/doubleflexdoorbox.py index 4d4c07f..8eb3a60 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/doubleflexdoorbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/doubleflexdoorbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,16 +13,17 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import boxes import math +import boxes + class DoubleFlexDoorBox(boxes.Boxes): """Box with two part lid with living hinges and round corners""" ui_group = "FlexBox" - def __init__(self): + def __init__(self) -> None: boxes.Boxes.__init__(self) self.addSettingsArgs(boxes.edges.FingerJointSettings) self.addSettingsArgs(boxes.edges.FlexSettings) @@ -54,7 +54,7 @@ class DoubleFlexDoorBox(boxes.Boxes): self.latch(self.latchsize) self.latch(self.latchsize, reverse=True) self.edge(ll) - + self.corner(90, r) self.cc(callback, 3) self.edges["f"](y - 2 * r) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/drillbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/drillbox.py index 77ec32f..7306d60 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/drillbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/drillbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,15 +13,18 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from boxes import Boxes, edges, Color, ArgparseEdgeType -from boxes.lids import _TopEdge +from boxes import ArgparseEdgeType, Boxes, Color, edges +from boxes.lids import LidSettings, _TopEdge + class DrillBox(_TopEdge): """A parametrized box for drills""" + description = """![Multiple DrillBoxes](static/samples/DrillBoxes.jpg) """ + ui_group = "Tray" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, @@ -30,6 +32,7 @@ class DrillBox(_TopEdge): self.addSettingsArgs(edges.RoundedTriangleEdgeSettings, outset=1) self.addSettingsArgs(edges.StackableSettings) self.addSettingsArgs(edges.MountingSettings) + self.addSettingsArgs(LidSettings) self.argparser.add_argument( "--top_edge", action="store", type=ArgparseEdgeType("eStG"), choices=list("eStG"), @@ -123,3 +126,4 @@ class DrillBox(_TopEdge): x, y, "ffff", callback=[lambda: self.drillholes(description=True)], move="right") + self.lid(x, y, self.top_edge) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/drillstand.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/drillstand.py index 911cf9f..53ecc16 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/drillstand.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/drillstand.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,19 +13,20 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from boxes import * import boxes +from boxes import * + class DrillStand(Boxes): """Box for drills with each compartment of a different height""" - description = """Note: `sh` gives the hight of the rows front to back. It though should have the same number of entries as `sy`. These heights are the one on the left side and increase throughout the row. To have each compartement a bit higher than the previous one the steps in `sh` should be a bit bigger than `extra_height`. + description = """Note: `sh` gives the height of the rows front to back. It though should have the same number of entries as `sy`. These heights are the one on the left side and increase throughout the row. To have each compartment a bit higher than the previous one the steps in `sh` should be a bit bigger than `extra_height`. Assembly: ![Parts](static/samples/DrillStand-drawing.png) -Start with putting the slots of the inner walls together. Be especially careful with adding the bottom. It is always assymetrical and flush with the right/lower side while being a little short on the left/higher side to not protrude into the side wall. +Start with putting the slots of the inner walls together. Be especially careful with adding the bottom. It is always asymmetrical and flush with the right/lower side while being a little short on the left/higher side to not protrude into the side wall. | | | | ---- | ---- | @@ -37,7 +37,7 @@ Start with putting the slots of the inner walls together. Be especially careful ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.StackableSettings, height=1.0, width=3) @@ -50,7 +50,7 @@ Start with putting the slots of the inner walls together. Be especially careful def yWall(self, nr, move=None): t = self.thickness - x, sx, y, sy, sh = self.x, self.sx, self.y, self.sy, self.sh + x, sx, y, sy, sh = self.x, self.sx, self.y, self.sy, self.sh eh = self.extra_height * (sum(sx[:nr])+ nr*t - t)/x tw, th = sum(sy) + t * len(sy) + t, max(sh) + eh @@ -65,12 +65,12 @@ Start with putting the slots of the inner walls together. Be especially careful for i in range(len(sy)-1, 0, -1): s1 = max(sh[i]-sh[i-1], 0) + 4*t s2 = max(sh[i-1]-sh[i], 0) + 4*t - + self.polyline(sy[i], 90, s1, -90, t, -90, s2, 90) self.polyline(sy[0], 90) self.edges["f"](sh[0] + eh) self.corner(90) - + self.move(tw, th, move) def sideWall(self, extra_height=0.0, foot_height=0.0, edges="šFf", move=None): @@ -109,7 +109,7 @@ Start with putting the slots of the inner walls together. Be especially careful self.step(t - edges[2].endwidth()) self.polyline(fh) self.edgeCorner("e", edges[0]) - + self.move(tw, th, move) def xWall(self, nr, move=None): @@ -125,7 +125,7 @@ Start with putting the slots of the inner walls together. Be especially careful if self.move(tw, th, move, True): return - + self.moveTo(t, eh+t, -a) for i in range(len(sx)-1): @@ -147,7 +147,7 @@ Start with putting the slots of the inner walls together. Be especially careful def xOutsideWall(self, h, edges="fFeF", move=None): t = self.thickness x, sx, y, sy, sh = self.x, self.sx, self.y, self.sy, self.sh - + edges = [self.edges.get(e, e) for e in edges] eh = self.extra_height @@ -160,7 +160,7 @@ Start with putting the slots of the inner walls together. Be especially careful if self.move(tw, th, move, True): return - + self.moveTo(edges[3].spacing(), eh+edges[0].margin(), -a) self.edge(t*math.tan(math.radians(a))) @@ -188,7 +188,7 @@ Start with putting the slots of the inner walls together. Be especially careful self.edgeCorner("e", edges[0], 90) self.moveTo(0, self.burn+edges[0].startwidth(), 0) - + for i in range(1, len(sx)): posx = sum(sx[:i]) + i*t - 0.5 * t length = h + self.extra_height * (sum(sx[:i]) + i*t - t)/x @@ -198,7 +198,7 @@ Start with putting the slots of the inner walls together. Be especially careful def bottomCB(self): t = self.thickness - x, sx, y, sy, sh = self.x, self.sx, self.y, self.sy, self.sh + x, sx, y, sy, sh = self.x, self.sx, self.y, self.sy, self.sh eh = self.extra_height a = math.degrees(math.atan(eh / x)) @@ -226,9 +226,8 @@ Start with putting the slots of the inner walls together. Be especially careful self.xOutsideWall(sh[-1], "hfef", move="up") self.rectangularWall(x/math.cos(bottom_angle)-t*math.tan(bottom_angle), y, "fefe", callback=[self.bottomCB], move="up") - + self.sideWall(foot_height=self.extra_height+2*t, move="right") for i in range(1, len(sx)): self.yWall(i, move="right") self.sideWall(self.extra_height, 2*t, move="right") - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/edges.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/edges.py index 527fb23..663873d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/edges.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/edges.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,13 +15,14 @@ from boxes import * + class Edges(Boxes): - """Print all registerd Edge types""" - + """Print all registered Edge types""" + webinterface = False - def __init__(self): - Boxes.__init__(self) + def __init__(self) -> None: + Boxes.__init__(self) def render(self): self.ctx = None @@ -31,4 +31,3 @@ class Edges(Boxes): for c in sorted(chars, key=lambda x:(x.lower(), x.isupper())): print("%s %s - %s" %(c, self.edges[c].__class__.__name__, self.edges[c].__doc__)) - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/electronicsbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/electronicsbox.py index 87e8862..ad4f327 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/electronicsbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/electronicsbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2017 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -22,7 +21,7 @@ class ElectronicsBox(Boxes): ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.buildArgParser("x", "y", "h", "outside") @@ -49,12 +48,12 @@ class ElectronicsBox(Boxes): t = self.thickness self.fingerHolesAt(0, self.h-1.5*t, self.triangle, 0) self.fingerHolesAt(self.x, self.h-1.5*t, self.triangle, 180) - + def wallyCB(self): t = self.thickness self.fingerHolesAt(0, self.h-1.5*t, self.triangle, 0) self.fingerHolesAt(self.y, self.h-1.5*t, self.triangle, 180) - + def render(self): t = self.thickness @@ -64,7 +63,7 @@ class ElectronicsBox(Boxes): hd = self.holedist tr = self.triangle trh = tr / 3. - + if self.outside: self.x = x = self.adjustSize(x) self.y = y = self.adjustSize(y) @@ -74,7 +73,7 @@ class ElectronicsBox(Boxes): move="right", label="Wall 1") self.rectangularWall(y, h, "ffef", callback=[self.wallyCB], move="up", label="Wall 2") - self.rectangularWall(y, h, "ffef", callback=[self.wallyCB], + self.rectangularWall(y, h, "ffef", callback=[self.wallyCB], label="Wall 4") self.rectangularWall(x, h, "fFeF", callback=[self.wallxCB], move="left up", label="Wall 3") @@ -94,6 +93,3 @@ class ElectronicsBox(Boxes): self.rectangularTriangle(tr, tr, "ffe", num=4, callback=[None, lambda: self.hole(trh, trh, d=d1)]) - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/eurorackskiff.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/eurorackskiff.py index 55cc093..c5490b6 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/eurorackskiff.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/eurorackskiff.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2017 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -22,44 +21,40 @@ class EuroRackSkiff(Boxes): ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.buildArgParser("h") self.argparser.add_argument( "--hp", action="store", type=int, default=84, help="Width of the case in HP") - + def wallxCB(self, x): t = self.thickness - + def wallyCB(self, y): t = self.thickness - self.fingerHolesAt(6, self.h-1.5*t, y, 0) + self.fingerHolesAt(0, self.h-1.5*t, y, 0) def railHoles(self): for i in range(0, self.hp): self.hole(i*5.08 + 2.54, 3, d=3.0) - + def render(self): t = self.thickness h = self.h y = self.hp * 5.08 x = 128.5 - - + + self.rectangularWall(y, 6, "feee", callback=[self.railHoles] , move="up") self.rectangularWall(y, 6, "feee", callback=[self.railHoles] , move="up") - self.rectangularWall(x, h, "fFeF", callback=[self.wallxCB(x)], + self.rectangularWall(x, h, "fFeF", callback=[lambda: self.wallxCB(x)], move="right") - self.rectangularWall(y, h, "ffef", callback=[self.wallyCB(y)], move="up") - self.rectangularWall(y, h, "ffef", callback=[self.wallyCB(y)]) - self.rectangularWall(x, h, "fFeF", callback=[self.wallxCB(x)], + self.rectangularWall(y, h, "ffef", callback=[lambda: self.wallyCB(y)], move="up") + self.rectangularWall(y, h, "ffef", callback=[lambda: self.wallyCB(y)]) + self.rectangularWall(x, h, "fFeF", callback=[lambda: self.wallxCB(x)], move="left up") self.rectangularWall(x, y, "FFFF", callback=[], move="right") - - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/fanhole.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/fanhole.py index 38929ab..e8ede93 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/fanhole.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/fanhole.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,13 @@ from boxes import * + class FanHole(Boxes): """Hole pattern for mounting a fan""" ui_group = "Holes" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.argparser.add_argument( @@ -43,7 +43,7 @@ class FanHole(Boxes): "--style", action="store", type=str, default="CW Swirl", choices=["CW Swirl", "CCW Swirl", "Hole"], help="Style of the fan hole") - + def arc(self, d, a): r = abs(1/math.cos(math.radians(90-a/2))*d/2) @@ -60,7 +60,7 @@ class FanHole(Boxes): ai = 90 ao = 360/n * 0.8 # angle going in - a1 = math.degrees(math.atan( + a1 = math.degrees(math.atan( ri*math.sin(math.radians(ai)) / (r - ri*math.cos(math.radians(ai))))) d1= (ri*math.sin(math.radians(ai))**2 + @@ -85,8 +85,8 @@ class FanHole(Boxes): self.corner(-180+a3) self.arc(d1, 85) self.corner(-90-a1) - - self.moveArc(-360./n, r) + + self.moveArc(-360./n, r) def render(self): r_h = self.mounting_holes / 2 diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/filamentspool.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/filamentspool.py new file mode 100644 index 0000000..848c5aa --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/filamentspool.py @@ -0,0 +1,98 @@ +# Copyright (C) 2013-2016 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * +from boxes.generators.bayonetbox import BayonetBox + + +class FilamentSpool(BayonetBox): + """A two part spool for 3D printing filament""" + + description = """ +Use small nails to properly align the pieces of the bayonet latch. Glue the parts of the bayonet latch before assembling the "axle". The inner parts go at the side and the outer parts at the inside of the axle. +![opened spool](static/samples/FilamentSpool-2.jpg)""" + + ui_group = "Misc" + + def __init__(self) -> None: + Boxes.__init__(self) + + self.addSettingsArgs(edges.FingerJointSettings) + + self.buildArgParser(h=48) + self.argparser.add_argument( + "--outer_diameter", action="store", type=float, default=200.0, + help="diameter of the flanges") + self.argparser.add_argument( + "--inner_diameter", action="store", type=float, default=100.0, + help="diameter of the center part") + self.argparser.add_argument( + "--axle_diameter", action="store", type=float, default=50.0, + help="diameter of the axle hole") + self.argparser.add_argument( + "--sides", action="store", type=int, default=8, + help="number of pieces for the center part") + self.argparser.add_argument( + "--alignment_pins", action="store", type=float, default=1.0, + help="diameter of the alignment pins") + + + def leftsideCB(self): + self.hole(0, 0, d=self.axle_diameter) + r, h, side = self.regularPolygon(self.sides, radius=self.inner_diameter/2) + for i in range(self.sides): + self.fingerHolesAt(-side/2, h+0.5*self.thickness, side, 0) + self.moveTo(0, 0, 360/self.sides) + self.outerHolesCB() + + def outerHolesCB(self): + t = self.thickness + for i in range(6): + for j in range(2): + self.rectangularHole( + 0, self.outer_diameter / 2 - 7.0, + self.outer_diameter * math.pi / 360 * 8, 5, r=2.5) + self.moveTo(0, 0, 10) + self.moveTo(0, 0, 360 / 6 - 20) + self.rectangularHole( + (self.outer_diameter + self.inner_diameter) / 4, 0, + (self.outer_diameter - self.inner_diameter) / 2 - 4*t, t, r=t/2) + + + def render(self): + t = self.thickness + + self.inner_diameter -= 2 * t + r, h, side = self.regularPolygon(self.sides, radius=self.inner_diameter/2) + self.diameter = 2*h + self.lugs = self.sides + + self.parts.disc( + self.outer_diameter, callback=self.leftsideCB, move="right") + self.parts.disc( + self.outer_diameter, hole=self.axle_diameter, + callback=lambda:(self.alignmentHoles(True), + self.outerHolesCB()), + move="right") + self.regularPolygonWall( + self.sides, r=self.inner_diameter/2, edges="f", + callback=[self.upperCB], move="right") + self.parts.disc(self.diameter, callback=self.lowerCB, move="right") + + for i in range(self.sides): + self.rectangularWall( + side, self.h - t, "feFe", + callback=[lambda:self.hole(side/2, self.h-2*t, r=t)], + move="right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/filltest.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/filltest.py index 28baed7..8fd29a3 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/filltest.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/filltest.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,17 +13,17 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from boxes import * -from shapely.geometry import * -import random import time +from boxes import * + + class FillTest(Boxes): # Change class name! """Piece for testing different settings for hole filling""" ui_group = "Part" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(fillHolesSettings, fill_pattern="hex") @@ -74,5 +73,3 @@ class FillTest(Boxes): # Change class name! def render(self): self.rectangularWall(self.x, self.y, "eeee", callback=[self.xHoles, None, None, None],) - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbook.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbook.py new file mode 100644 index 0000000..4f4d183 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbook.py @@ -0,0 +1,313 @@ +# Copyright (C) 2024 Oliver Jensen +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class FlexBook(Boxes): + """Box with living hinge styled after a book.""" + + ui_group = "FlexBox" + + description = """ +If you have an enclosure, arrange the living hinge to be as close to your extractor fan as possible. + +![Open](static/samples/FlexBook-2.jpg)""" + + def __init__(self) -> None: + Boxes.__init__(self) + self.addSettingsArgs(edges.FingerJointSettings) + self.addSettingsArgs(edges.FlexSettings) + self.buildArgParser(x=75.0, y=35.0, h=125.0) + self.argparser.add_argument( + "--latchsize", action="store", type=float, default=8, + help="size of latch in multiples of thickness") + self.argparser.add_argument( + "--recess_wall", action="store", type=boolarg, default=False, + help="Whether to recess the inner wall for easier object removal") + + + def flexBookSide(self, h, x, r, callback=None, move=None): + t = self.thickness + + tw, th = h+t, x+2*t+r + if self.move(tw, th, move, True): + return + + self.fingerHolesAt(0, x+1.5*t, h, 0) + + self.edges["F"](h) + self.corner(90, 0) + self.edges["e"](t) + self.edges["f"](x + t) + self.corner(180, r) + self.edges["e"](x + 2*t) + self.corner(90) + + self.move(tw, th, move) + + + def flexBookRecessedWall(self, h, y, include_recess, callback=None, move=None): + t = self.thickness + + tw, th = h, y+2*t + + if self.move(tw, th, move, True): + return + + # TODO: figure out math for gentler angles + cutout_radius = min(h/4, y/8) + cutout_angle = 90 + cutout_predist = y * 0.2 + cutout_angle_dist = h/2 - 2 * cutout_radius + cutout_base_dist = y - (y * .4) - 4 * cutout_radius + + self.moveTo(0, t) + + self.edges["f"](h) + self.corner(90,) + self.edges["e"](y) + self.corner(90) + self.edges["f"](h) + self.corner(90) + if include_recess: + self.polyline( + cutout_predist, + (cutout_angle, cutout_radius), + cutout_angle_dist, + (-cutout_angle, cutout_radius), + cutout_base_dist, + (-cutout_angle, cutout_radius), + cutout_angle_dist, + (cutout_angle, cutout_radius), + cutout_predist) + else : + self.edges["e"](y) + self.corner(90) + + self.move(tw, th, move) + + + def flexBookLatchWall(self, h, y, latchSize, callback=None, move=None): + t = self.thickness + + if self.recess_wall: + x_adjust = 0 + else: + x_adjust = 3 * t + + tw, th = h+t+x_adjust, y+2*t + + if self.move(tw, th, move, True): + return + + self.moveTo(x_adjust, t) + + self.edges["f"](h) + self.corner(90) + self.edges["f"](y) + self.corner(90) + self.edges["f"](h) + self.corner(90) + + self.rectangularHole(y/2, -1.5*t, latchSize - 2*t, t) + + self.polyline( + (y-latchSize) / 2, + -90, + 2.5*t, + (90, t/2), + latchSize - t, + (90, t/2), + 2.5*t, + -90, + (y-latchSize) / 2, + 90 + ) + + self.move(tw, th, move) + + def flexBookCover(self, move=None): + x, y = self.x, self.y + latchSize = self.latchsize + c4 = self.c4 + t = self.thickness + + tw = 2*x + 6*t + 2*c4 + t + th = y + 4*t + + if self.move(tw, th, move, True): + return + + self.moveTo(2*t, 0) + + self.edges["h"](x+t) + self.edges["X"](2*c4 + t, y + 4*t) # extra thickness here to make it fit + self.edges["e"](x+t) + self.corner(90, 2*t) + self.edges["e"](y/2) + + self.rectangularHole(0, 1.5*t, latchSize, t) + self.rectangularHole((latchSize+7*t)/2, 3.5*t, t, t) + self.rectangularHole(-(latchSize+7*t)/2, 3.5*t, t, t) + + self.edges["e"](y/2) + self.corner(90, 2*t) + self.edges["e"](x+t + 2*c4 + t) # corresponding extra thickness + self.edges["h"](x+t) + self.corner(90, 2*t) + self.edges["h"](y) + self.corner(90, 2*t) + + if False: + # debug lines + self.moveTo(0, 2*t) + self.edges["e"](x+t + 2*c4 + x+t + t) + self.corner(90) + self.edges["e"](y) + self.corner(90) + self.edges["e"](x+t + 2*c4 + x+t + t) + self.corner(90) + self.edges["e"](y) + self.corner(90) + + self.edges["e"](x) + self.corner(90) + self.edges["e"](y) + self.corner(90) + self.edges["e"](x) + self.corner(90) + self.edges["e"](y) + self.corner(90) + + self.move(tw, th, move) + + def flexBookLatchBracket(self, isCover, move=None): + t = self.thickness + round = t/3 + + tw, th = 5*t, 5.5*t + if self.move(tw, th, move, True): + return + + if isCover: + self.edge(5*t) + else: + self.edge(t) + self.corner(90) + self.edge(2*t - round) + self.corner(-90, round) + self.edge(1.5*t - round) + self.rectangularHole(0, 1.5 * t, t, t) + self.edge(1.5*t - round) + self.corner(-90, round) + self.edge(2*t - round) + self.corner(90) + self.edge(t) + + self.corner(90) + self.edge(3*t) + self.corner(180, 2.5 * t) + self.edge(3*t) + + if not isCover: + # anchor pin + self.moveTo(-1.5*t, 1.25*t) + self.ctx.stroke() + self.rectangularWall(t, 2*t) + + self.move(tw, th, move) + + def flexBookLatchPin(self, move=None): + t = self.thickness + l = self.latchsize + + tw, th = l + 4*t, 5*t + + if self.move(tw, th, move, True): + return + + round = t/3 + + self.moveTo(2*t, 0) + + self.polyline( + l, + 90, + 2*t, + -90, + 2*t - round, + (90, round), + 2*t - 2*round, + (90, round), + 3*t - round, + -90, + t - round, + (90, round), + l - 2*t - 2*round, + (90, round), + t - round, + -90, + 3*t - round, + (90, round), + 2*t - 2*round, + (90, round), + 2*t - round, + -90, + 2*t, + 90) + self.move(tw, th, move) + + + + + def render(self): + + # I found it easier to conceptualize swapping y and h + y = self.h + self.h = self.y + self.y = y + t = self.thickness + + self.radius = self.h / 2 + self.c4 = c4 = math.pi * self.radius * 0.5 + + self.latchsize *= self.thickness + + self.flexBookCover(move="up") + self.flexBookRecessedWall(self.h, self.y, self.recess_wall, move="mirror right") + self.flexBookLatchWall(self.h, self.y, self.latchsize, move="right") + + with self.saved_context(): + self.flexBookSide(self.h, self.x, self.radius, move="right") + self.flexBookSide(self.h, self.x, self.radius, move="mirror right") + self.flexBookSide(self.h, self.x, self.radius, move="up only") + + with self.saved_context(): + self.flexBookLatchBracket(False, move="up") + self.flexBookLatchBracket(False, move="up") + self.flexBookLatchBracket(False, move="right only") + + with self.saved_context(): + self.flexBookLatchBracket(True, move="up") + self.flexBookLatchBracket(True, move="up") + self.flexBookLatchBracket(False, move="right only") + + l = self.latchsize + self.rectangularWall( + 4*t, l, + callback=[lambda: self.rectangularHole(2*t, l/2, 2.5*t, .8*l, r=2*t)], + move="right") + self.flexBookLatchPin(move="right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox.py index 8da4dc6..e385f5c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,16 +13,17 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import boxes import math +import boxes + class FlexBox(boxes.Boxes): """Box with living hinge and round corners""" ui_group = "FlexBox" - def __init__(self): + def __init__(self) -> None: boxes.Boxes.__init__(self) self.addSettingsArgs(boxes.edges.FingerJointSettings) self.addSettingsArgs(boxes.edges.FlexSettings) @@ -120,6 +120,3 @@ class FlexBox(boxes.Boxes): self.surroundingWall(move="up") self.flexBoxSide(self.x, self.y, self.radius, move="right") self.flexBoxSide(self.x, self.y, self.radius, move="mirror") - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox2.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox2.py index c395142..8729888 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox2.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox2.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,14 +14,14 @@ # along with this program. If not, see . from boxes import * -import math + class FlexBox2(Boxes): """Box with living hinge and top corners rounded""" ui_group = "FlexBox" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.addSettingsArgs(edges.FlexSettings) @@ -117,5 +116,3 @@ class FlexBox2(Boxes): self.flexBoxSide(self.y, self.h, self.radius, move="right") self.flexBoxSide(self.y, self.h, self.radius, move= "mirror right") self.rectangularWall(self.x, self.h - self.radius - self.latchsize, edges="fFeF") - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox3.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox3.py index a882afc..97b143e 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox3.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox3.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,7 +14,6 @@ # along with this program. If not, see . from boxes import * -import math class FlexBox3(Boxes): @@ -23,7 +21,7 @@ class FlexBox3(Boxes): ui_group = "FlexBox" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=1) self.addSettingsArgs(edges.FlexSettings) @@ -163,6 +161,3 @@ class FlexBox3(Boxes): self.flexBoxSide(x, y, r, move="right") self.flexBoxSide(x, y, r, move="mirror right") self.rectangularWall(z, y, edges="fFeF") - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox4.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox4.py index 8652a40..433ee88 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox4.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox4.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2018 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,7 +14,6 @@ # along with this program. If not, see . from boxes import * -import math class FlexBox4(Boxes): @@ -23,7 +21,7 @@ class FlexBox4(Boxes): ui_group = "FlexBox" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.addSettingsArgs(edges.FlexSettings) @@ -82,9 +80,9 @@ class FlexBox4(Boxes): self.edges["X"](c4, h + 2 * self.thickness) self.edge(y - r - self.latchsize) - self.latch(self.latchsize+t, False) + self.latch(self.latchsize, False, extra_length=t) self.edge(h + 2 * self.thickness) - self.latch(self.latchsize+t, False, True) + self.latch(self.latchsize, False, True, extra_length=t) self.edge(y - r - self.latchsize) self.edge(c4) self.edge(x - 2 * r) @@ -115,6 +113,3 @@ class FlexBox4(Boxes): self.flexBoxSide(self.x, self.y, self.radius, move="right") self.flexBoxSide(self.x, self.y, self.radius, move="mirror right") self.rectangularWall(self.x, self.h, edges="FeFF") - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox5.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox5.py index d69c684..f9faf94 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox5.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flexbox5.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,16 +13,17 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import boxes import math +import boxes + class FlexBox5(boxes.Boxes): """Box with living hinge and round corners""" ui_group = "FlexBox" - def __init__(self): + def __init__(self) -> None: boxes.Boxes.__init__(self) self.addSettingsArgs(boxes.edges.FingerJointSettings) self.addSettingsArgs(boxes.edges.FlexSettings) @@ -46,7 +46,7 @@ class FlexBox5(boxes.Boxes): l = self.l tw , th = l+r1+r2, 2*max(r1, r2)+2*t - + if self.move(tw, th, move, True): return @@ -107,7 +107,7 @@ class FlexBox5(boxes.Boxes): self.h = self.adjustSize(self.h) self.top_diameter = self.adjustSize(self.top_diameter) self.bottom_diameter = self.adjustSize(self.bottom_diameter) - + t = self.thickness self.latchsize *= self.thickness d_t, d_b = self.top_diameter, self.bottom_diameter diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flextest.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flextest.py index 77433f8..4e1a718 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flextest.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flextest.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -18,11 +17,11 @@ from boxes import * class FlexTest(Boxes): - "Piece for testing different flex settings" + """Piece for testing different flex settings""" ui_group = "Part" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FlexSettings) self.buildArgParser("x", "y") @@ -41,6 +40,3 @@ class FlexTest(Boxes): self.corner(90) self.edge(y) self.corner(90) - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flextest2.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flextest2.py index 364b7cf..033c049 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flextest2.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/flextest2.py @@ -1,4 +1,3 @@ -#!/usr/bin/python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -18,11 +17,11 @@ from boxes import * class FlexTest2(Boxes): - "Piece for testing 2D flex settings" + """Piece for testing 2D flex settings""" ui_group = "Part" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser("x", "y") self.argparser.add_argument( @@ -33,5 +32,3 @@ class FlexTest2(Boxes): x, y = self.x, self.y self.rectangularWall(x, y, callback=[lambda: self.flex2D(x, y, self.fw)]) - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/folder.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/folder.py index f4898b1..020e482 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/folder.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/folder.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,13 +14,12 @@ # along with this program. If not, see . from boxes import * -import math class Folder(Boxes): """Book cover with flex for the spine""" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FlexSettings) self.buildArgParser("x", "y", "h") @@ -44,6 +42,3 @@ class Folder(Boxes): self.corner(90, r) self.edge(y - 2 * r) self.corner(90, r) - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/frontpanel.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/frontpanel.py new file mode 100644 index 0000000..4b7d58e --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/frontpanel.py @@ -0,0 +1,211 @@ +# Copyright (C) 2013-2017 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import io +import shlex + +from boxes import * + + +def str_to_bool(s: str) -> bool: + return s.lower() in ('true', '1', 't', 'y', 'yes', 'yeah', 'yup', 'certainly', 'uh-huh') + + +class FrontPanel(Boxes): + """Mounting Holes and cutouts for all your holy needs.""" + + description = f""" + + + + +This will help you create font (and side and top) panels for your +boxes that are pre-configured for all the bits and bobs you'd like to +install + + The layout can create several types of holes including rectangles, + circles and mounting holes. The default shows an example layout with all + currently supported objects. + +#### +`rect x y w h [cr=0] [cx=True] [cy=True]` + + x: x position + y: y position + w: width + h: height + cr: optional, Corner radius, default=0 + cx: optional, Center x. the x position denotes the center of the rectangle. + accepts t, T, 1, or other true-like values. + cy: optional, Center y. the y position denotes the center of the rectangle. + +#### outline +`rect w h` + + w: width + h: height + +`outline` has a special meaning: You can create multiple panel outlines with one command. +This has the effect of making it easy to manage all the holes on all the sides of +your boxes. + +#### circle +`circle x y r` + + x: x position + y: y position + r: radius + +#### mountinghole +mountinghole x y d_shaft [d_head=0] [angle=0] + + x: x position + y: y position + d_shaft: diameter of the shaft part of the mounting hole + d_head: optional. diameter of the head + angle: optional. angle of the mounting hole + +#### text +`text x y size "some text" [angle=0] [align=bottom|left]` + + x: x position + y: y position + size: size, in mm + text: text to render. This *must* be in quotation marks + angle: angle (in degrees) + align: string with combinations of (top|middle|bottom) and (left|center|right), + separated by '|'. Default is 'bottom|left' + + + +#### nema +`nema x y size [screwhole_size=0]` + + x: x position (center of shaft) + y: y position (center of shaft) + size: nema size. One of [{', '.join([f'{x}' for x in Boxes.nema_sizes])}] + screw: screw size, in mm. Optional. Default=0, which means the default size + + """ + + ui_group = "Holes" + + def __init__(self) -> None: + Boxes.__init__(self) + self.argparser.add_argument( + "--layout", action="store", type=str, + default=""" +outline 100 100 +rect 50 60 80 30 3 True False +text 50 91 7 "Super Front Panel With Buttons!" 0 bottom|center +circle 10 45 3.5 +circle 30 45 3.5 +circle 50 45 3.5 +circle 70 45 3.5 +circle 90 45 3.5 +text 10 40 3 "BTN_1" 0 top|center +text 35 45 3 "BTN_2" 90 top|center +text 50 50 3 "BTN_3" 180 top|center +text 65 45 3 "BTN_4" 270 top|center +text 90 45 3 "5" 0 middle|center +mountinghole 5 85 3 6 90 +mountinghole 95 85 3 6 90 + +# Start another panel, 30x50 +outline 30 50 +rect 15 25 15 15 1 True True +text 15 25 3 "__Fun!" 0 bottom|left +text 15 25 3 "__Fun!" 45 bottom|left +text 15 25 3 "__Fun!" 90 bottom|left +text 15 25 3 "__Fun!" 135 bottom|left +text 15 25 3 "__Fun!" 180 bottom|left +text 15 25 3 "__Fun!" 225 bottom|left +text 15 25 3 "__Fun!" 270 bottom|left + +text 3 10 2 "Another panel, for fun" 0 top|left + + +# Let's create another panel with a nema motor on it +outline 40 40 +nema 20 20 17 +""") + + def applyOffset(self, x, y): + return (x+self.offset[0], y+self.offset[1]) + + def drawRect(self, x, y, w, h, r=0, center_x="True", center_y="True") -> None: + x, y, w, h, r = (float(i) for i in [x, y, w, h, r]) + x, y = self.applyOffset(x, y) + center_x = str_to_bool(center_x) + center_y = str_to_bool(center_y) + self.rectangularHole(x, y, w, h, r, center_x, center_y) + + def drawCircle(self, x, y, r) -> None: + x, y, r = (float(i) for i in [x, y, r]) + x, y = self.applyOffset(x, y) + self.hole(x, y, r) + + def drawMountingHole(self, x, y, d_shaft, d_head=0.0, angle=0) -> None: + x, y, d_shaft, d_head, angle = (float(i) for i in [x, y, d_shaft, d_head, angle]) + x, y = self.applyOffset(x, y) + self.mountingHole(x, y, d_shaft, d_head, angle) + + def drawOutline(self, w, h): + w, h = (float(i) for i in [w, h]) + if self.outline is not None: + self.offset = self.applyOffset(self.outline[0]+10, 0) + self.outline = (w, h) # store away for next time + x = 0 + y = 0 + x, y = self.applyOffset(x, y) + border = [(x, y), (x+w, y), (x+w, y+h), (x, y+h), (x, y)] + self.showBorderPoly( border ) + + def drawText(self, x, y, size, text, angle=0, align='bottom|left'): + x, y, size, angle = (float(i) for i in [x, y, size, angle]) + x, y = self.applyOffset(x, y) + align = align.replace("|", " ") + self.text(text=text, x=x, y=y, fontsize=size, angle=angle, align=align) + + def drawNema(self, x, y, size, screwhole_size=0): + x, y, size, screwhole_size = (float(i) for i in [x, y, size, screwhole_size]) + if size in self.nema_sizes: + x, y = self.applyOffset(x, y) + self.NEMA(size, x, y, screwholes=screwhole_size) + + def parse_layout(self, layout): + f = io.StringIO(layout) + line = 0 + objects = { + 'outline': self.drawOutline, + 'rect': self.drawRect, + 'circle': self.drawCircle, + 'mountinghole': self.drawMountingHole, + 'text': self.drawText, + 'nema': self.drawNema, + } + + for l in f.readlines(): + line += 1 + l = re.sub('#.*$', '', l) # remove comments + l = l.strip() + la = shlex.split(l, comments=True, posix=True) + if len(la) > 0 and la[0].lower() in objects: + objects[la[0]](*la[1:]) + + def render(self): + self.offset = (0.0, 0.0) + self.outline = None # No outline yet + self.parse_layout(self.layout) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/frontpanel_test.json b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/frontpanel_test.json new file mode 100644 index 0000000..892bf74 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/frontpanel_test.json @@ -0,0 +1,3 @@ +outline 60 60 +squre 10 20 25 34 +circle 10 20 40 diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/gear.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/gear.py index 37c34d9..f4fdad5 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/gear.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/gear.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,13 @@ from boxes import * + class Gears(Boxes): """Gears""" ui_group = "Part" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.argparser.add_argument( "--teeth1", action="store", type=int, default=12, @@ -51,7 +51,7 @@ class Gears(Boxes): help="angle of the teeth touching (in degrees)") self.argparser.add_argument( "--profile_shift", action="store", type=float, default=20, - help="in precent of the modulus") + help="in percent of the modulus") def render(self): # adjust to the variables you want in the local scope @@ -87,10 +87,9 @@ class Gears(Boxes): self.hole(t+r+r1+r2, t+r, self.shaft2/2) self.moveTo(0, 2*r+t) - self.text("""Pitch radius 1: %.1fmm -Outer diameter 1: %.1fmm -Pitch radius 2: %.1fmm -Outer diameter 2: %.1fmm -Axis distance: %.1fmm - """ % (r1, d1, r2, d2, r1+r2), align="bottom left") - + self.text(f"Pitch radius 1: {r1:.1f}mm\n" + f"Outer diameter 1: {d1:.1f}mm\n" + f"Pitch radius 2: {r2:.1f}mm\n" + f"Outer diameter 2: {d2:.1f}mm\n" + f"Axis distance: {r1 + r2:.1f}mm\n", + align="bottom left") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/gearbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/gearbox.py index 547d686..e93e286 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/gearbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/gearbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -22,7 +21,7 @@ class GearBox(Boxes): ui_group = "Part" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.argparser.add_argument( @@ -33,7 +32,7 @@ class GearBox(Boxes): help="number of teeth on outgoing shaft") self.argparser.add_argument( "--modulus", action="store", type=float, default=3, - help="modulus of the theeth in mm") + help="modulus of the teeth in mm") self.argparser.add_argument( "--shaft", action="store", type=float, default=6., help="diameter of the shaft") @@ -93,6 +92,3 @@ class GearBox(Boxes): for i in range(self.stages): self.gears(teeth=self.teeth1, dimension=self.modulus, angle=pressure_angle, mount_hole=mh, profile_shift=profile_shift, move="down") - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/gridfinitybase.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/gridfinitybase.py new file mode 100644 index 0000000..66f2603 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/gridfinitybase.py @@ -0,0 +1,89 @@ +# Copyright (C) 2013-2014 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * +from boxes import lids + + +class GridfinityBase(Boxes): + """A parameterized Gridfinity base""" + + description = """This is a configurable gridfinity base. This + design is based on + Zach Freedman's Gridfinity system""" + + ui_group = "Tray" + + def __init__(self) -> None: + Boxes.__init__(self) + self.addSettingsArgs(edges.FingerJointSettings, space=4, finger=4) + self.addSettingsArgs(lids.LidSettings) + self.argparser.add_argument("--x", type=int, default=3, help="number of grids in X direction") + self.argparser.add_argument("--y", type=int, default=2, help="number of grids in Y direction") + self.argparser.add_argument("--h", type=float, default=7*3, help="height of sidewalls of the tray (mm)") + self.argparser.add_argument("--m", type=float, default=0.5, help="Extra margin around the gridfinity base to allow it to drop into the carrier (mm)") + self.argparser.add_argument( + "--bottom_edge", action="store", + type=ArgparseEdgeType("Fhse"), choices=list("Fhse"), + default='F', + help="edge type for bottom edge") + self.argparser.add_argument("--pitch", type=int, default=42, help="The Gridfinity pitch, in mm. Should always be 42.") + self.argparser.add_argument("--opening", type=int, default=38, help="The cutout for each grid opening. Typical is 38.") + + def generate_grid(self): + pitch = self.pitch + nx, ny = self.x, self.y + opening = self.opening + for col in range(nx): + for row in range(ny): + lx = col*pitch+pitch/2 + ly = row*pitch+pitch/2 + self.rectangularHole(lx, ly, opening, opening) + + def create_base_plate(self): + pitch = self.pitch + nx, ny = self.x, self.y + opening = self.opening + self.rectangularWall(nx*pitch, ny*pitch, move="up", callback=[self.generate_grid]) + + def create_tray(self): + pitch = self.pitch + nx, ny = self.x, self.y + margin = self.m + x, y, h = nx*pitch, ny*pitch, self.h + t = self.thickness + x += 2*margin + y += 2*margin + t1, t2, t3, t4 = "eeee" + b = self.edges.get(self.bottom_edge, self.edges["F"]) + sideedge = "F" # if self.vertical_edges == "finger joints" else "h" + + self.rectangularWall(x, h, [b, sideedge, t1, sideedge], + ignore_widths=[1, 6], move="right") + self.rectangularWall(y, h, [b, "f", t2, "f"], + ignore_widths=[1, 6], move="up") + self.rectangularWall(y, h, [b, "f", t4, "f"], + ignore_widths=[1, 6], move="") + self.rectangularWall(x, h, [b, sideedge, t3, sideedge], + ignore_widths=[1, 6], move="left up") + + if self.bottom_edge != "e": + self.rectangularWall(x, y, "ffff", move="up") + + def render(self): + self.create_base_plate() + self.create_tray() + self.lid(self.x*self.pitch + 2*self.m, + self.y*self.pitch + 2*self.m) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/gridfinitytraylayout.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/gridfinitytraylayout.py new file mode 100644 index 0000000..04c2058 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/gridfinitytraylayout.py @@ -0,0 +1,116 @@ +import boxes +from boxes import Boxes, lids, restore +from boxes.Color import Color +from boxes.generators.traylayout import TrayLayout + + +class GridfinityTrayLayout(TrayLayout): + """A Gridfinity Tray Generator based on TrayLayout""" + + description = """ +This is a general purpose gridfinity tray generator. You can create +somewhat arbitrarily shaped trays, or just do nothing for simple grid +shaped trays. + +The dimensions are automatically calculated to fit perfectly into a +gridfinity grid (like the GridfinityBase, or any other Gridfinity +based base). + +Edit the layout text graphics to adjust your tray. +You can replace the hyphens and vertical bars representing the walls +with a space character to remove the walls. You can replace the space +characters representing the floor by a "X" to remove the floor for +this compartment. +""" + def __init__(self) -> None: + Boxes.__init__(self) + self.addSettingsArgs(boxes.edges.FingerJointSettings) + self.addSettingsArgs(lids.LidSettings) + self.buildArgParser(h=50) + self.outside = True # We're *always* outside for gridfinity + self.pitch = 42.0 # gridfinity pitch is defined as 42. + self.opening = 38 + self.opening_margin = 2 + self.argparser.add_argument("--hi", type=float, default=0, help="inner height of inner walls in mm (leave to zero for same as outer walls)") + self.argparser.add_argument("--nx", type=int, default=3, help="number of gridfinity grids in X direction") + self.argparser.add_argument("--ny", type=int, default=2, help="number of gridfinity grids in Y direction") + self.argparser.add_argument("--countx", type=int, default=5, help="split x into this many grid sections. 0 means same as --nx") + self.argparser.add_argument("--county", type=int, default=3, help="split y into this many grid sections. 0 means same as --ny") + self.argparser.add_argument("--margin", type=float, default=0.75, help="Leave this much total margin on the outside, in mm") + self.argparser.add_argument("--layout", type=str, help="You can hand edit this before generating", default="\n"); + + def generate_layout(self): + layout = '' + countx = self.countx + county = self.county + if countx == 0: + countx = self.nx + if county == 0: + county = self.ny + + stepx = self.x / countx + stepy = self.y / county + for i in range(countx): + line = ' |' * i + f" ,> {stepx}mm\n" + layout += line + for i in range(county): + layout += "+-" * countx + f"+\n" + layout += "| " * countx + f"|{stepy}mm\n" + layout += "+-" * countx + "+\n" + return layout + + @restore + def rectangularEtching(self, x, y, dx, dy, r=0, center_x=True, center_y=True): + """ + Draw a rectangular hole + + :param x: position + :param y: position + :param dx: width + :param dy: height + :param r: (Default value = 0) radius of the corners + :param center_x: (Default value = True) if True, x position is the center, else the start + :param center_y: (Default value = True) if True, y position is the center, else the start + """ + r = min(r, dx/2., dy/2.) + x_start = x if center_x else x + dx / 2.0 + y_start = y - dy / 2.0 if center_y else y + self.moveTo(x_start, y_start, 180) + self.edge(dx / 2.0 - r) # start with an edge to allow easier change of inner corners + for d in (dy, dx, dy, dx / 2.0 + r): + self.corner(-90, r) + self.edge(d - 2 * r) + + def baseplate_etching(self): + x = -self.thickness - self.margin / 2 + y = -self.thickness - self.margin / 2 + o = self.opening + p = self.pitch + m = self.opening_margin + self.ctx.stroke() + with self.saved_context(): + for xx in [0, self.nx-1]: + for yy in [0, self.ny-1]: + self.set_source_color(Color.ETCHING) + self.rectangularEtching(x+p/2+xx*p, y+p/2+yy*p, o-m, o-m) + self.ctx.stroke() + + def render(self): + # Create a layout + self.x = self.pitch * self.nx - self.margin + self.y = self.pitch * self.ny - self.margin + self.outer_x = self.x + self.outer_y = self.y + + self.prepare() + self.walls() + with self.saved_context(): + self.base_plate(callback=[self.baseplate_etching], + move="mirror right") + foot = self.opening - self.opening_margin + for i in range(min(self.nx * self.ny, 4)): + self.rectangularWall(foot, foot, move="right") + self.base_plate(callback=[self.baseplate_etching], + move="up only") + self.lid(sum(self.x) + (len(self.x)-1) * self.thickness, + sum(self.y) + (len(self.y)-1) * self.thickness) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/halfbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/halfbox.py index 83db9d0..b5184ed 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/halfbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/halfbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,6 +15,7 @@ from boxes import * + class HalfBox(Boxes): """Configurable half of a box which can be: a bookend, a hanging shelf, an angle clamping jig, ...""" @@ -35,7 +35,7 @@ and many more... """ ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, finger=2.0,space=2.0) @@ -46,16 +46,16 @@ and many more... self.argparser.add_argument("--ClampingSize", action="store", type=float, default=25.0, help="diameter of clamping holes") self.argparser.add_argument("--Mounting", action="store", type=boolarg, default=False, help="add mounting holes") self.argparser.add_argument("--Sturdy", action="store", type=boolarg, default=False, help="create sturdy construction (e.g. shelf, clamping jig, ...)") - + def polygonWallExt(self, borders, edge="f", turtle=False, callback=None, move=None): - # extended polygon wall. + # extended polygon wall. # same as polygonWall, but with extended border parameters # each border dataset consists of # length # turn angle # radius of turn (without radius correction) # edge type - + for i in range(0, len(borders), 4): self.cc(callback, i) length = borders[i] @@ -82,19 +82,19 @@ and many more... for y in reversed(self.sy[1:]): posy += y + self.thickness self.fingerHolesAt(posy, 0, self.h) - + def render(self): # adjust to the variables you want in the local scope - + x, h = self.x, self.h d = self.ClampingSize t = self.thickness - + # triangle with sides: x (horizontal), h (upwards) and l # angles: 90° between x & h # b between h & l # c between l & x - + l = math.sqrt(x * x + h * h) b = math.degrees(math.asin(x / l)) c = math.degrees(math.asin(h / l)) @@ -105,22 +105,22 @@ and many more... if 90 + b + c < 179: c = 180 - c - # small triangle top: 2*t, h1, l1 + # small triangle top: 2*t, h1, l1 h1 = (2*t)/x*h l1 = (2*t)/x*l - # small triangle left: x2, 2*t, l2 + # small triangle left: x2, 2*t, l2 x2 = (2*t)/h*x l2 = (2*t)/h*l - + # render your parts here - + if self.Sturdy: width = sum(self.sy) + (len(self.sy) - 1) * t self.rectangularWall(x, width, "fffe", callback=[None, self.xHoles, None, None], move="right", label="bottom") self.rectangularWall(h, width, "fGfF" if self.Mounting else "fefF", callback=[None, None, None, self.hHoles], move="up", label="back") self.rectangularWall(x, width, "fffe", callback=[None, self.xHoles, None, None], move="left only", label="invisible") - + for i in range(2): self.move(x+x2+2*t + self.edges["f"].margin(), h+h1+2*t + self.edges["f"].margin(), "right", True, label="side " + str(i)) self.polygonWallExt(borders=[x2, 0, 0, "e", x, 0, 0, "h",2*t, 90, 0, "e", 2*t, 0, 0, "e", h, 0, 0, "h",h1, 180-b, 0, "e", l+l1+l2, 180-c, 0, "e"]) @@ -131,7 +131,7 @@ and many more... self.move(x+x2+2*t + self.edges["f"].margin(), h+h1+2*t + self.edges["f"].margin(), "right", False, label="side " + str(i)) if len(self.sy) > 1: - for i in range((len(self.sy) - 1)): + for i in range(len(self.sy) - 1): self.move(x + self.edges["f"].margin(), h + self.edges["f"].margin(), "right", True, label="support " + str(i)) self.polygonWallExt(borders=[x, 90, 0, "f", h, 180-b, 0, "f", l, 180-c, 0, "e"]) if self.Clamping: @@ -146,7 +146,7 @@ and many more... self.rectangularWall(h, width, "eGeF" if self.Mounting else "eeeF", callback=[None, None, None, self.hHoles], move="up", label="side") self.rectangularWall(x, width, "efee", callback=[None, self.xHoles, None, None], move="left only", label="invisible") - for i in range((len(self.sy) - 1)): + for i in range(len(self.sy) - 1): self.move(x + self.edges["f"].margin(), h + self.edges["f"].margin(), "right", True, label="support " + str(i)) self.polygonWallExt(borders=[x, 90, 0, "f", h, 180-b, 0, "f", l, 180-c, 0, "e"]) if self.Clamping: diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/heart.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/heart.py index cdabf78..191e070 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/heart.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/heart.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,13 @@ from boxes import * + class HeartBox(Boxes): - """Box in the form of an heart""" + """Box in the form of a heart""" ui_group = "FlexBox" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, finger=1.0,space=1.0) @@ -42,7 +42,7 @@ class HeartBox(Boxes): if self.top == "closed": return - + for i in range(2): self.moveTo(t, t) self.polyline((l, 2), (180, r), (d, 1), -90, @@ -59,12 +59,11 @@ class HeartBox(Boxes): l = 2/3. * x r = l/2. - 0.5*t - + borders = [l, (180, r), t, -90, t, (180, r), l, 90] self.polygonWalls(borders, h) self.rectangularWall(0, h, "FFFF", move="up only") self.polygonWall(borders, callback=[self.CB], move="right") - self.moveTo(-2*t) self.polygonWall(borders, move="mirror right") if self.top == "lid": self.polygonWall([l+t, (180, r+t), 0, -90, 0, (180, r+t), l+t, 90], 'e') diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/hingebox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/hingebox.py index 08dc87c..d25dda4 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/hingebox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/hingebox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,20 @@ from boxes import * + class HingeBox(Boxes): """Box with lid attached by cabinet hinges""" + description = """Needs (metal) pins as hinge axles. Pieces of nails will +do fine. They need to be cut to length as they are captured as soon as the +hinges are assembled. + +Assemble the box and the lid separately. Then insert the axle into the hinges. +Then attach the hinges on the inside of the box and then connect them to lid. +""" ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.addSettingsArgs(edges.CabinetHingeSettings) @@ -37,7 +44,7 @@ class HingeBox(Boxes): x, y, h, hl = self.x, self.y, self.h, self.lidheight s = self.splitlid - + if self.outside: x = self.adjustSize(x) y = self.adjustSize(y) @@ -79,6 +86,3 @@ class HingeBox(Boxes): self.edges['u'].parts(move="up") if s: self.edges['u'].parts(move="up") - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/holepattern.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/holepattern.py index 59fe10a..ac79544 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/holepattern.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/holepattern.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2022 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,16 +14,14 @@ # along with this program. If not, see . from boxes import * -from shapely.geometry import * -import random -import time + class HolePattern(Boxes): """Generate hole patterns in different simple shapes""" ui_group = "Holes" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(fillHolesSettings, fill_pattern="hex") @@ -63,7 +60,7 @@ class HolePattern(Boxes): (d2*x, y), (d*x, y), (0, d2*y), (0, d*y)] else: # "rectangle" border = [(0, 0), (x, 0), (x, y), (0, y)] - + self.fillHoles( pattern=self.fillHoles_fill_pattern, border=border, diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/hooks.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/hooks.py index 4d3a8b7..393c16a 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/hooks.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/hooks.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2018 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,14 +14,14 @@ # along with this program. If not, see . from boxes import * -import math + class Hook(Boxes): - """A hook wit a rectangular mouth to mount at the wall""" + """A hook with a rectangular mouth to mount at the wall""" ui_group = "Misc" # see ./__init__.py for names - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=0.5) @@ -40,7 +39,7 @@ class Hook(Boxes): help="width of the hook from the side") self.argparser.add_argument("--angle", action="store", type=float, default=45., - help="angle of the support underneeth") + help="angle of the support underneath") def render(self): @@ -79,11 +78,11 @@ class Hook(Boxes): self.hole(x_h, y1, d=3) self.hole(x_h, y2, d=3) self.hole(x_h, y3, d=3) - + @property def height_back(self): - + return self.strength + self.height + self.h_a @property diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/integratedhingebox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/integratedhingebox.py index dbc2132..4288bc5 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/integratedhingebox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/integratedhingebox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -22,7 +21,7 @@ class IntegratedHingeBox(Boxes): ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.addSettingsArgs(edges.ChestHingeSettings) @@ -30,7 +29,7 @@ class IntegratedHingeBox(Boxes): self.argparser.add_argument( "--lidheight", action="store", type=float, default=20.0, help="height of lid in mm") - + def render(self): @@ -61,6 +60,3 @@ class IntegratedHingeBox(Boxes): self.rectangularWall(y, x, "ffff", move="up") self.rectangularWall(y, x, "ffff") - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/jigsaw.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/jigsaw.py index 7bcfcd1..f47a7ff 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/jigsaw.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/jigsaw.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,15 +14,14 @@ # along with this program. If not, see . from boxes import * -import random class JigsawPuzzle(Boxes): # change class name here and below - """Fractal jigsaw puzzle. Still aplha""" + """Fractal jigsaw puzzle. Still alpha.""" webinterface = False # Change to make visible in web interface - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.count = 0 self.argparser.add_argument( @@ -83,5 +81,3 @@ class JigsawPuzzle(Boxes): # change class name here and below self.burn = 0.0 self.moveTo(10, 10) self.hilbert(self.depth) - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/jointpanel.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/jointpanel.py index 8657b6f..be6e5e6 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/jointpanel.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/jointpanel.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,6 +15,7 @@ from boxes import * + class JointPanel(Boxes): """Create pieces larger than your laser cutter by joining them with Dove Tails""" @@ -28,10 +28,10 @@ Small dove tails make it easier to fit parts in without problems. Lookout for pi For plywood this method works well with a very stiff press fit. Aim for needing a hammer to join the pieces together. This way they will feel like they have been welder together. """ - + ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs( diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/keyboard.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/keyboard.py index 88a81d6..f19bd12 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/keyboard.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/keyboard.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- # Copyright (C) 2021 Guillaume Collic # # This program is free software: you can redistribute it and/or modify @@ -15,9 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import re import argparse -from boxes import Boxes, boolarg +import re + +from boxes import boolarg + class Keyboard: """ @@ -33,7 +33,7 @@ class Keyboard: SWITCH_CASE_SIZE = 15.6 FRAME_CUTOUT = 14 - def __init__(self): + def __init__(self) -> None: pass def add_common_keyboard_parameters( @@ -116,7 +116,6 @@ class Keyboard: Spaces are not important. For example '3x2 / 4@11' means we want 3 columns, the two first with 3 rows without offset, and the last with 4 rows starting at 11mm high - """ result = [] try: @@ -166,7 +165,7 @@ class Keyboard: grid_hole(3, -4, led_hole_size) def apply_callback_on_columns(self, cb, columns_definition, spacing=None, reverse=False): - if spacing == None: + if spacing is None: spacing = self.STANDARD_KEY_SPACING if reverse: columns_definition = list(reversed(columns_definition)) @@ -244,7 +243,7 @@ class Keyboard: A simple plate cutout, a 14mm rectangle, as specified in this reference sheet https://cdn.sparkfun.com/datasheets/Components/Switches/MX%20Series.pdf - With_notch shoul be used for a secondary lower plate, strengthening the first one. + With_notch should be used for a secondary lower plate, strengthening the first one. A notch is added to let the hooks grasp the main upper plate. Current position should be switch center. @@ -282,4 +281,4 @@ class Keyboard: self.moveTo(0, 0, -90) self.moveTo(half_size - self.burn) else: - self.rectangularHole(0, 0, size, size, r=radius) \ No newline at end of file + self.rectangularHole(0, 0, size, size, r=radius) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/keyholder.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/keyholder.py new file mode 100644 index 0000000..fef6eda --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/keyholder.py @@ -0,0 +1,185 @@ +# Copyright (C) 2013-2014 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class HangerEdge(edges.BaseEdge): + char = "H" + + def margin(self) -> float: + return self.hook_height * 0.7 + + def __call__(self, l, **kw): + # Radius of the bottom part of the hook + radius_outside = self.hook_height * 0.5 + radius_inside = radius_outside - self.hook_thickness + + # Make corners less sharp + radius_burr = 1.5 + + hookInnerHeight = self.hook_height * 0.7 + hookLength = self.hook_height * 0.7 + + # Correct orientation + self.polyline(0, -90) + + # Line bottom + self.edge(hookLength - radius_outside) + + # Outer corner + self.corner(90, radius_outside) + + # Line right + self.edge(hookInnerHeight - radius_outside - self.hook_thickness / 2) + + # Semicircle at top + self.corner(180, self.hook_thickness / 2) + + # Line left-ish + self.edge( + hookInnerHeight + - self.hook_thickness + - self.hook_thickness / 2 + - radius_inside + ) + + # Inner corner + self.corner(-90, radius_inside) + + # Line bottom + self.edge(hookLength - self.hook_thickness - 2 * radius_burr - radius_inside) + self.corner(-90, radius_burr) + + # Line top + self.edge(self.hook_height - self.hook_thickness - 2 * radius_burr) + self.corner(90, radius_burr) + + # Correct orientation + self.polyline(0, -90) + + +class KeyHolder(Boxes): + """Wall organizer with hooks for keys or similar small items""" + + description = """Example for a KeyHolder with a slightly larger backplate and 8 hooks. This uses 6mm plywood for extra stability. + +Closeup: + +![KeyHolder-2](static/samples/KeyHolder-2.jpg) + +Full picture: +""" + + ui_group = "WallMounted" + + def __init__(self) -> None: + Boxes.__init__(self) + self.argparser.add_argument( + "--num_hooks", action="store", type=int, default=7, help="Number of hooks" + ) + self.argparser.add_argument( + "--hook_distance", + action="store", + type=float, + default=20, + help="Distance between hooks", + ) + self.argparser.add_argument( + "--hook_thickness", + action="store", + type=float, + default=5, + help="Thickness of hook", + ) + self.argparser.add_argument( + "--hook_height", + action="store", + type=float, + default=20, + help="Height of back part of hook", + ) + + # Padding around the hooks to define the size of the back plate + self.argparser.add_argument( + "--padding_top", + action="store", + type=float, + default=10, + help="Padding above hooks", + ) + self.argparser.add_argument( + "--padding_left_right", + action="store", + type=float, + default=5, + help="Padding left/right from hooks", + ) + self.argparser.add_argument( + "--padding_bot", + action="store", + type=float, + default=30, + help="Padding below hooks", + ) + + self.argparser.add_argument( + "--mounting", + action="store", + type=boolarg, + default=False, + help="Add mounting holes", + ) + + self.addSettingsArgs( + edges.FingerJointSettings, surroundingspaces=0.0, finger=1.0, space=1.0 + ) + self.addSettingsArgs(edges.MountingSettings) + + def yHoles(self): + """ + Holes for hooks to attach to + """ + posx = 0.5 * self.thickness + posx += self.padding_left_right + for _ in range(self.num_hooks): + self.fingerHolesAt(posx, self.padding_bot, self.hook_height) + posx += self.hook_distance + self.thickness + + def render(self): + self.addPart(HangerEdge(self, 1)) + + # Total height and width of the backplate + h = self.hook_height + self.padding_bot + self.padding_top + w = ( + (self.padding_left_right * 2) + + self.num_hooks * self.thickness + + (self.num_hooks - 1) * self.hook_distance + ) + + # Back plate + self.rectangularWall( + w, + h, + "eeGe" if self.mounting else "eeee", + callback=[self.yHoles, None, None, None], + move="up", + ) + + # Hooks + for _ in range(self.num_hooks): + self.rectangularWall( + self.hook_thickness, self.hook_height, "eHef", move="right" + ) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/keypad.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/keypad.py index c5c5acd..1696356 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/keypad.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/keypad.py @@ -4,6 +4,7 @@ from copy import deepcopy from boxes import Boxes, boolarg from boxes.edges import FingerJointSettings + from .keyboard import Keyboard @@ -18,7 +19,7 @@ class Keypad(Boxes, Keyboard): box_padding = 10 triangle = 25.0 - def __init__(self): + def __init__(self) -> None: super().__init__() self.argparser.add_argument( '--h', action='store', type=int, default=30, @@ -109,7 +110,7 @@ class Keypad(Boxes, Keyboard): return [callback] def hotplug(self): - """Callback for the key stabelizers.""" + """Callback for the key stabilizers.""" self.pcb_holes( with_pcb_mount=self.pcb_mount_enable, with_diode=self.diode_enable, diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/lamp.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/lamp.py index 84b96b7..03ad9ad 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/lamp.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/lamp.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,7 +14,6 @@ # along with this program. If not, see . from boxes import * -import math """ 22x7.5x7cm @@ -51,17 +49,17 @@ class RoundedTriangle(edges.Edge): self.edge(l) self.corner(90 - angle, r) - def startAngle(self): - return 90 + def startAngle(self) -> float: + return 90.0 - def endAngle(self): - return 90 + def endAngle(self) -> float: + return 90.0 class Lamp(Boxes): webinterface = False - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.buildArgParser(x=220, y=75, h=70) @@ -129,6 +127,3 @@ class Lamp(Boxes): self.rectangularWall(x, h, edges='hFFF', holesMargin=5, move="right") self.rectangularWall(x, h, edges='hFFF', holesMargin=5) - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/laptopstand.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/laptopstand.py index b9b18d0..f977970 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/laptopstand.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/laptopstand.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,16 +13,17 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from boxes import * from math import * +from boxes import * + class LaptopStand(Boxes): # Change class name! """A simple X shaped frame to support a laptop on a given angle""" ui_group = "Misc" # see ./__init__.py for names - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.argparser.add_argument( diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/laserclamp.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/laserclamp.py index 9d6dd48..5370a4e 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/laserclamp.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/laserclamp.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,18 +15,19 @@ from boxes import * + class LaserClamp(Boxes): """A clamp to hold down material to a knife table""" description = """You need a tension spring of the proper length to make the clamp work. -Increace extraheight to get more space for the spring and to make the +Increase extraheight to get more space for the spring and to make the sliding mechanism less likely to bind. You may need to add some wax on the parts sliding on each other to reduce friction. """ ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=0) @@ -75,7 +75,7 @@ parts sliding on each other to reduce friction. else: self.polyline(6*t) self.polyline(4*t, (90, 2*t), 3*t, 135, 2*ls, 45, 1*t, -90, 6*t, -90) - + self.polyline(h_min, (90, t), 2*t, (90, t), h_min+h_extra-0*t, (-90, t), t, (180, t), 0, 90, 0, (-180, 0.5*t), 0 , 90) @@ -88,7 +88,7 @@ parts sliding on each other to reduce friction. if h_extra and h_extra < 2*t: h_extra = 2*t - + self.topPart(h_max+h_extra, move="right") self.bottomPart(h_min, h_extra, move="right") self.roundedPlate(4*t, h_min+h_extra+4*t, edge="e", r=t, diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/laserholdfast.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/laserholdfast.py index bc21f03..cd293a6 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/laserholdfast.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/laserholdfast.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,13 @@ from boxes import * + class LaserHoldfast(Boxes): """A holdfast for honey comb tables of laser cutters""" ui_group = "Part" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser(x=25, h=40) @@ -34,7 +34,7 @@ class LaserHoldfast(Boxes): def render(self): # adjust to the variables you want in the local scope - x, hh, h, sw = self.x, self.hookheight, self.h, self.shaftwidth + x, hh, h, sw = self.x, self.hookheight, self.h, self.shaftwidth t = self.thickness a = 30 diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/lbeam.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/lbeam.py index 9624163..d360b3e 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/lbeam.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/lbeam.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,13 @@ from boxes import * + class LBeam(Boxes): """Simple L-Beam: two pieces joined with a right angle""" ui_group = "Part" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser("x", "y", "h", "outside") self.addSettingsArgs(edges.FingerJointSettings) @@ -38,5 +38,3 @@ class LBeam(Boxes): self.rectangularWall(x, h, "eFee", move="right") self.rectangularWall(y, h, "eeef") - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/magazinefile.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/magazinefile.py index d8c6cb8..7716901 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/magazinefile.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/magazinefile.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -17,10 +16,11 @@ from boxes import * from boxes.lids import _TopEdge -class MagazinFile(Boxes): + +class MagazineFile(Boxes): """Open magazine file""" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser(x=100, y=200, h=300, hi=0, outside=False) self.addSettingsArgs(edges.FingerJointSettings) @@ -87,6 +87,3 @@ class MagazinFile(Boxes): self.side(y, h, hi, t1) self.moveTo(y + 15, h + hi + 15, 180) self.side(y, h, hi, t3) - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/makitapowersupply.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/makitapowersupply.py index aa1c18c..949ce06 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/makitapowersupply.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/makitapowersupply.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,6 +15,7 @@ from boxes import * + class MakitaPowerSupply(Boxes): """Bench power supply powered with Maktia 18V battery or laptop power supply""" @@ -27,7 +27,7 @@ To allow powering by laptop power supply: flip switch, Lenovo round socket (or a ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/microrack.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/microrack.py index 5eabb8e..92cd3f4 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/microrack.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/microrack.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2019 Gabriel Morell # # This program is free software: you can redistribute it and/or modify @@ -13,9 +12,8 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import decimal -from boxes import Boxes, edges, boolarg +from boxes import Boxes, boolarg, edges class SBCMicroRack(Boxes): @@ -24,7 +22,7 @@ class SBCMicroRack(Boxes): webinterface = True ui_group = "Shelf" # see ./__init__.py for names - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) @@ -139,7 +137,7 @@ class SBCMicroRack(Boxes): t = self.thickness x = self.x w = x + self.hole_dist_edge * 2 - height_per = self.clearance_z + t + height_per = self.clearance_z + t usb_height = self.netusb_z usb_width = self.netusb_x for i in range(self.sbcs): @@ -199,4 +197,3 @@ class SBCMicroRack(Boxes): "efef", callback=[self.paint_mounting_holes], move="up") - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/nemamount.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/nemamount.py index a827e8a..6ccc4d2 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/nemamount.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/nemamount.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2017 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,13 @@ from boxes import * + class NemaMount(Boxes): - """Mounting braket for a Nema motor""" + """Mounting bracket for a Nema motor""" ui_group = "Part" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.argparser.add_argument( @@ -52,4 +52,3 @@ class NemaMount(Boxes): self.fingerHolesAt(0.5*t, t, x, 90) self.fingerHolesAt(1.5*t+x, t, x, 90) self.fingerHolesAt(t, 0.5*t, x, 0) - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/nemapattern.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/nemapattern.py index d4d25fd..0976f66 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/nemapattern.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/nemapattern.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2017 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,13 @@ from boxes import * + class NemaPattern(Boxes): """Mounting holes for a Nema motor""" ui_group = "Holes" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.argparser.add_argument( diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/notesholder.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/notesholder.py index 7363a69..90c6161 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/notesholder.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/notesholder.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,20 +14,31 @@ # along with this program. If not, see . from boxes import * -from boxes.edges import Edge +from boxes.edges import CompoundEdge, Edge + class USlotEdge(Edge): + def __init__(self, boxes, settings, edge="f"): + super().__init__(boxes, settings) + self.e = edge + def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw): l = length - d = self.settings + o = self.settings + d = length * (1-o/100) / 2 r = min(3*self.thickness, (l-2*d)/2) - self.edges["f"](d) + self.edges[self.e](d) + self.step(-self.edges[self.e].endwidth()) self.polyline(0, 90, 0, (-90, r), l-2*d-2*r, (-90, r), 0, 90) - self.edges["f"](d) + self.step(self.edges[self.e].startwidth()) + self.edges[self.e](d) - def margin(self): - return self.edges["f"].margin() + def margin(self) -> float: + return self.edges[self.e].margin() + + def startwidth(self): + return self.edges[self.e].startwidth() class HalfStackableEdge(edges.StackableEdge): @@ -49,7 +59,7 @@ class HalfStackableEdge(edges.StackableEdge): self.boxes.corner(-p * s.angle, r) self.boxes.edge(length - 1 * s.width - 2 * l) - def endwidth(self): + def endwidth(self) -> float: return self.settings.holedistance + self.settings.thickness class NotesHolder(Boxes): @@ -57,19 +67,38 @@ class NotesHolder(Boxes): ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=1) self.addSettingsArgs(edges.StackableSettings) - self.buildArgParser(x=78, y=78, h=35, bottom_edge="s") + self.buildArgParser(sx="78*1", y=78, h=35) + self.argparser.add_argument( + "--bottom_edge", action="store", + type=ArgparseEdgeType("Fhsfe"), choices=list("Fhsfe"), + default="s", + help="edge type for bottom edge") self.argparser.add_argument( "--opening", action="store", type=float, default=40, - help="percent of front that's open") + help="percent of front (or back) that's open") + self.argparser.add_argument( + "--back_openings", action="store", type=boolarg, default=False, + help="have openings on the back side, too") + + def fingerHoleCB(self, lengths, height, posy=0.0): + def CB(): + t = self.thickness + px = -0.5 * t + for x in lengths[:-1]: + px += x + t + self.fingerHolesAt(px, posy, height, 90) + return CB def render(self): - x, y, h = self.x, self.y, self.h + sx, y, h = self.sx, self.y, self.h t = self.thickness + x = sum(sx) + (len(sx) - 1) * t + o = max(0, min(self.opening, 100)) sides = x * (1-o/100) / 2 @@ -77,32 +106,73 @@ class NotesHolder(Boxes): if self.bottom_edge == "s": b2 = HalfStackableEdge(self, self.edges["s"].settings, self.edges["f"].settings) + b3 = self.edges["h"] else: b2 = b + b3 = b - with self.saved_context(): - self.rectangularWall(y, h, [b, "F", "e", "F"], - ignore_widths=[1, 6], move="right") - if self.opening == 0.0: - self.rectangularWall(x, h, [b, "f", "e", "f"], + b4 = Edge(self, None) + b4.startwidth = lambda: b3.startwidth() + + + for side in range(2): + with self.saved_context(): + self.rectangularWall(y, h, [b, "F", "e", "F"], ignore_widths=[1, 6], move="right") - else: - self.rectangularWall(sides, h, [b2, "e", "e", "f"], - ignore_widths=[1, 6], move="right") - self.rectangularWall(sides, h, [b2, "e", "e", "f"], - ignore_widths=[1, 6], move="right mirror") + # front walls + if self.opening == 0.0 or (side and not self.back_openings): + self.rectangularWall(x, h, [b, "f", "e", "f"], + ignore_widths=[1, 6], move="right") + else: + self.rectangularWall(sx[0] * (1-o/100) / 2, h, + [b2, "e", "e", "f"], + ignore_widths=[1, 6], move="right") + for ix in range(len(sx)-1): + left = sx[ix] * (1-o/100) / 2 + right = sx[ix+1] * (1-o/100) / 2 + h_e = t + bottom_edge = CompoundEdge( + self, [b3, b4, b3], [left, t, right]) + self.rectangularWall( + left+right+t, h, + [bottom_edge, "e", "e", "e"], + callback=[lambda: self.fingerHolesAt(left+t/2, 0, h, 90)], + move="right") - self.rectangularWall(x, h, [b, "F", "e", "F"], - ignore_widths=[1, 6], move="up only") + self.rectangularWall(sx[-1] * (1-o/100) / 2, h, + [b2, "e", "e", "f"], + ignore_widths=[1, 6], + move="right mirror") - with self.saved_context(): - self.rectangularWall(y, h, [b, "F", "e", "F"], ignore_widths=[1, 6], move="right") - self.rectangularWall(x, h, [b, "f", "e", "f"], ignore_widths=[1, 6], move="right") - self.rectangularWall(y, h, [b, "F", "e", "F"], move="up only") + self.rectangularWall(x, h, [b, "F", "e", "F"], + ignore_widths=[1, 6], move="up only") + # hack to have it reversed in second go and then back to normal + sx = list(reversed(sx)) + # bottom if self.bottom_edge != "e": - if self.opening == 0.0: - self.rectangularWall(x, y, ["f", "f", "f", "f"], move="up") - else: - self.rectangularWall(x, y, [USlotEdge(self, sides), "f", "f", "f"], move="up") + outer_edge = "h" if self.bottom_edge == "f" else "f" + font_edge = back_edge = outer_edge + u_edge = USlotEdge(self, o, outer_edge) + outer_width = self.edges[outer_edge].startwidth() + if self.opening > 0.0: + front_edge = CompoundEdge( + self, + ([u_edge, edges.OutSetEdge(self, outer_width)]*len(sx))[:-1], + ([l for x in sx for l in (x, t)])[:-1]) + if self.opening > 0.0 and self.back_openings: + back_edge = CompoundEdge( + self, + ([u_edge, edges.OutSetEdge(self, outer_width)]*len(sx))[:-1], + ([l for x in reversed(sx) for l in (x, t)])[:-1]) + self.rectangularWall( + x, y, + [front_edge, outer_edge, back_edge, outer_edge], + callback=[self.fingerHoleCB(sx, y)], + move="up") + # innner walls + for i in range(len(sx)-1): + self.rectangularWall( + y, h, ("e" if self.bottom_edge=="e" else "f") + "fef", + move="right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/openbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/openbox.py index 0b16343..5fd0c72 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/openbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/openbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,13 @@ from boxes import * + class OpenBox(Boxes): """Box with top and front open""" ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser("x", "y", "h", "outside") self.argparser.add_argument( diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/organpipe.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/organpipe.py index 30bd82e..39e1e2d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/organpipe.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/organpipe.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- # Copyright (C) 2013-2018 Florian Festi # # Based on pipecalc by Christian F. Coors @@ -18,8 +16,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from math import * + from boxes import * -from math import * pitches = ['c', 'c#', 'd', 'd#', 'e', 'f', 'f#', 'g', 'g#', 'a', 'a#' ,'b'] @@ -41,16 +40,16 @@ class OrganPipe(Boxes): # Change class name! def getRadius(self, pitch, octave, intonation): steps = pitches.index(pitch) + (octave-2)*12 + intonation return 0.5 * 0.15555 * 0.957458**steps - + def getAirSpeed(self, wind_pressure, air_density=1.2): return (2.0 * (wind_pressure / air_density))**.5 - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, finger=3.0, space=3.0, surroundingspaces=1.0) - + """ air_temperature: f64, """ @@ -73,7 +72,7 @@ class OrganPipe(Boxes): # Change class name! help="Cutup to mouth ratio") self.argparser.add_argument( "--mensur", action="store", type=int, default=0, - help=u"Distance in halftones in the Normalmensur by Töpfer") + help="Distance in halftones in the Normalmensur by Töpfer") self.argparser.add_argument( "--windpressure", action="store", type=float, default=588.4, help="uses unit selected below") @@ -91,12 +90,12 @@ class OrganPipe(Boxes): # Change class name! f = self.getFrequency(self.pitch, self.octave, 440) self.windpressure *= pressure_units.get(self.windpressure_units, 1.0) - + speed_of_sound = 343.6 # XXX util::speed_of_sound(self.air_temperature); // in m/s air_density = 1.2 air_speed = self.getAirSpeed(self.windpressure, air_density) - i = self.intonation; + i = self.intonation radius = self.getRadius(self.pitch, self.octave, i) * 1000 cross_section = pi * radius**2 circumference = pi * radius * 2.0 @@ -110,9 +109,9 @@ class OrganPipe(Boxes): # Change class name! sound_power = (0.001 * pi * (air_density / speed_of_sound) * f**2 * (1.7 * (jet_thickness * speed_of_sound * f * mouth_area * mouth_area**.5)**.5)**2) - air_consumption_rate = air_speed * mouth_width * jet_thickness * 1E6; + air_consumption_rate = air_speed * mouth_width * jet_thickness * 1E6 - wavelength = speed_of_sound / f * 1000; + wavelength = speed_of_sound / f * 1000 if self.stopped: theoretical_resonator_length = wavelength / 4.0 @@ -125,11 +124,11 @@ class OrganPipe(Boxes): # Change class name! air_hole_diameter = 2.0 * ((mouth_width * jet_thickness * 10.0)**.5 / pi) total_length = resonator_length + base_length - + e = ["f", "e", edges.CompoundEdge(self, "fef", (resonator_length - mouth_height - 10*t, mouth_height + 10*t, base_length)), "f"] - + self.rectangularWall(total_length, pipe_depth, e, callback=[ lambda: self.fingerHolesAt(base_length-0.5*t, 0, pipe_depth-jet_thickness)], move="up") @@ -146,4 +145,3 @@ class OrganPipe(Boxes): # Change class name! self.rectangularWall(mouth_width, pipe_depth, "fFfF", callback=[ lambda:self.hole(mouth_width/2, pipe_depth/2, d=air_hole_diameter)], move="right") self.rectangularWall(mouth_width, pipe_depth - jet_thickness, "ffef", move="right") - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ottobody.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ottobody.py index a6ff548..4b15c85 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ottobody.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ottobody.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -17,7 +16,7 @@ try: from gettext import gettext as _ except ImportError: - def _(message): + def _(message: str) -> str: return message from boxes import * @@ -28,7 +27,7 @@ class OttoBody(Boxes): ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.addSettingsArgs(edges.ChestHingeSettings) @@ -57,7 +56,7 @@ class OttoBody(Boxes): self.hole(6, self.y/2+9, 0.9) self.rectangularHole(6, self.y/2-5.5, 12, 23) self.hole(6, self.y/2-20, 0.9) - + def rightBottomCB(self): self.hole(7, self.y-5, 2) @@ -96,7 +95,7 @@ class OttoBody(Boxes): def PCB_Clamp(self, w, s, h, move=None): t = self. thickness f = 2**0.5 - + if self.move(w+4, h+8+t, move, True): return self.polyline(w, 90, s, -90, 1, (90, 1), (h-s-1, 2), 90, w-2, 90, @@ -110,7 +109,7 @@ class OttoBody(Boxes): self.y = y = 60. self.h = h = 35. self.hl = hl = 30. - + t = self.thickness hx = self.edges["O"].startwidth() @@ -124,7 +123,7 @@ class OttoBody(Boxes): self.moveTo(hx) self.rectangularWall(x, h-hx, "FfOf", ignore_widths=[2], move="up", label=_("Left bottom side")) self.rectangularWall(x, hl-hx2, "pfFf", ignore_widths=[1], move="up", label=_("Left top side")) - self.moveTo(-hx) + self.moveTo(-hx) self.rectangularWall(x, h-hx, "Ffof", ignore_widths=[5], callback=[ lambda: self.rectangularHole(y-7.5, h-4-7.5, 6.2, 7.)], move="up", label=_("Right bottom side")) @@ -162,6 +161,3 @@ class OttoBody(Boxes): self.rectangularWall(y-5.6, 14, callback=[ None, None, None, self.rightBottomCB], move="up", label=_("Servo mount")) - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ottolegs.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ottolegs.py index 86e8e5a..8ab0650 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ottolegs.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ottolegs.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -17,6 +16,7 @@ from boxes import * from boxes import edges + class LegEdge(edges.BaseEdge): def __call__(self, l, **kw): @@ -29,7 +29,7 @@ class OttoLegs(Boxes): ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, finger=1.0, space=1.0, @@ -56,7 +56,7 @@ class OttoLegs(Boxes): for l in (x, y, x, y): self.polyline((l - 2*r, 2), 45, r*2**0.5, 45) - + self.move(x, y, move) def ankles(self, x, h, edge="f", callback=None, move=None): @@ -91,7 +91,7 @@ class OttoLegs(Boxes): self.moveTo(3.45, 0, -90) self.polyline(0, (-264, 3.45), 0, 36, 6.55, 108, 0, (330, 9.0, 4), 0, 108, 6.55) self.move(20, 20, move) - + def ankle2(self): # from vertical edge @@ -148,4 +148,3 @@ class OttoLegs(Boxes): self.ankles(30, 25, callback=[None, self.ankle1], move="right") self.ankles(30, 25, callback=[None, self.ankle2], move="right") self.partsMatrix(2, 2, "right", self.servoring) - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ottosoles.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ottosoles.py index 0cc64c2..20e6608 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ottosoles.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ottosoles.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2018 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,13 @@ from boxes import * + class OttoSoles(Boxes): """Foam soles for the OttO bot""" ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser(x=58., y=38.) @@ -45,7 +45,7 @@ class OttoSoles(Boxes): w2 = w * 2**0.5 - c2 / 2 d = w * math.tan(math.radians(22.5)) - + self.edges["d"].settings.setValues(w, size=0.4, depth=0.3, radius=0.05) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/paintbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/paintbox.py index 0f4d155..3020d39 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/paintbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/paintbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,16 +13,24 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from boxes import Boxes, edges, boolarg +from boxes import Boxes, boolarg, edges class PaintStorage(Boxes): """Stackable storage for hobby paint or other things""" webinterface = True - ui_group = "Shelf" # see ./__init__.py for names + ui_group = "Shelf" # see ./__init__.py for names - def __init__(self): + canheight: int + candiameter: int + minspace: int + additional_bottom: int + additional_top: int + hexpattern: bool + drawer: bool + + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) @@ -40,6 +47,12 @@ class PaintStorage(Boxes): self.argparser.add_argument( "--minspace", action="store", type=int, default=10, help="Minimum space between the paintcans") + self.argparser.add_argument( + "--additional_bottom", action="store", type=boolarg, default=False, + help="Additional bottom/floor with holes the paintcans go through") + self.argparser.add_argument( + "--additional_top", action="store", type=boolarg, default=False, + help="Additional top/floor with holes the paintcans go through") self.argparser.add_argument( "--hexpattern", action="store", type=boolarg, default=False, help="Use hexagonal arrangement for the holes instead of orthogonal") @@ -48,31 +61,52 @@ class PaintStorage(Boxes): help="Create a stackable drawer instead") def paintholes(self): - "Place holes for the paintcans evenly" + """Place holes for the paintcans evenly""" if self.hexpattern: - self.moveTo(self.minspace/2, self.minspace/2) + self.moveTo(self.minspace / 2, self.minspace / 2) settings = self.hexHolesSettings settings.diameter = self.candiameter settings.distance = self.minspace settings.style = 'circle' - self.hexHolesRectangle(self.y - 1*self.minspace, - self.x - 1*self.minspace, + self.hexHolesRectangle(self.y - 1 * self.minspace, + self.x - 1 * self.minspace, settings) return - n_x = int(self.x / (self.candiameter+self.minspace)) - n_y = int(self.y / (self.candiameter+self.minspace)) + n_x = int(self.x / (self.candiameter + self.minspace)) + n_y = int(self.y / (self.candiameter + self.minspace)) if n_x <= 0 or n_y <= 0: return - spacing_x = (self.x - n_x*self.candiameter)/n_x - spacing_y = (self.y - n_y*self.candiameter)/n_y + spacing_x = (self.x - n_x * self.candiameter) / n_x + spacing_y = (self.y - n_y * self.candiameter) / n_y for i in range(n_y): for j in range(n_x): - self.hole(i * (self.candiameter+spacing_y) + (self.candiameter+spacing_y)/2, - j * (self.candiameter+spacing_x) + (self.candiameter+spacing_x)/2, - self.candiameter/2) + self.hole(i * (self.candiameter + spacing_y) + (self.candiameter + spacing_y) / 2, + j * (self.candiameter + spacing_x) + (self.candiameter + spacing_x) / 2, + self.candiameter / 2) + + def sidesCb(self): + x, y = self.x, self.y + t = self.thickness + + stack = self.edges['s'].settings + h = self.canheight - stack.height - stack.holedistance + t + hx = 1 / 2. * x + hh = h / 4. + hr = min(hx, hh) / 2 + + if not self.drawer: + self.rectangularHole(h / 3, (x / 2.0) - t, hh, hx, r=hr) + self.fingerHolesAt(((self.canheight/3)*2)-t*2, -t, x, 90) + + if self.additional_bottom: + self.fingerHolesAt((self.canheight / 6) - (t / 2), -t, x, 90) + if self.additional_top: + self.fingerHolesAt(self.canheight - ((self.canheight / 6) + t), -t, x, 90) + else: + self.rectangularHole(h / 3, (x / 2.0) - t, hh, hx, r=hr) def render(self): # adjust to the variables you want in the local scope @@ -82,25 +116,14 @@ class PaintStorage(Boxes): stack = self.edges['s'].settings h = self.canheight - stack.height - stack.holedistance + t - hx = 1/2.*x - hh = h/4. - hr = min(hx, hh) / 2 - + wall_callbacks = [self.sidesCb] if not self.drawer: wall_keys = "EsES" - wall_callbacks = [ - lambda: self.rectangularHole(h / 3, (x / 2.0) - t, hh, hx, r=hr), - lambda: self.fingerHolesAt(0, self.canheight / 3, x, 0), - ] bottom_keys = "EfEf" else: wall_keys = "FsFS" - wall_callbacks = [ - lambda: self.rectangularHole(h / 3, (x / 2.0) - t, hh, hx, r=hr) - ] bottom_keys = "FfFf" - # Walls self.rectangularWall( h, x - 2 * t, wall_keys, @@ -123,12 +146,16 @@ class PaintStorage(Boxes): # Bottom self.rectangularWall( - y, x-2*t, bottom_keys, ignore_widths=[1, 2, 5, 6], move="up" + y, x - 2 * t, bottom_keys, ignore_widths=[1, 2, 5, 6], move="up" ) if not self.drawer: # Top self.rectangularWall(y, x, "efef", callback=[self.paintholes], move="up") + if self.additional_bottom: + self.rectangularWall(y, x, "efef", callback=[self.paintholes], move="up") + if self.additional_top: + self.rectangularWall(y, x, "efef", callback=[self.paintholes], move="up") else: # Sides self.rectangularWall(y, h, "efff", move="up") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/paperbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/paperbox.py index 453378a..a5ea36c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/paperbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/paperbox.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- # Copyright (C) 2020 Guillaume Collic # # This program is free software: you can redistribute it and/or modify @@ -16,6 +14,7 @@ # along with this program. If not, see . import math + from boxes import Boxes @@ -30,12 +29,12 @@ class PaperBox(Boxes): This box is made of paper. There is marks in the "outside leftover paper" to help see where to fold -(cutting with tabs helps use them). The cut is very precise, and could be too tight if misaligned when glued. A plywood box (such as a simple TypeTray) of the same size is a great guide during folding and glueing. Just fold the box against it. Accurate quick and easy. +(cutting with tabs helps use them). The cut is very precise, and could be too tight if misaligned when glued. A plywood box (such as a simple TypeTray) of the same size is a great guide during folding and gluing. Just fold the box against it. Accurate quick and easy. A paper creaser (or bone folder) is also useful. """ - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser("x", "y", "h") @@ -49,7 +48,7 @@ A paper creaser (or bone folder) is also useful. ) self.argparser.add_argument( - "--lid_heigth", + "--lid_height", type=float, default=15, help="Height of the lid (part which goes inside the box)", @@ -120,7 +119,7 @@ A paper creaser (or bone folder) is also useful. 0, ] + self.lid_cut(lid_cut_length) - + self.lid(width - 2 * self.thickness) + + self.lid_tab(width - 2 * self.thickness) + [0] + self.lid_cut(lid_cut_length, reverse=True) + [ @@ -188,17 +187,17 @@ A paper creaser (or bone folder) is also useful. return ( self.side_with_finger_hole(width, self.finger_hole_diameter) + half_side - + self.lid(width) + + self.lid_tab(width) + list(reversed(half_side)) ) - def lid(self, width): + def lid_tab(self, width): return [ - self.lid_heigth - self.lid_radius, + self.lid_height - self.lid_radius, (90, self.lid_radius), width - 2 * self.lid_radius, (90, self.lid_radius), - self.lid_heigth - self.lid_radius, + self.lid_height - self.lid_radius, ] def mark(self, length): @@ -254,7 +253,7 @@ A paper creaser (or bone folder) is also useful. ] def ear_description(self, length, lid_cut_length, reverse=False): - ear_depth = max(lid_cut_length, self.lid_heigth) + ear_depth = max(lid_cut_length, self.lid_height) radius = min(self.lid_radius, ear_depth - lid_cut_length) start_margin = self.thickness end_margin = 2 * self.burn diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/phoneholder.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/phoneholder.py index 5427f47..3160224 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/phoneholder.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/phoneholder.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- # Copyright (C) 2021 Guillaume Collic # # This program is free software: you can redistribute it and/or modify @@ -17,6 +15,7 @@ import math from functools import partial + from boxes import Boxes, edges @@ -35,7 +34,7 @@ class PhoneHolder(Boxes): Default values are currently based on Galaxy S7. """ - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.argparser.add_argument( "--phone_height", @@ -213,7 +212,7 @@ class PhoneHolder(Boxes): class BottomEdge(edges.BaseEdge): - def __init__(self, boxes, support_start_height, support_spacing): + def __init__(self, boxes, support_start_height, support_spacing) -> None: super().__init__(boxes, None) self.support_start_height = support_start_height self.support_spacing = support_spacing @@ -241,7 +240,7 @@ class BottomEdge(edges.BaseEdge): class SideEdge(edges.BaseEdge): - def __init__(self, boxes, tab_start, tab_length, reverse=False): + def __init__(self, boxes, tab_start, tab_length, reverse=False) -> None: super().__init__(boxes, None) self.tab_start = tab_start self.tab_length = tab_length @@ -265,12 +264,12 @@ class SideEdge(edges.BaseEdge): self.polyline(0, -90, self.thickness, 90) self.edges["F"](tab_end) - def startwidth(self): + def startwidth(self) -> float: return self.boxes.thickness class TabbedEdge(edges.BaseEdge): - def __init__(self, boxes, tab_start, tab_length, tab_depth, reverse=False): + def __init__(self, boxes, tab_start, tab_length, tab_depth, reverse=False) -> None: super().__init__(boxes, None) self.tab_start = tab_start self.tab_length = tab_length @@ -302,5 +301,5 @@ class TabbedEdge(edges.BaseEdge): ) self.edges["f"](tab_end) - def margin(self): + def margin(self) -> float: return self.tab_depth + self.thickness diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/piratechest.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/piratechest.py new file mode 100644 index 0000000..c3da958 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/piratechest.py @@ -0,0 +1,134 @@ +# Copyright (C) 2013-2016 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class PirateChest(Boxes): + """Box with polygon lid with chest hinges.""" + + description = """Do not assemble sides before attaching the lid! + Hinge of the lid has to be placed first because it is impossible + to get it in position without removing the side wall. The lid can + be a bit tricky to assemble. Keep track of how the parts fit together. + Part with label "lid back" is placed in the hinges""" + + ui_group = "Box" + + def __init__(self) -> None: + Boxes.__init__(self) + self.addSettingsArgs(edges.FingerJointSettings, finger=1.0,space=1.0) + self.addSettingsArgs(edges.HingeSettings) + + self.buildArgParser("x", "y", "h", "outside") + self.argparser.add_argument( + "--n", action="store", type=int, default=5, + help="number of sides on the lid. n ≥ 3") + + + def render(self): + # adjust to the variables you want in the local scope + x, y, h = self.x, self.y, self.h + if self.outside: + x = self.adjustSize(x) + y = self.adjustSize(y) + h = self.adjustSize(h, "f", False) + t = self.thickness + n = self.n + + if (n < 3): + raise ValueError("number of sides on the lid must be greater or equal to 3 (got %i)" % n) + + hy = self.edges["O"].startwidth() + h -= hy + if (h < 0): + raise ValueError("box to low to allow for hinge (%i)" % h) + + # create edge for non 90 degree joints in the lid + fingerJointSettings = copy.deepcopy(self.edges["f"].settings) + fingerJointSettings.setValues(self.thickness, angle=180./(n-1)) + fingerJointSettings.edgeObjects(self, chars="gGH") + + # render all parts + self.ctx.save() + + self.rectangularWall(x, y, "FFFF", move="up", label="Bottom") + frontlid, toplids, backlid = self.topside(y, n = n, move="only", bottom='P') + + self.rectangularWall(x, backlid, "qFgF", move="up", label="lid back") + for _ in range(n-2): + self.rectangularWall(x, toplids, "GFgF", move="up", label="lid top") + self.rectangularWall(x, frontlid, "GFeF", move="up", label="lid front") + + self.ctx.restore() + self.rectangularWall(x, y, "FFFF", move="right only") + + with self.saved_context(): + self.rectangularWall(x, h, "fFQF", ignore_widths=[2, 5], move="right", label="front") + self.rectangularWall(y, h, "ffof", ignore_widths=[5], move="right", label="right") + self.rectangularWall(0, h, "eeep", move="right only") + self.rectangularWall(x, h, "fFoF", move="up only") + self.rectangularWall(x, 0, "Peee", move="up only") + + e1 = edges.CompoundEdge(self, "Fe", (h, hy)) + e2 = edges.CompoundEdge(self, "eF", (hy, h)) + e_back = ("f", e1, "e", e2) + + with self.saved_context(): + self.rectangularWall(x, h+hy, e_back, move="right", label="back") # extend back to correct height + self.rectangularWall(0, h, "ePee", move="right only") + self.rectangularWall(y, h, "ffOf", ignore_widths=[2], move="right", label="left") + self.rectangularWall(x, h, "fFOF", move="up only") + self.rectangularWall(x, 0, "peee", move="up only") + + self.topside(y, n = n, move="right", bottom='p', label="lid left") + self.topside(y, n = n, move="right", bottom='P', label="lid right") + + + def topside(self, y, n, bottom, move=None, label=""): + radius, hp, side = self.regularPolygon((n - 1) * 2, h=y/2.0) + + tx = y + 2 * self.edges.get('f').spacing() + lidheight = hp if n % 2 else radius + ty = lidheight + self.edges.get('f').spacing() + self.edges.get(bottom).spacing() + + if self.move(tx, ty, move, before=True): + return side/2 + self.edges.get(bottom).spacing(), side, side/2 + + self.moveTo(self.edges.get('f').margin(), self.edges.get(bottom).margin()) + + self.edges.get(bottom)(y) + + self.corner(90) + if bottom == 'p': + self.edges.get('f')(side/2 + self.edges.get(bottom).spacing()) + else: + self.edges.get('f')(side/2) + + self.corner(180 / (n - 1)) + for _ in range(n-2): + self.edges.get('f')(side) + self.corner(180 / (n - 1)) + + if bottom == 'P': + self.edges.get('f')(side/2 + self.edges.get(bottom).spacing()) + else: + self.edges.get('f')(side/2) + + self.corner(90) + + self.move(tx, ty, move, label=label) + + return side/2 + self.edges.get(bottom).spacing(), side, side/2 diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/pizzashovel.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/pizzashovel.py new file mode 100644 index 0000000..8038c61 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/pizzashovel.py @@ -0,0 +1,100 @@ +# Copyright (C) 2013-2016 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class PizzaShovel(Boxes): + """Pizza shovel with conveyor belt action""" + + description = """ +You need (permanent) baking paper to create the conveyor. With that you can pick up and put down the pizza by moving the handle attached to the belt. + """ + + ui_group = "Misc" + + def __init__(self) -> None: + Boxes.__init__(self) + + self.addSettingsArgs(edges.FingerJointSettings) + self.addSettingsArgs(edges.HandleEdgeSettings, outset=0.0, height=40, hole_width="30:30:30") + self.buildArgParser(x=382, y=400) + self.argparser.add_argument( + "--grip_length", action="store", type=float, default=250.0, + help="Length of the grip. Zero for holes for a screw-in handle") + self.argparser.add_argument( + "--grip_height", action="store", type=float, default=30.0, + help="Height of the grip. Distance between the cross beams.") + self.argparser.add_argument( + "--top_holes", action="store", type=float, default=3.0, + help="Diameter of the screw holes in the bottom of the pusher - where the screws pass through") + self.argparser.add_argument( + "--bottom_holes", action="store", type=float, default=2.0, + help="Diameter of the screw holes in the bottom of the pusher - where the screws hold") + self.argparser.add_argument( + "--grip_holes", action="store", type=float, default=3.0, + help="Diameter of the screw holes for zero griplength") + + def holesCB(self, d): + def cb(): + for i in range(5): + self.hole((self.x-3)/5 * (i+0.5), 20, d=d) + return cb + + def gripCB(self, top): + + def cb(): + t = self.thickness + if self.grip_length: + for d in (-t, +t): + self.fingerHolesAt(self.x/2 + d, 0, 40, 90) + else: + for y in ((10, 30) if top else (15, 35, 60)): + self.hole(self.x/2, y, d=self.grip_holes) + return cb + + def render(self): + x, y, h = self.x, self.y, self.grip_height + grip = self.grip_length + t = self.thickness + + ce = edges.CompoundEdge(self, "fe", [y/2, y/2]) + ec = edges.CompoundEdge(self, "ef", [y/2, y/2]) + + self.rectangularWall(x, y, ["e", ce, "e", ec], move="up") + self.rectangularWall(x, 40, "efef", callback=[self.gripCB(top=True)], move="up") + self.rectangularWall(x, 80, "efef", callback=[self.gripCB(top=False)], move="up") + for i in range(2): + a = math.atan((h+2*t) / (y/2 - 30)) + l = (y/2 - 30) / math.cos(a) + a = math.degrees(a) + self.polygonWall((y/2+40, (90, t), h+2*t, (90, t), 70, a, l, -a, 0, (180, t)), "e", + callback=[lambda: (self.fingerHolesAt(0, 1.5*t, y/2, 0), + self.fingerHolesAt(y/2+t, 1.5*t, 40, 0)), + None, + lambda: self.fingerHolesAt(-t, 1.5*t, 80, 0)], + move="up") + + self.rectangularWall(x-3, 40, "eeee", callback=[self.holesCB(self.bottom_holes)], move="up") + self.rectangularWall(x-3, 40, "yeee", callback=[self.holesCB(self.top_holes)], move="up") + + if grip: + ce1 = edges.CompoundEdge(self, "fe", (40, grip-h/2)) + ce2 = edges.CompoundEdge(self, "ef", (grip-h/2, 40)) + self.flangedWall(40+grip-h/2, h, [ce1, "e", ce2, "e"], flanges=[0, h/2], r=h/2, move="up") + self.flangedWall(40+grip-h/2, h, "eeee", flanges=[0, h/2], r=h/2, move="up") + self.flangedWall(40+grip-h/2, h, [ce1, "e", ce2, "e"], flanges=[0, h/2], r=h/2, move="up") + self.flangedWall(30+grip-h/2, h-2*t, "eeee", flanges=[0, h/2-t], r=h/2-t, move="up") + self.flangedWall(30+grip-h/2, h-2*t, "eeee", flanges=[0, h/2-t], r=h/2-t, move="up") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/planetary.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/planetary.py index ff0b029..eb92c3f 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/planetary.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/planetary.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,16 +14,14 @@ # along with this program. If not, see . from boxes import * -import math class Planetary(Boxes): - """Planetary Gear with possibly multiple identical stages""" ui_group = "Part" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.argparser.add_argument( "--sunteeth", action="store", type=int, default=8, @@ -102,6 +99,3 @@ class Planetary(Boxes): self.gears(teeth=self.planetteeth, dimension=self.modulus, angle=pressure_angle, mount_hole=self.shaft, profile_shift=profile_shift, move="up") - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/planetary2.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/planetary2.py index 230744a..c6c5928 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/planetary2.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/planetary2.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,23 +14,22 @@ # along with this program. If not, see . from boxes import * -import math + class Planetary2(Boxes): - """Balanced force Difference Planetary Gear (not yet working properly)""" ui_group = "Unstable" description = """Still has issues. The middle planetary gears set must not have a mashing sun gear as it can't be a proper gear set.""" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser("nema_mount") self.argparser.add_argument( "--profile", action="store", type=str, default="GT2_2mm", choices=pulley.Pulley.getProfiles(), - help="profile of the teeth/belt") + help="profile of the teeth/belt") self.argparser.add_argument( "--sunteeth", action="store", type=int, default=20, help="number of teeth on sun gear") @@ -46,7 +44,7 @@ class Planetary2(Boxes): help="enable secondary ring with given delta to the ring gear") self.argparser.add_argument( "--modulus", action="store", type=float, default=1.0, - help="modulus of the theeth in mm") + help="modulus of the teeth in mm") self.argparser.add_argument( "--shaft", action="store", type=float, default=6., help="diameter of the shaft") @@ -132,7 +130,7 @@ class Planetary2(Boxes): def planets(): self.moveTo(size3/2, size3/2) for angle in planetpositions: - angle += 180 # compensate for 3 postion in callback + angle += 180 # compensate for 3 position in callback self.moveTo(0, 0, angle) self.hole((pitch1+pitch2), 0, size2/2) self.moveTo(0, 0, -angle) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/platonic.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/platonic.py index b23f751..56fd0d0 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/platonic.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/platonic.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2020 Norbert Szulc # # This program is free software: you can redistribute it and/or modify @@ -15,9 +14,8 @@ # along with this program. If not, see . from boxes import * -from boxes.edges import FingerJointBase, FingerJointEdge +from boxes.edges import FingerJointEdge -from math import sin, pi class UnevenFingerJointEdge(FingerJointEdge): """Uneven finger joint edge """ @@ -43,7 +41,7 @@ class UnevenFingerJointEdge(FingerJointEdge): leftover -= play shift = (f + s) / 2 # we shift all fingers to make them un even - if (leftover < shift): + if (leftover < shift): leftover = shift self.edge((leftover + shift)/2, tabs=1) # Whole point of this class @@ -77,7 +75,7 @@ class UnevenFingerJointEdge(FingerJointEdge): self.edge((leftover - shift)/2, tabs=1) # Whole point of this class # Unstable -class UnevenFingerJointEdgeCounterPart(UnevenFingerJointEdge): +class UnevenFingerJointEdgeCounterPart(UnevenFingerJointEdge): """Uneven finger joint edge - other side""" char = 'U' description = "Uneven Finger Joint (opposing side)" @@ -98,7 +96,7 @@ class Platonic(Boxes): "icosahedro": (20, 3), } - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=0) @@ -124,5 +122,3 @@ class Platonic(Boxes): for _ in range(faces): self.regularPolygonWall(corners, side=e, edges="u", move="right") - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/polehook.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/polehook.py index 27b1f8c..3a3c687 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/polehook.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/polehook.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,10 +15,11 @@ from boxes import * + class PoleHook(Boxes): # change class name here and below """Hook for pole like things to be clamped to another pole""" - - def __init__(self): + + def __init__(self) -> None: Boxes.__init__(self) # Uncomment the settings for the edge types you use @@ -51,7 +51,7 @@ class PoleHook(Boxes): # change class name here and below e = self.edges.get(edge, edge) - + self.moveTo(0, e.margin()) if e is self.edges["e"]: @@ -65,7 +65,7 @@ class PoleHook(Boxes): # change class name here and below else: self.polyline(0, 90, d, 90, w, 90, 0, (-180, d/2), 0.5*d, (180, w/2), 1.5 * d, 90) - + self.move(tw, th, move) def lock(self, l1, l2, w, move=None): @@ -113,11 +113,11 @@ class PoleHook(Boxes): # change class name here and below self.parts.disc(d+2*ww, hole=self.screw, move="right") self.parts.disc(d+2*ww, callback=self.clamp, hole=self.screw+0.5*t, move="right") self.parts.disc(d+2*ww, hole=self.screw+0.5*t, move="right") - self.parts.waivyKnob(50, callback=lambda:self.nutHole(self.screwhead), + self.parts.wavyKnob(50, callback=lambda:self.nutHole(self.screwhead), move="right") - self.parts.waivyKnob(50, callback=lambda:self.nutHole(self.screwhead), + self.parts.wavyKnob(50, callback=lambda:self.nutHole(self.screwhead), move="right") - self.parts.waivyKnob(50, hole=self.screw+0.5*t, move="right") + self.parts.wavyKnob(50, hole=self.screw+0.5*t, move="right") ll = ((d**2 + (0.5*(d+ww))**2)**0.5) - 0.5 * d for i in range(3): @@ -125,5 +125,3 @@ class PoleHook(Boxes): # change class name here and below for i in range(2): self.parts.disc(ww, move="up") - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/pulley.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/pulley.py index 2be87c9..e76aabd 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/pulley.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/pulley.py @@ -1,4 +1,3 @@ -#!/usr/bin/python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,7 +15,6 @@ from boxes import * from boxes import pulley -import math class Pulley(Boxes): @@ -24,7 +22,7 @@ class Pulley(Boxes): ui_group = "Part" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) # remove cli params you do not need self.buildArgParser(h=6.) @@ -77,6 +75,3 @@ class Pulley(Boxes): for i in range(int(math.ceil(self.h / self.thickness))): self.pulley(self.teeth, self.profile, insideout=self.insideout, r_axle=self.axle / 2.0, move="right") - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rack10box.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rack10box.py index e38d212..edd864d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rack10box.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rack10box.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2018 Sebastian Reichel # # This program is free software: you can redistribute it and/or modify @@ -14,9 +13,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from boxes import * from boxes.generators.rack19box import Rack19Box + class Rack10Box(Rack19Box): """Closed box with screw on top for mounting in a 10" rack.""" diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rack19box.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rack19box.py index 97a6e2a..c4a85ae 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rack19box.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rack19box.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2018 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -22,7 +21,7 @@ class Rack19Box(Boxes): ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=0.5) self.argparser.add_argument( @@ -46,22 +45,22 @@ class Rack19Box(Boxes): t = self.thickness self.fingerHolesAt(0, self.h-1.5*t, self.triangle, 0) self.fingerHolesAt(self.x, self.h-1.5*t, self.triangle, 180) - + def wallxfCB(self): # front t = self.thickness for x in (8.5, self.x+2*17.+2*t-8.5): for y in (6., self.h-6.+t): self.rectangularHole(x, y, 10, 6.5, r=3.25) - + self.moveTo(t+17., t) self.wallxCB() - + def wallyCB(self): t = self.thickness self.fingerHolesAt(0, self.h-1.5*t, self.triangle, 0) self.fingerHolesAt(self.y, self.h-1.5*t, self.triangle, 180) - - + + def _render(self, type): t = self.thickness @@ -77,17 +76,18 @@ class Rack19Box(Boxes): tr = self.triangle trh = tr / 3. - self.rectangularWall(y, h, "ffef", callback=[self.wallyCB], move="right") - self.rectangularWall(x, h, "fFeF", callback=[self.wallxCB], - move="up") - self.flangedWall(x, h, "FFeF", callback=[self.wallxfCB], r=t, - flanges=[0., 17., -t, 17.]) self.rectangularWall(y, h, "ffef", callback=[self.wallyCB], - move="left up") + move="right", label="right") + self.flangedWall(x, h, "FFEF", callback=[self.wallxfCB], r=t, + flanges=[0., 17., -t, 17.], move="up", label="front") + self.rectangularWall(x, h, "fFeF", callback=[self.wallxCB], + label="back") + self.rectangularWall(y, h, "ffef", callback=[self.wallyCB], + move="left up", label="left") - self.rectangularWall(x, y, "fFFF", move="right") + self.rectangularWall(x, y, "fFFF", move="up", label="bottom") self.rectangularWall(x, y, callback=[ - lambda:self.hole(trh, trh, d=d2)] * 4, move='up') + lambda:self.hole(trh, trh, d=d2)] * 4, move='right', label="lid") self.rectangularTriangle(tr, tr, "ffe", num=4, callback=[None, lambda: self.hole(trh, trh, d=d1)]) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rack19halfwidth.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rack19halfwidth.py index 3012091..214c384 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rack19halfwidth.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rack19halfwidth.py @@ -1,14 +1,14 @@ """Half 19inch rack unit for musical equipment.""" from boxes import Boxes -from boxes.edges import Edge + class Rack19HalfWidth(Boxes): """Half width 19inch rack unit for musical equipment.""" ui_group = "Box" - def __init__(self): + def __init__(self) -> None: super().__init__() self.argparser.add_argument( '--ru_count', action='store', type=float, default=1, diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rackbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rackbox.py index 3d40d15..a495f7c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rackbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rackbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2017 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -22,7 +21,7 @@ class RackBox(Boxes): ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=1.2) self.buildArgParser("x", "y", "h", "outside") @@ -46,33 +45,33 @@ class RackBox(Boxes): t = self.thickness self.fingerHolesAt(0, self.h-1.5*t, self.triangle, 0) self.fingerHolesAt(self.x, self.h-1.5*t, self.triangle, 180) - + def wallxfCB(self): # front t = self.thickness hd = self.holedist for x in (hd, self.x+3*hd+2*t): for y in (hd, self.h-hd+t): self.hole(x, y, self.d3/2.) - + self.moveTo(t+2*hd, t) self.wallxCB() - + def wallyCB(self): t = self.thickness self.fingerHolesAt(0, self.h-1.5*t, self.triangle, 0) self.fingerHolesAt(self.y, self.h-1.5*t, self.triangle, 180) - - + + def render(self): t = self.thickness self.h = h = self.h + 2*t # compensate for lid x, y, h = self.x, self.y, self.h d1, d2, d3 =self.d1, self.d2, self.d3 - hd = self.holedist + hd = self.holedist tr = self.triangle trh = tr / 3. - + if self.outside: self.x = x = self.adjustSize(x) self.y = y = self.adjustSize(y) @@ -92,6 +91,3 @@ class RackBox(Boxes): self.rectangularTriangle(tr, tr, "ffe", num=4, callback=[None, lambda: self.hole(trh, trh, d=d1)]) - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rectangularWall.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rectangularWall.py index 8806ddf..97d425c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rectangularWall.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rectangularWall.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,23 +15,24 @@ from boxes import * + class RectangularWall(Boxes): - """Simple wall""" + """Simple wall with options for different edges""" ui_group = "Part" # see ./__init__.py for names - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.CabinetHingeSettings) - self.addSettingsArgs(edges.ClickSettings) - self.addSettingsArgs(edges.DoveTailSettings) + self.addSettingsArgs(edges.ClickSettings) + self.addSettingsArgs(edges.DoveTailSettings) self.addSettingsArgs(edges.FingerJointSettings) self.addSettingsArgs(edges.GearSettings) self.addSettingsArgs(edges.GripSettings) self.addSettingsArgs(edges.HingeSettings) self.addSettingsArgs(edges.ChestHingeSettings) - self.addSettingsArgs(edges.LidSettings) + self.addSettingsArgs(edges.SlideOnLidSettings) self.addSettingsArgs(edges.StackableSettings) self.buildArgParser(x=100, h=100) @@ -53,7 +53,7 @@ class RectangularWall(Boxes): "--left_edge", action="store", type=ArgparseEdgeType("cCdDeEfFghiIjJkKlLmMnNoOpPqQRsSšŠuUvV"), choices=list("cCdDeEfFghiIjJkKlLmMnNoOpPqQRsSšŠuUvV"), default="e", help="edge type for left edge") - + def cb(self, nr): t = self.thickness @@ -68,4 +68,3 @@ class RectangularWall(Boxes): self.moveTo(3*t, 3*t) self.rectangularWall(self.x, self.h, self.edgetypes, callback=self.cb) - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/regularbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/regularbox.py index 7244749..0bdce89 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/regularbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/regularbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,7 +15,7 @@ from boxes import * from boxes.generators.bayonetbox import BayonetBox -import copy + class RegularBox(BayonetBox): """Box with regular polygon as base""" @@ -25,35 +24,43 @@ class RegularBox(BayonetBox): The lids needs to be glued. For the bayonet lid all outside rings attach to the bottom, all inside rings to the top. """ - + ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=1) self.buildArgParser("h", "outside") self.argparser.add_argument( - "--radius", action="store", type=float, default=50.0, - help="inner radius if the box (at the corners)") + "--radius_bottom", action="store", type=float, default=50.0, + help="inner radius of the box bottom (at the corners)") + self.argparser.add_argument( + "--radius_top", action="store", type=float, default=50.0, + help="inner radius of the box top (at the corners)") self.argparser.add_argument( "--n", action="store", type=int, default=5, help="number of sides") self.argparser.add_argument( "--top", action="store", type=str, default="none", - choices=["none", "hole", "angled hole", "angled lid", "angled lid2", "round lid", "bayonet mount"], + choices=["none", "hole", "angled hole", "angled lid", "angled lid2", "round lid", "bayonet mount", "closed"], help="style of the top and lid") self.argparser.add_argument( "--alignment_pins", action="store", type=float, default=1.0, help="diameter of the alignment pins for bayonet lid") + self.argparser.add_argument( + "--bottom", action="store", type=str, default="closed", + choices=["none", "closed", "hole", "angled hole", "angled lid", "angled lid2", "round lid"], + help="style of the bottom and bottom lid") self.lugs=6 def render(self): - r, h, n = self.radius, self.h, self.n + r0, r1, h, n = self.radius_bottom, self.radius_top, self.h, self.n if self.outside: - r = r = r - self.thickness / math.cos(math.radians(360/(2*n))) + r0 = r0 - self.thickness / math.cos(math.radians(360/(2*n))) + r1 = r1 - self.thickness / math.cos(math.radians(360/(2*n))) if self.top == "none": h = self.adjustSize(h, False) elif "lid" in self.top and self.top != "angled lid": @@ -63,27 +70,52 @@ The lids needs to be glued. For the bayonet lid all outside rings attach to the t = self.thickness + + r0, sh0, side0 = self.regularPolygon(n, radius=r0) + r1, sh1, side1 = self.regularPolygon(n, radius=r1) + + # length of side edges + #l = (((side0-side1)/2)**2 + (sh0-sh1)**2 + h**2)**0.5 + l = ((r0-r1)**2 + h**2)**.5 + # angles of sides -90° aka half of top angle of the full pyramid sides + a = math.degrees(math.asin((side1-side0)/2/l)) + # angle between sides (in boxes style change of travel) + phi = 180 - 2 * math.degrees( + math.asin(math.cos(math.pi/n) / math.cos(math.radians(a)))) + fingerJointSettings = copy.deepcopy(self.edges["f"].settings) - fingerJointSettings.setValues(self.thickness, angle=360./n) + fingerJointSettings.setValues(self.thickness, angle=phi) fingerJointSettings.edgeObjects(self, chars="gGH") - r, sh, side = self.regularPolygon(n, radius=r) + beta = math.degrees(math.atan((sh1-sh0)/h)) + angle_bottom = 90 + beta + angle_top = 90 - beta - with self.saved_context(): - self.regularPolygonWall(corners=n, r=r, edges='F', move="right") - if self.top == "angled lid": + fingerJointSettings = copy.deepcopy(self.edges["f"].settings) + fingerJointSettings.setValues(self.thickness, angle=angle_bottom) + fingerJointSettings.edgeObjects(self, chars="yYH") + + fingerJointSettings = copy.deepcopy(self.edges["f"].settings) + fingerJointSettings.setValues(self.thickness, angle=angle_top) + fingerJointSettings.edgeObjects(self, chars="zZH") + + + def drawTop(r, sh, top_type, joint_type): + if top_type == "closed": + self.regularPolygonWall(corners=n, r=r, edges=joint_type[1], move="right") + elif top_type == "angled lid": self.regularPolygonWall(corners=n, r=r, edges='e', move="right") self.regularPolygonWall(corners=n, r=r, edges='E', move="right") - elif self.top in ("angled hole", "angled lid2"): - self.regularPolygonWall(corners=n, r=r, edges='F', move="right", + elif top_type in ("angled hole", "angled lid2"): + self.regularPolygonWall(corners=n, r=r, edges=joint_type[1], move="right", callback=[lambda:self.regularPolygonAt( 0, 0, n, h=sh-t)]) - if self.top == "angled lid2": + if top_type == "angled lid2": self.regularPolygonWall(corners=n, r=r, edges='E', move="right") - elif self.top in ("hole", "round lid"): - self.regularPolygonWall(corners=n, r=r, edges='F', move="right", + elif top_type in ("hole", "round lid"): + self.regularPolygonWall(corners=n, r=r, edges=joint_type[1], move="right", hole=(sh-t)*2) - if self.top == "round lid": + if top_type == "round lid": self.parts.disc(sh*2, move="right") if self.top == "bayonet mount": self.diameter = 2*sh @@ -93,23 +125,43 @@ The lids needs to be glued. For the bayonet lid all outside rings attach to the callback=[self.upperCB], move="right") self.parts.disc(sh*2, move="right") - self.regularPolygonWall(corners=n, r=r, edges='F', move="up only") - side = 2 * math.sin(math.radians(180.0/n)) * r - fingers = self.top in ("hole", "angled hole", "round lid", - "angled lid2", "bayonet mount") - + with self.saved_context(): + drawTop(r0, sh0, self.bottom, "yY") + drawTop(r1, sh1, self.top, "zZ") + + self.regularPolygonWall(corners=n, r=max(r0, r1), edges='F', move="up only") + + fingers_top = self.top in ("closed", "hole", "angled hole", + "round lid", "angled lid2", "bayonet mount") + fingers_bottom = self.bottom in ("closed", "hole", "angled hole", + "round lid", "angled lid2") + + t_ = self.edges["G"].startwidth() + bottom_edge = ('y' if fingers_bottom else 'e') + top_edge = ('z' if fingers_top else 'e') + d_top = max(0, -t_ * math.sin(math.radians(a))) + d_bottom = max(0.0, t_ * math.sin(math.radians(a))) + l -= (d_top + d_bottom) + if n % 2: + e = bottom_edge + 'ege' + top_edge + 'eeGee' + borders = [side0, 90-a, d_bottom, 0, l, 0, d_top, 90+a, side1, + 90+a, d_top, -90, t_, 90, l, 90, t_, -90, d_bottom, 90-a] for i in range(n): - self.rectangularWall(side, h, move="right", - edges="fgfG" if fingers else "fgeG") + self.polygonWall(borders, edge=e, correct_corners=False, + move="right") else: + borders0 = [side0, 90-a, + d_bottom, -90, t_, 90, l, 90, t_, -90, d_top, + 90+a, side1, 90+a, + d_top, -90, t_, 90, l, 90, t_, -90, d_bottom, 90-a] + e0 = bottom_edge + 'eeGee' + top_edge + 'eeGee' + borders1 = [side0, 90-a, d_bottom, 0, l, 0, d_top, 90+a, side1, + 90+a, d_top, 0, l, 0, d_bottom, 90-a] + e1 = bottom_edge + 'ege' + top_edge + 'ege' for i in range(n//2): - self.rectangularWall(side, h, move="right", - edges="fGfG" if fingers else "fGeG") - self.rectangularWall(side, h, move="right", - edges="fgfg" if fingers else "fgeg") - - - - + self.polygonWall(borders0, edge=e0, correct_corners=False, + move="right") + self.polygonWall(borders1, edge=e1, correct_corners=False, + move="right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/regularstarbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/regularstarbox.py index 60e1d91..7ba4b2c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/regularstarbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/regularstarbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,7 +14,7 @@ # along with this program. If not, see . from boxes import * -import copy + class SlotEdge(edges.Edge): @@ -24,21 +23,21 @@ class SlotEdge(edges.Edge): r, h = self.settings.radius, self.settings.h sh = self.settings.sh # distance side to center - li = 2 * sh * math.tan(math.radians(90/n)) # side inner 2x polygone + li = 2 * sh * math.tan(math.radians(90/n)) # side inner 2x polygon ls2 = t / math.tan(math.radians(180/n)) ls1 = t / math.cos(math.radians(90-(180/n))) lo = (length-li-2*ls1)/2 li = li - 2*ls2 # correct for overlap of wall - + d = h/2 - + if li > 0: poly = [lo-1, (90, 1), d+t-1, -90, ls1+ls2, -90, d-t, (90, t)] self.polyline(*(poly + [li-2*t] + list(reversed(poly)))) - def startwidth(self): + def startwidth(self) -> float: return self.settings.thickness @@ -50,7 +49,7 @@ class RegularStarBox(Boxes): description = """![Open box](static/samples/RegularStarBox-2.jpg)""" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.buildArgParser("h", "outside") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/robotarm.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/robotarm.py index 10d0ae0..9f8ac6d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/robotarm.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/robotarm.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2017 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -17,12 +16,13 @@ from boxes import * from boxes import robot, servos + class RobotArm(Boxes): # change class name here and below """Segments of servo powered robot arm""" ui_group = "Part" - - def __init__(self): + + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) @@ -56,4 +56,3 @@ class RobotArm(Boxes): # change class name here and below servoClsA = getattr(servos, servoA) servoClsB = getattr(servos, servoB) armcls(self, servoClsA(self), servoClsB(self))(length, move="up") - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rollholder.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rollholder.py new file mode 100644 index 0000000..8581f1d --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rollholder.py @@ -0,0 +1,133 @@ +# Copyright (C) 2013-2016 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class RollHolder(Boxes): + """Holder for kitchen rolls or other rolls""" + + description = """Needs a dowel or pipe as axle.""" + + ui_group = "WallMounted" + + def __init__(self) -> None: + Boxes.__init__(self) + + self.addSettingsArgs(edges.FingerJointSettings) + self.argparser.add_argument( + "--width", action="store", type=float, default=275, + help="length of the axle in mm") + self.argparser.add_argument( + "--diameter", action="store", type=float, default=120, + help="maximum diameter of the roll in mm (choose generously)") + self.argparser.add_argument( + "--height", action="store", type=float, default=80, + help="height of mounting plate in mm") + self.argparser.add_argument( + "--axle", action="store", type=float, default=25, + help="diameter of the axle in mm including play") + self.argparser.add_argument( + "--screw_holes", action="store", type=float, default=4, + help="diameter of mounting holes in mm") + self.argparser.add_argument( + "--one_piece", action="store", type=boolarg, default=True, + help="have a continuous back plate instead of two separate holders") + + def side(self, move=None): + d = self.diameter + a = self.axle + h = self.height + t = self.thickness + + tw, th = h, (d + a) / 2 + 4 * t + + if self.move(tw, th, move, True): + return + + self.moveTo(0, t) + self.edges["f"](h) + self.fingerHolesAt(-(a/2+3*t), self.burn, d/2, 90) + self.polyline(0, 90, d/2, (90, a/2 + 3*t)) + + r = a/2 + 3*t + a = math.atan2(float(d/2), (h-a-6*t)) + alpha = math.degrees(a) + + self.corner(alpha, r) + self.edge(((h-2*r)**2+(d/2)**2)**0.5) + self.corner(90-alpha, r) + self.corner(90) + + self.move(tw, th, move) + + def backCB(self): + t = self.thickness + a = self.axle + h = self.height + w = self.width + + plate = w + 2*t + h/2 if self.one_piece else h/2 + t + + self.fingerHolesAt(h/4+t/2-3*t, 0, h, 90) + self.fingerHolesAt(h/4-3*t, h-3*t-a/2, h/4, 180) + + if self.one_piece: + self.fingerHolesAt(h/4+t/2+t-3*t+w, 0, h, 90) + self.fingerHolesAt(h/4+2*t-3*t+w, h-3*t-a/2, h/4, 0) + + for x in (0, plate-6*t): + for y in (3*t, h-3*t): + self.hole(x, y, d=self.screw_holes) + + def rings(self): + a = self.axle + r = a/2 + t = self.thickness + + self.moveTo(0, a+1.5*t, -90) + for i in range(2): + self.polyline(r-1.5*t, (180, r+3*t), 0, (180, 1.5*t), 0, + (-180, r), r-1.5*t, (180, 1.5*t)) + self.moveTo(a-t, a+12*t, 180) + + + def render(self): + t = self.thickness + w = self.width + d = self.diameter + a = self.axle + h = self.height + + self.height = h = max(h, a+10*t) + + self.side(move="right") + self.side(move="right") + + self.rectangularTriangle(h/4, d/2, "ffe", num=2, r=3*t, move="right") + + if self.one_piece: + self.roundedPlate(w+h/2+2*t, h, edge="e", r=3*t, + extend_corners=False, + callback=[self.backCB], move="right") + else: + self.roundedPlate(h/2+t, h, edge="e", r=3*t, + extend_corners=False, + callback=[self.backCB], move="right") + self.roundedPlate(h/2+t, h, edge="e", r=3*t, + extend_corners=False, + callback=[self.backCB], move="right mirror") + + self.rings() diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rotary.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rotary.py index 0816d1d..a19e5d2 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rotary.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/rotary.py @@ -1,4 +1,3 @@ -#!/usr/bin/python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -18,8 +17,8 @@ from boxes import * class MotorEdge(edges.BaseEdge): - # def margin(self): - # return 30 + # def margin(self) -> float: + # return 30.0 def __call__(self, l, **kw): self.polyline( l - 165, 45, @@ -30,15 +29,15 @@ class MotorEdge(edges.BaseEdge): class OutsetEdge(edges.OutSetEdge): - def startwidth(self): - return 20 + def startwidth(self) -> float: + return 20.0 class HangerEdge(edges.BaseEdge): char = "H" - def margin(self): - return 40 + def margin(self) -> float: + return 40.0 def __call__(self, l, **kw): self.fingerHolesAt(0, -0.5 * self.thickness, l, angle=0) @@ -55,8 +54,8 @@ class HangerEdge(edges.BaseEdge): class RollerEdge(edges.BaseEdge): - def margin(self): - return 20 + def margin(self) -> float: + return 20.0 def __call__(self, l, **kw): m = 40 + 100 @@ -68,7 +67,7 @@ class RollerEdge(edges.BaseEdge): class RollerEdge2(edges.BaseEdge): - def margin(self): + def margin(self) -> float: return self.thickness def __call__(self, l, **kw): @@ -80,12 +79,11 @@ class RollerEdge2(edges.BaseEdge): class Rotary(Boxes): - """Rotary Attachment for engraving cylindrical objects in a laser cutter""" ui_group = "Unstable" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) @@ -101,7 +99,7 @@ class Rotary(Boxes): help="diameter of the axles") self.argparser.add_argument( "--knifethickness", action="store", type=float, default=8., - help="thickness of the knifes in mm. Use 0 for use with honey comb table.") + help="thickness of the knives in mm. Use 0 for use with honey comb table.") self.argparser.add_argument( "--beamwidth", action="store", type=float, default=32., help="width of the (aluminium) profile connecting the parts") @@ -252,9 +250,9 @@ class Rotary(Boxes): with self.saved_context(): self.rectangularWall(hw - 2 * t - 2, 60, edges="efef", move="right") self.rectangularWall(hw - 4 * t - 4, 60, edges="efef", move="right") - # Spindel auxiliaries - self.parts.waivyKnob(50, callback=lambda: self.nutHole("M8"), move="right") - self.parts.waivyKnob(50, callback=lambda: self.nutHole("M8"), move="right") + # Spindle auxiliaries + self.parts.wavyKnob(50, callback=lambda: self.nutHole("M8"), move="right") + self.parts.wavyKnob(50, callback=lambda: self.nutHole("M8"), move="right") self.rectangularWall(hw - 2 * t - 4, 60, edges="efef", move="up only") @@ -292,7 +290,7 @@ class Rotary(Boxes): self.rectangularWall(ow, h, edges="hFFH", move="right") self.rectangularWall(ow, h, edges="hFFH", move="right") self.rectangularWall(ow, h, edges="hFFH", move="up only") - + # Motor block mw = 40 self.rectangularWall(3.6 * d, h, edges=["h", "f", MotorEdge(self, None),"f"], callback=[self.mainPlate], move="up") @@ -324,6 +322,3 @@ class Rotary(Boxes): hole=self.axle, move="right") self.parts.disc(self.diameter - 2 * self.rubberthickness + 4, hole=self.axle, move="right up") - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/roundedbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/roundedbox.py index dbbb9d0..429a8c2 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/roundedbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/roundedbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -32,11 +31,11 @@ With lid: ui_group = "FlexBox" - def __init__(self): + def __init__(self) -> None: boxes.Boxes.__init__(self) self.addSettingsArgs(boxes.edges.FingerJointSettings) self.addSettingsArgs(boxes.edges.DoveTailSettings) - self.addSettingsArgs(boxes.edges.FlexSettings) + self.addSettingsArgs(boxes.edges.FlexSettings) self.buildArgParser("x", "y", "outside", sh="100.0") self.argparser.add_argument( "--radius", action="store", type=float, default=15, @@ -75,7 +74,7 @@ With lid: self.moveTo(0, dr) for l in (lx, ly, lx, ly): - self.edge(l); + self.edge(l) self.corner(90, r) def cb(self, nr): @@ -135,5 +134,3 @@ With lid: self.surroundingWall(x, y, r, h, pe, pe, pieces=self.wallpieces, callback=self.cb) - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/royalgame.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/royalgame.py index 653a8aa..a1dfeb7 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/royalgame.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/royalgame.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,6 +15,7 @@ from boxes import * + class RoyalGame(Boxes): """The Royal Game of Ur""" @@ -29,7 +29,7 @@ class RoyalGame(Boxes): """ - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) @@ -73,7 +73,7 @@ class RoyalGame(Boxes): l = s/7*2**0.5 self.moveTo(x-s/2 + s/14, y-s/2, 45) self.polyline(*([l, -90, l, 90]*3 + [l/2, 90])*4) - + def castle(self, x, y, s): self._castle(x, y, 0.9*s) self._castle(x, y, 0.5*s) @@ -160,7 +160,7 @@ class RoyalGame(Boxes): for dx in (-1, 1): for dy in (-1, 1): self.hole(x+dx*.2*s, y+dy*.2*s, 0.07*s) - + def render(self): x = self.x @@ -178,10 +178,9 @@ class RoyalGame(Boxes): self.rectangularWall(x, y, "fMff", move="up") self.rectangularWall(x, y, "fNff", callback=[self.top,], move="up") - + self.partsMatrix(7, 7, "up", self.parts.disc, 0.8*size, callback=self.player1) self.partsMatrix(7, 7, "up", self.parts.disc, 0.8*size, callback=self.player2) self.dice(size, 4, move="up") self.dice(size, 4, move="up") - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/sevensegment.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/sevensegment.py new file mode 100644 index 0000000..fa4033a --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/sevensegment.py @@ -0,0 +1,109 @@ +# Copyright (C) 2013-2014 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class SevenSegmentPattern(Boxes): + """Holepatterns and walls for a seven segment digit""" + + description = """This pattern is indented to be used with a LED stripe that is wound through all segments in an S pattern while the stripe being upright on its side. It can also be used with small pieces of LED stripes connected with short wires for large enough sizes. +""" + + ui_group = "Holes" + + def __init__(self): + Boxes.__init__(self) + self.addSettingsArgs(edges.FingerJointSettings) + self.argparser.add_argument( + "--digit", action="store", type=float, default=100.0, + help="height of the digit (without walls) in mm") + self.argparser.add_argument( + "--h", action="store", type=float, default=20.0, + help="height separation walls in mm") + + @restore + @holeCol + def segment(self, l, w): + w2 = w * 2**0.5 + self.moveTo(0, 0, 45) + self.polyline(w2, -45, l-2*w, -45, w2, -90, w2, -45, + l-2*w, -45, w2, -90) + + @restore + def seven_segments(self, x): + t = self.thickness + l = 0.4 * x + w = 0.05 * x + d = 0.05 * x + width = l + 2*w + d # 0.55 * x + + #self.rectangularHole(width/2, x/2, width, x) + + for px in [w/2 + d/2 , w/2 + l + 1.5*d]: + for py in [w + d/2, w + l + 1.5*d]: + with self.saved_context(): + self.moveTo(px, py, 90) + self.segment(l, w) + for i in range(3): + with self.saved_context(): + self.moveTo(w/2 + d, w + i*(l+d)) + self.segment(l, w) + + def seven_segment_holes(self, x): + t = self.thickness + l = 0.4 * x + w = 0.05 * x + d = 0.05 * x + width = l + 2*w + d + + for i in range(2): + self.fingerHolesAt(t/4*2**.5, x/2+w-t/4*2**.5, + 2**0.5*(width-t) - t/2, -45) + self.fingerHolesAt(t, t, 2**0.5* (.55*x/2 - t) - t/2, 45) + self.fingerHolesAt(width/2 + t/2**.5/2, + width/2 + t/2**.5/2, + 2**0.5*(l/2+d/2) - 1.5*t, 45) + self.fingerHolesAt(-t/2, x/2 + 0.25*t, x/2 - 0.25*t, 90) + self.fingerHolesAt(-t/2, 0, x/2 - 0.25*t, 90) + self.fingerHolesAt(-t, -t/2, l + 2*w + d + 2*t, 0) + self.moveTo(width, x, 180) + + def seven_segment_separators(self, x, h, n=1): + t = self.thickness + l = 0.4 * x + w = 0.05 * x + d = 0.05 * x + width = l + 2*w + d # 0.55 * x + for length in ( + 2**0.5*(width-t) - t/2, + 2**0.5* x/4 - t, + 2**0.5*(l/2+d/2) - 1.5*t, + x/2 - 0.25*t, + x/2 - 0.25*t, + l + 2*w + d + 2*t,): + self.partsMatrix(2*n, 1, "right", + self.rectangularWall, length, h, "feee") + + def render(self): + digit, h = self.digit, self.h + t = self.thickness + + self.seven_segments(digit) + self.moveTo(0.55*digit+self.spacing+t, t) + #self.seven_segments(digit) + self.seven_segment_holes(digit) + self.moveTo(0.55*digit+self.spacing+t, -t) + self.seven_segment_separators(digit, h) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/sevensegmentclock.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/sevensegmentclock.py new file mode 100644 index 0000000..dcbc183 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/sevensegmentclock.py @@ -0,0 +1,86 @@ +# Copyright (C) 2013-2014 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + +from .sevensegment import SevenSegmentPattern + + +class SevenSegmentClock(SevenSegmentPattern): + """Seven segment clock build with LED stripe""" + + description = """You need a LED stripe that is wound through all segments in an S pattern and then continuing to the next digit while the stripe being upright on its side. Selecting *debug* gives a better idea how things fit together. + +Adding a diffuser on top or at the bottom of the segment holes will probably enhance the visuals. Just using paper may be enough. + +There is currently not a lot of space for electronics and this generator is still untested. Good luck! +""" + + ui_group = "Misc" + ui_group = "Unstable" + + def __init__(self): + Boxes.__init__(self) + self.addSettingsArgs(edges.FingerJointSettings) + self.argparser.add_argument( + "--height", action="store", type=float, default=100.0, + help="height of the front panel (with walls if outside is selected) in mm") + self.argparser.add_argument( + "--h", action="store", type=float, default=20.0, + help="depth (with walls if outside is selected) in mm") + self.buildArgParser(outside=False) + + def frontCB(self): + x = self.height + self.hole(1.27*x, 0.4*x, 0.05*x) + self.hole(1.27*x, 0.6*x, 0.05*x) + self.moveTo(0.1*x, 0.1*x) + for i in range(2): + for j in range(2): + self.seven_segments(.8 * x) + #self.seven_holes(.8 * x) + self.moveTo(.6 * x) + self.moveTo(0.1 * x) + + def backCB(self): + x = self.height + self.moveTo(0.1*x, 0.1*x) + for i in range(2): + for j in range(2): + self.seven_segment_holes(.8 * x) + self.moveTo(.6 * x) + self.moveTo(0.1 * x) + + + def render(self): + height, h = self.height, self.h + + if self.outside: + height = self.height = self.adjustSize(height) + h = self.h = self.adjustSize(h) + + t = self.thickness + y = (3*0.60 + 0.1 + 0.2) * height + 0.55 * 0.8 * height + + self.rectangularWall(height, h, "FFFF", move="right") + self.rectangularWall(y, h, "FfFf", move="up") + self.rectangularWall(y, h, "FfFf") + self.rectangularWall(height, h, "FFFF", move="left up") + + with self.saved_context(): + self.rectangularWall(y, height, "ffff", callback=[self.frontCB], move="right") + self.rectangularWall(y, height, "ffff", callback=[self.backCB], move="right") + self.rectangularWall(y, height, "ffff", move="up only") + self.seven_segment_separators(0.8*height, h, 4) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/shadowbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/shadowbox.py new file mode 100644 index 0000000..e985c88 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/shadowbox.py @@ -0,0 +1,110 @@ +# Copyright (C) 2024 Oliver Jensen +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class Shadowbox(Boxes): + """The frame and spacers necessary to display a shadowbox / lightbox.""" + + description = """ +The frame needed to build a shadowbox from paper cutouts. +The cutout used in the photographs can be downloaded [here](https://3axis.co/laser-cut-my-neighbor-totoro-3d-lightbox-lamp-cdr-file/eoxldrxo/). + +See the diagram below for dimensions. + +![diagram](static/samples/Shadowbox-diagram.jpg) + +![backlit](static/samples/Shadowbox-backlit.jpg) +""" + + ui_group = "Misc" + + def __init__(self) -> None: + Boxes.__init__(self) + self.addSettingsArgs(edges.FingerJointSettings) + self.addSettingsArgs(edges.DoveTailSettings, angle=10, depth=1.5, radius=0.1, size=1) + self.buildArgParser(x=200, y=260) + self.argparser.add_argument( + "--layers", action="store", type=int, default=7, + help="the number of paper layers; don't forget the back (blank) layer!") + self.argparser.add_argument( + "--framewidth", action="store", type=float, default=10, + help="the width of the paper layer frames") + self.argparser.add_argument( + "--frameheight", action="store", type=float, default=10, + help="the height of the paper layer frames") + self.argparser.add_argument( + "--extraheight", action="store", type=float, default=20, + help="cumulative height of your paper layers, play between frames, the LED strip, battery/wiring, anything else you want to fit in the case") + self.argparser.add_argument( + "--casejoinery", action="store", type=boolarg, default=True, + help="whether or not to join sides to front plate (disable if doing manual joins on fancy wood)") + + def render(self): + x, y = self.x, self.y + t = self.thickness + extraheight = self.extraheight + frameheight = self.frameheight + framewidth = self.framewidth + casejoinery = self.casejoinery + layers = self.layers + height = layers * t + extraheight + + # inner frames horizontal bars + for _ in range(2*layers): + self.polygonWall([ + x, 90, + frameheight, 90, + framewidth, 0, x - framewidth*2, 0, framewidth, 90, + frameheight, 90], + "eeDeDe", move="up") + + # inner frames vertical bars + for _ in range(2*layers): + self.rectangularWall(y - frameheight*2, framewidth, "eded", move="up") + + # faceplate + hypotenuse = math.sqrt((frameheight+t)**2 + (framewidth+t)**2) + angle = math.degrees(math.acos((framewidth+t) / hypotenuse)) + edgetypes = 'eFeeee' if casejoinery else 'eeeeee' + + vframe_poly = [ + t, 0, y, 0, t, 90+angle, + hypotenuse, 90-angle, + y - frameheight*2, 90-angle, + hypotenuse, 90+angle] + hframe_poly = [ + t, 0, x, 0, t, 90+angle, + hypotenuse, 90-angle, + x - framewidth*2, 90-angle, + hypotenuse, 90+angle] + + self.polygonWall(vframe_poly, edgetypes, move="up") + self.polygonWall(vframe_poly, edgetypes, move="up") + + angle = 90 - angle + self.polygonWall(hframe_poly, edgetypes, move="up") + self.polygonWall(hframe_poly, edgetypes, move="up") + + # case sides + if casejoinery: + top_edge = 'f' + else: + top_edge = 'e' + self.rectangularWall(x, height, f"ef{top_edge}f", move="up") + self.rectangularWall(x, height, f"ef{top_edge}f", move="up") + self.rectangularWall(y, height, f"eF{top_edge}F", move="up") + self.rectangularWall(y, height, f"eF{top_edge}F", move="up") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/shoe.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/shoe.py new file mode 100644 index 0000000..db79656 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/shoe.py @@ -0,0 +1,146 @@ +# Copyright (C) 2013-2016 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class Shoe(Boxes): + """Shoe shaped box""" + + description = """Shoe shaped box with flat sides and rounded top. + Works best if flex if under slight compression. + Make sure that the following conditions are met: + y > tophole + r + fronttop; + height > frontheight.""" + + ui_group = "Misc" + + def __init__(self) -> None: + Boxes.__init__(self) + + self.addSettingsArgs(edges.FingerJointSettings) + self.addSettingsArgs(edges.FlexSettings) + + self.argparser.add_argument( + "--width", action="store", type=float, default=65, + help="width of the shoe") + self.argparser.add_argument( + "--length", action="store", type=float, default=175, + help="length front to back") + self.argparser.add_argument( + "--height", action="store", type=float, default=100, + help="height at the back of the shoe") + self.argparser.add_argument( + "--frontheight", action="store", type=float, default=35, + help="height at the front of the shoe") + self.argparser.add_argument( + "--fronttop", action="store", type=float, default=20, + help="length of the flat part at the front of the shoe") + self.argparser.add_argument( + "--tophole", action="store", type=float, default=75, + help="length of the opening at the top") + self.argparser.add_argument( + "--radius", action="store", type=float, default=30, + help="radius of the bend") + + + def render(self): + x, y, h = self.width, self.length, self.height + t = self.thickness + + hf = self.frontheight + yg = self.tophole + tf = self.fronttop + r=self.radius + + if (hf > h): + # Give an error because the result will be wrong with possible unconnected paths + raise ValueError("Height at front of shoe must be less than height at back of shoe.") + + stretch = (self.edges["X"].settings.stretch) + + self.ctx.save() + self.rectangularWall(y, x, "FFFF", move="up", label="Bottom") + lf,a=self.shoeside(y,h,hf,yg,tf,r, move="up", label="Side") + self.shoeside(y,h,hf,yg,tf,r, move="mirror up", label="Side") + self.ctx.restore() + self.rectangularWall(y, x, "FFFF", move="right only") + self.rectangularWall(x, h, "ffef", move="up", label="Back") + self.rectangularWall(x, hf, "ffff", move="up", label="front") + dr = a*(r-t)/stretch + self.shoelip(x, tf, dr, lf, label="top") + + + def shoelip(self, x, tf, dr, lf, move=None, label=""): + + w = self.edges["F"].spacing() + + th = tf + dr + lf + self.edges["F"].spacing() + self.edges["e"].spacing() + tw = x + 2*w + if self.move(tw, th, move, True, label=label): + return + + self.moveTo(self.edges["F"].spacing(), self.edges["e"].spacing()) + + self.edges["F"](x) + self.edgeCorner("F", "F") + self.edges["F"](tf) + self.edges["X"](dr, h=x+2*w) + self.edges["F"](lf) + self.edgeCorner("F", "e") + self.edges["e"](x) + self.edgeCorner("e", "F") + self.edges["F"](lf) + self.edges["E"](dr) + self.edges["F"](tf) + self.edgeCorner("F", "F") + + self.move(tw, th, move, label=label) + + def shoeside(self, y, h, hf, yg, tf, r, move=None, label=""): + import math + + tx = y + 2 * self.edges.get('F').spacing() + ty = h + self.edges.get('f').spacing() + self.edges.get("e").spacing() + + if self.move(tx, ty, move, before=True): + return + + lf = math.sqrt((h-hf)**2+(y-yg-tf)**2) + af = 90-math.degrees(math.atan((h-hf)/(y-yg-tf))) + + atemp = math.degrees(math.atan((h-hf-r)/(y-yg-tf))) + dtemp = math.sqrt((h-hf-r)**2+(y-yg-tf)**2) + lf = math.sqrt(dtemp**2-r**2) + af = 90-atemp-math.degrees(math.atan(r/lf)) + + self.moveTo(self.edges.get('f').margin(), self.edges.get("f").margin()) + + self.edges.get('f')(y) + self.edgeCorner(self.edges["f"],self.edges["F"],90) + self.edges.get('F')(hf) + self.edgeCorner(self.edges["F"],self.edges["f"],90) + self.edges.get('f')(tf) + self.corner(af-90,r) + self.edges.get('f')(lf) + self.edgeCorner(self.edges["f"],self.edges["e"],90-af) + self.edges.get('e')(yg) + self.edgeCorner(self.edges["e"],self.edges["F"],90) + self.edges.get('F')(h) + self.edgeCorner(self.edges["F"],self.edges["f"],90) + + self.move(tx, ty, move, label=label) + + return lf,math.radians(90-af) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/shutterbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/shutterbox.py index 897bed6..55a9f00 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/shutterbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/shutterbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,6 +15,7 @@ from boxes import * + class ShutterBox(Boxes): """Box with a rolling shutter made of flex""" @@ -30,7 +30,7 @@ class ShutterBox(Boxes): def side(self, l, h, r, style, move=None): t = self.thickness - + if self.move(l+2*t, h+2*t, move, True): return @@ -76,7 +76,7 @@ class ShutterBox(Boxes): else: self.polyline(l-2*r, (90, r+t), 0, 90, t, -90) self.edges["f"](h-r) - + self.move(l+2*t, h+2*t, move) def cornerRadius(self, r, two=False, move=None): @@ -104,7 +104,7 @@ class ShutterBox(Boxes): self.polyline(l-r, (90, r-1.5*t), 0, 90, t, 90, 0, (-90, r-2.5*t), l-r, 90, t, 90) self.moveTo(-t-s, t+s) self.polyline(l-r, (90, r-1.5*t), 0, 90, t, 90, 0, (-90, r-2.5*t), l-r, 90, t, 90) - + self.move(tw, th, move) def rails2(self, l, r, move=None): @@ -137,7 +137,7 @@ class ShutterBox(Boxes): self.polyline(0, 90, h, 90, l, 90, h, 90) self.move(l, h, move) - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=0.5) @@ -158,7 +158,7 @@ class ShutterBox(Boxes): style = self.style self.n = n = 3 - + if not r: self.radius = r = h / 2 self.radius = r = min(r, h/2) @@ -186,14 +186,14 @@ class ShutterBox(Boxes): self.side(x, h, r, style, move="up only") self.rectangularWall(x, y, "FFFF", move="right") - + if style == "single": self.door(x-r+0.5*math.pi*r + 3*t, y-0.2*t, move="right") else: self.door(x-2*r+math.pi*r + 3*t, y-0.2*t, move="right") self.rectangularWall(2*t, y-2.2*t, edges="eeef", move="right") - + a = 90. / n ls = 2*math.sin(math.radians(a/2)) * (r-2.5*t) @@ -217,16 +217,15 @@ class ShutterBox(Boxes): self.rectangularWall(x-2*r, y, "fbfb", move="right") else: self.rectangularWall(x-r, y, "fbfe", move="right") - + self.rectangularWall(ls, y, "fafB", move="right") - + for i in range(n-2): self.rectangularWall(ls, y, "fafA", move="right") - + if h - 2*r > 2*t: self.rectangularWall(ls, y, "fbfA", move="right") self.rectangularWall(h - 2*r, y, "fefB", move="right") else: self.rectangularWall(ls, y, "fefA", move="right") - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/sidedoorhousing.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/sidedoorhousing.py index a6238cf..1cbc666 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/sidedoorhousing.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/sidedoorhousing.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2020 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -17,27 +16,39 @@ from boxes import * from boxes.generators.console2 import Console2 + class SideDoorHousing(Console2): - """A box with service hatches at the sides""" + """Box with service hatches on either one or both of the sides that are locked with latches""" ui_group = "Box" description = """ -This box is designed as a housing for electronic projects. It has hatches that can be re-opened with simple tools. It intentionally cannot be opened with bare hands - if build with thin enough material. The hatches are at the x sides. +This box is designed as a housing for electronic projects but could be used for other purposes. It has hatches that can be re-opened with simple tools. If built from thin enough material, it intentionally cannot be opened with bare hands. + +The hatches are on the x sides. #### Assembly instructions -The main body is easy to assemble by starting with the floor and then adding the four walls and the top piece. +The main body is easy to assemble: +1. Starting with the floor and then add the four walls (for any removable sides, the wall will just be a small part on the base ) +2. Add the top piece -For the removable walls you need to add the lips and latches. The U-shaped clamps holding the latches in place need to be clued in place without also gluing the latches themselves. Make sure the springs on the latches point inwards and the angled ends point to the side walls as shown here (showing a different box type): +For the removable walls: +1. Add the lips to the removable walls +2. Sit the latches in place (it is importand to make sure the springs on the latches point inwards and the angled ends point to the side walls. See image below) +3. Glue the U-shaped clamps in place (it is important **not** to glue the latches) + +![Wall details](static/samples/SideDoorHousing.jpg) -![Wall details](static/samples/Console2-backwall-detail.jpg) #### Re-Opening The latches lock in place when closed. To open them they need to be pressed in and can then be moved aside. + +![Closed Box](static/samples/SideDoorHousing-2.jpg) + """ - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=.5) @@ -48,7 +59,7 @@ The latches lock in place when closed. To open them they need to be pressed in a "--double_door", action="store", type=boolarg, default=True, help="allow removing the backwall, too") - + def render(self): x, y, h = self.x, self.y, self.h t = self.thickness @@ -80,7 +91,7 @@ The latches lock in place when closed. To open them they need to be pressed in a self.rectangularHole(y-1.55*t, latchpos, 1.1*t, 1.1*t))], move=move) - + for i in range(2 if self.double_door else 1): self.rectangularWall(x, t, (bottom, "F", "e", "F"), ignore_widths=[1, 6], move="up") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/silverwarebox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/silverwarebox.py index 8a243a0..32287c2 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/silverwarebox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/silverwarebox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -26,7 +25,7 @@ class Silverware(Boxes): ui_group = "Unstable" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser(x=250, y=154, h=120) @@ -106,4 +105,3 @@ class Silverware(Boxes): self.moveTo(-3.0 * (l + 2 * t + 8 * b), h - 10 + 2 * t + 8 * b) self.basePlate(x, y, r) - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/skadis.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/skadis.py new file mode 100644 index 0000000..593dc89 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/skadis.py @@ -0,0 +1,44 @@ +# Copyright (C) 2013-2016 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class SkadisBoard(Boxes): + """Customizable Ikea like pegboard""" + + ui_group = "Misc" + + def __init__(self) -> None: + Boxes.__init__(self) + + self.argparser.add_argument( + "--columns", action="store", type=int, default=17, + help="Number of holes left to right counting both even and odd rows") + self.argparser.add_argument( + "--rows", action="store", type=int, default=27, + help="Number of rows of holes top to bottom") + + + def CB(self): + for r in range(self.rows): + for c in range(self.columns): + if (r+c) % 2 == 0: + continue + self.rectangularHole((c+1)*20 - 8, (r+1)*20, 5, 15, r=2.5) + + def render(self): + self.roundedPlate((self.columns+1) * 20, (self.rows+1)*20, edge="e", r=8, + extend_corners=False, callback=[self.CB]) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/slantedtray.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/slantedtray.py new file mode 100644 index 0000000..2aee928 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/slantedtray.py @@ -0,0 +1,87 @@ +# Copyright (C) 2013-2023 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from functools import partial + +from boxes import * + + +class SlantedTray(Boxes): + """One row tray with high back wall and low front wall""" + + ui_group = "Tray" + + description = """Can be used as a display or for cards or gaming tokens. Lay on the side to get piles to draw from. + ![Example Use](static/samples/SlantedTray-2.jpg)""" + + def __init__(self) -> None: + Boxes.__init__(self) + + self.addSettingsArgs(edges.FingerJointSettings) + # self.addSettingsArgs(edges.StackableSettings) + + self.buildArgParser(sx="40*3", y=40.0, h=40.0, outside=False) + self.argparser.add_argument( + "--front_height", action="store", type=float, default=0.3, + help="height of the front as fraction of the total height") + + def finger_holes_CB(self, sx, h): + t = self.thickness + pos = -0.5 * t + for x in sx[:-1]: + pos += x + t + self.fingerHolesAt(pos, 0, h) + + def render(self): + # adjust to the variables you want in the local scope + sx, y, h = self.sx, self.y, self.h + t = self.thickness + + if self.outside: + self.sx = sx = self.adjustSize(sx) + self.y = y = self.adjustSize(y) + self.h = h = self.adjustSize(h, False) + + front_height = h * self.front_height + x = sum(sx) + t * (len(sx) - 1) + + self.rectangularWall( + x, + h, + "eFfF", + move="up", + callback=[partial(self.finger_holes_CB, sx, h)], + ) + + self.rectangularWall( + x, + y, + "FFfF", + move="up", + callback=[partial(self.finger_holes_CB, sx, y)], + ) + + self.rectangularWall( + x, + front_height, + "FFeF", + move="up", + callback=[ + partial(self.finger_holes_CB, sx, front_height) + ], + ) + + for _ in range(len(sx) + 1): + self.trapezoidWall(y, h, front_height, "ffef", move="right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/slidingdrawer.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/slidingdrawer.py index 4996283..559e667 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/slidingdrawer.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/slidingdrawer.py @@ -1,11 +1,12 @@ from boxes import * + class SlidingDrawer(Boxes): """Sliding drawer box""" ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser(x=60, y=100, h=30, outside='true') self.addSettingsArgs(edges.FingerJointSettings, finger=2.0, space=2.0) @@ -13,7 +14,7 @@ class SlidingDrawer(Boxes): self.argparser.add_argument( "--play", action="store", type=float, default=0.15, - help="play between the two parts as multipleof the wall thickness") + help="play between the two parts as multiple of the wall thickness") def render(self): @@ -47,4 +48,3 @@ class SlidingDrawer(Boxes): self.rectangularWall(y, h, "fffe", label="out box wall", move="up left") self.rectangularWall(x, h, "fFfF", label="out box wall") - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/smallpartstray.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/smallpartstray.py new file mode 100644 index 0000000..bca0fa6 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/smallpartstray.py @@ -0,0 +1,255 @@ +# Copyright (C) 2013-2016 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * +from boxes import lids +from boxes.edges import CompoundEdge + + +class SmallPartsTray(Boxes): + """Tray with slants to easier get out game tokens or screws""" + + ui_group = "Tray" + + def __init__(self) -> None: + Boxes.__init__(self) + + self.addSettingsArgs(edges.FingerJointSettings) + # self.addSettingsArgs(edges.StackableSettings) + self.addSettingsArgs(lids.LidSettings) + + self.buildArgParser(sx="50*3", y=100, h=30, outside=True) + self.argparser.add_argument( + "--angle", action="store", type=float, default=45., + help="angle of the ramps") + self.argparser.add_argument( + "--rampheight", action="store", type=float, default=.5, + help="height of the ramps relative to to total height") + self.argparser.add_argument( + "--two_sided", action="store", type=boolarg, default=True, + help="have ramps on both sides. Enables sliding dividers") + self.argparser.add_argument( + "--front_panel", action="store", type=boolarg, default=True, + help="have a vertical wall at the ramp") + + + def innerWall(self, h, y, ramp_h, ramp_y, two_ramps, front=True, + move=None): + a = math.degrees(math.atan(ramp_h/ramp_y)) + l = (ramp_h**2 + ramp_y**2)**.5 + if two_ramps: + self.polygonWall( + [y-2*ramp_y, a, l, 90-a, h-ramp_h, 90, y, + 90, h-ramp_h, 90-a, l, a], + "fffeff" if front else "ffeeef", move=move) + else: + self.polygonWall( + [y-ramp_y, 90, h, 90, y, 90, h-ramp_h, 90-a, l, a], + "ffeff" if front else "ffeef", move=move) + + def outerWall(self, h, y, ramp_h, ramp_y, two_ramps, front=True, + move=None): + a = math.degrees(math.atan(ramp_h/ramp_y)) + l = (ramp_h**2 + ramp_y**2)**.5 + t = self.thickness + + def cb(): + with self.saved_context(): + self.moveTo(ramp_y, 0, 180-a) + self.fingerHolesAt(0, 0.5*t, l, 0) + if two_ramps: + self.moveTo(y-ramp_y, 0, a) + self.fingerHolesAt(0, -0.5*t, l, 0) + + if two_ramps: + self.rectangularWall( + y, h, + [CompoundEdge(self, "EFE", (ramp_y, y-2*ramp_y, ramp_y)), + CompoundEdge(self, "EF", (ramp_h, h-ramp_h)) if front else "e", + "e", + CompoundEdge(self, "FE", (h-ramp_h, ramp_h)) if front else "e"], + callback=[cb], move=move) + else: + self.rectangularWall( + y, h, [ + CompoundEdge(self, "EF", (ramp_y, y-ramp_y)) if front else "e", + "F", + "e", + CompoundEdge(self, "FE", (h-ramp_h, ramp_h))], + callback=[cb], move=move) + + + def holeCB(self, sections, height): + def CB(): + pos = -0.5 * self.thickness + for l in sections[:-1]: + pos += l + self.thickness + self.fingerHolesAt(pos, 0, height) + return CB + + def render_simple_tray_divider(self, width, height, move): + """ + Simple movable divider. A wall with small feet for a little more stability. + """ + + if self.move(width, height, move, True): + return + + t = self.thickness + self.moveTo(t) + self.polyline( + width - 2 * t, + 90, + t, + -90, + t, + 90, + height - t, + 90, + width, + 90, + height - t, + 90, + t, + -90, + t, + 90, + ) + + self.move(width, height, move) + + def render_simple_tray_divider_feet(self, move=None): + sqr2 = math.sqrt(2) + t = self.thickness + divider_foot_width = 2 * t + full_width = t + 2 * divider_foot_width + move_length = full_width / sqr2 + 2 * self.burn + move_width = full_width / sqr2 + 2 * self.burn + + if self.move(move_width, move_length, move, True): + return + + self.moveTo(self.burn) + self.ctx.save() + self.polyline( + sqr2 * divider_foot_width, + 135, + t, + -90, + t, + -90, + t, + 135, + sqr2 * divider_foot_width, + 135, + full_width, + 135, + ) + self.ctx.restore() + + self.moveTo(-self.burn / sqr2, self.burn * (1 + 1 / sqr2), 45) + self.moveTo(full_width) + + self.polyline( + 0, + 135, + sqr2 * divider_foot_width, + 135, + t, + -90, + t, + -90, + t, + 135, + sqr2 * divider_foot_width, + 135, + ) + + self.move(move_width, move_length, move) + + + def render(self): + # adjust to the variables you want in the local scope + sx, y, h = self.sx, self.y, self.h + t = self.thickness + a = self.angle + b = "e" + + if self.outside: + self.sx = sx = self.adjustSize(sx) + self.h = h = self.adjustSize(h, False) + dy = t if self.front_panel else t / 2**0.5 + self.y = y = self.adjustSize(y, dy, dy) + + x = sum(sx) + (len(sx)-1) * t + + ramp_h = h * self.rampheight + ramp_y = ramp_h / math.tan(math.radians(a)) + + if self.two_sided and (2*ramp_y + 3*t > y): + ramp_y = (y - 3*t) / 2 + ramp_h = ramp_y * math.tan(math.radians(a)) + elif ramp_y > y - t: + ramp_y = y - t + ramp_h = ramp_y * math.tan(math.radians(a)) + + ramp_l = (ramp_h**2 + ramp_y**2)**.5 + + with self.saved_context(): + self.outerWall(h, y, ramp_h, ramp_y, + self.two_sided, self.front_panel, move="up") + self.outerWall(h, y, ramp_h, ramp_y, + self.two_sided, self.front_panel, move="mirror up") + for i in range(len(sx)-1): + self.innerWall(h, y, ramp_h, ramp_y, + self.two_sided, self.front_panel, move="up") + self.innerWall(h, y, ramp_h, ramp_y, + self.two_sided, self.front_panel, move="right only") + + if self.front_panel: + self.rectangularWall( + x, h-ramp_h, "efef", + callback=[self.holeCB(sx, h-ramp_h)], move="up") + self.rectangularWall(x, ramp_l, "efef", + callback=[self.holeCB(sx, ramp_l)], move="up") + if self.two_sided: + self.rectangularWall( + x, y-2*ramp_y, "efef", + callback=[self.holeCB(sx, y-2*ramp_y)], move="up") + self.rectangularWall( + x, ramp_l, "efef", + callback=[self.holeCB(sx, ramp_l)], move="up") + if self.front_panel: + self.rectangularWall( + x, h-ramp_h, "efef", + callback=[self.holeCB(sx, h-ramp_h)], move="up") + else: + self.rectangularWall( + x, y-ramp_y, "efff", + callback=[self.holeCB(sx, y-ramp_y)], move="up") + self.rectangularWall( + x, h, "Ffef", + callback=[self.holeCB(sx, h)], move="up") + + + if self.two_sided: + with self.saved_context(): + for l in self.sx: + self.render_simple_tray_divider(l, h, move="right") + + self.partsMatrix(len(self.sx), 0, "right", self.render_simple_tray_divider_feet) + self.render_simple_tray_divider(l, h, move="up only") + + self.lid(x, y) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/smallpartstray2.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/smallpartstray2.py new file mode 100644 index 0000000..ed767fa --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/smallpartstray2.py @@ -0,0 +1,233 @@ +# Copyright (C) 2013-2023 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * +from boxes.lids import LidSettings, _TopEdge + + +class TopEdge(edges.BaseEdge): + + def __init__(self, boxes, lengths, h): + super().__init__(boxes, None) + self.lengths = lengths + self.h = h + + def __call__(self, length, **kw): + h = self.h + t = self.boxes.thickness + t2 = t * 2**.5 + slot = h/2**.5 + t/2 + self.polyline( + 0, 90, t2, -45, + slot-t, -90, t, -90, slot, 135, + self.lengths[0] - t2/2) + + for l in self.lengths[1:]: + self.polyline( + 0, 45, t, 45, h/2-t2/2, -90, t, -90, h/2-t2, 135, + slot-t, -90, t, -90, slot, 135, + l - t2/2 + ) + self.polyline(t2/2) + +class SmallPartsTray2(_TopEdge): + """A Type Tray variant with slopes toward the front""" + + description = """Assemble inside out. If there are inner front to back walls start with attaching the floor boards to them. Then add the vertical inner left to right walls. After sliding in the slopes attach the outer wall to fix everything in place. + +If there are no inner front to back walls just add everything to one side wall and then add the other one after that. Possibly saving the front and back as last step.""" + + ui_group = "Tray" + + def __init__(self) -> None: + Boxes.__init__(self) + self.addTopEdgeSettings(fingerjoint={"surroundingspaces": 1.0}, + roundedtriangle={"outset" : 1}) + self.addSettingsArgs(LidSettings) + self.buildArgParser("sx", "sy", "hi", "outside", h=30) + # "bottom_edge", "top_edge") + self.argparser.add_argument( + "--back_height", action="store", type=float, default=0.0, + help="additional height of the back wall - e top edge only") + self.argparser.add_argument( + "--radius", action="store", type=float, default=0.0, + help="radius for strengthening side walls with back_height") + self.argparser.add_argument( + "--handle", type=boolarg, default=False, help="add handle to the bottom (changes bottom edge in the front)", + ) + + def fingerHolesCB(self, sections, height): + + def CB(): + posx = -0.5 * self.thickness + for x in sections[:-1]: + posx += x + self.thickness + self.fingerHolesAt(posx, 0, height) + return CB + + + def fingerHoleLineCB(self, posx, posy, sections): + def CB(): + self.moveTo(posx, posy, 90) + for l in sections: + self.fingerHolesAt(0, 0, l, 0) + self.moveTo(l+self.thickness) + return CB + + + def xHoles(self): + posx = -0.5 * self.thickness + for x in self.sx[:-1]: + posx += x + self.thickness + self.fingerHolesAt(posx, 0, self.hi) + + def yHoles(self): + t = self.thickness + posy = -0.5 * self.thickness + for y in self.sy[:-1]: + posy += y + self.thickness + self.fingerHolesAt(posy, 0, self.hi-t*2**.5) + with self.saved_context(): + self.moveTo(posy-0.5*t, self.hi, 135) + self.fingerHolesAt(-0.5*t, 0, self.hi*2**.5+t/2) + + self.moveTo(posy+self.sy[-1]+0.5*t, self.hi, 135) + self.fingerHolesAt(-0.5*t, 0, self.hi*2**.5+t/2) + + def render(self): + + # tmp settings + self.top_edge = "e" + self.bottom_edge = "F" + + if self.outside: + self.sx = self.adjustSize(self.sx) + self.sy = self.adjustSize(self.sy) + self.h = self.adjustSize(self.h, e2=False) + if self.hi: + self.hi = self.adjustSize(self.hi, e2=False) + + x = sum(self.sx) + self.thickness * (len(self.sx) - 1) + y = sum(self.sy) + self.thickness * (len(self.sy) - 1) + h = self.h + sameh = not self.hi + hi = self.hi = self.hi or h + t = self.thickness + + # outer walls + b = self.bottom_edge + tl, tb, tr, tf = self.topEdges(self.top_edge) + self.closedtop = self.top_edge in "fFhŠ" + + bh = self.back_height if self.top_edge == "e" else 0.0 + + # x sides + + self.ctx.save() + + # outer walls - front/back + if bh: + self.rectangularWall(x, h+bh, [b, "f", tb, "f"], + callback=[self.xHoles], + #ignore_widths=[], + move="up", label="back") + self.rectangularWall(x, h, ["f" if self.handle else b, "f", "e", "f"], + callback=[self.fingerHolesCB(self.sx[::-1], h),], + move="up", label="front") + else: + self.rectangularWall(x, h, [b, "F", tb, "F"], + callback=[self.xHoles], + #ignore_widths=[1, 6], + move="up", label="back") + self.rectangularWall(x, hi-t*2**.5, ["f" if self.handle else b, "F", "e", "F"], + callback=[self.fingerHolesCB(self.sx[::-1], hi-t*2**.5),], + #ignore_widths=[] if self.handle else [1, 6], + move="up", label="front") + + + # floor boards + + t2 = t * 2**.5 + dy = hi + t2/2 + slot = t + t2/2 # 1.5*t + floors = [self.sy[0]- hi - slot + t2, slot] + self.rectangularWall( + x, floors[0], "ffef", + callback=[self.fingerHolesCB(self.sx, self.sy[0]-dy)], + move="up", label="floor back side") + for y_ in self.sy[1:]: + self.rectangularWall( + x, y_ - slot + t, "efef", + callback=[self.fingerHolesCB(self.sx, y_ - slot + t), + self.fingerHoleLineCB(hi-t2+t/2, 0, self.sx[::-1])], + move="up", label="floor") + floors.extend([y_ - slot + t, slot]) + self.rectangularWall( + x, hi-t2, "efYf" if self.handle else "efff", + callback=[self.fingerHolesCB(self.sx, hi-t2)], + move="up", label="floor front side") + floors.append(hi-t2) + + # Inner walls + + be = "f" if b != "e" else "e" + + for i in range(len(self.sy) - 1): + e = [edges.SlottedEdge(self, self.sx, be, slots=0.5 * hi), "f", + edges.SlottedEdge(self, self.sx[::-1], "e"), "f"] + self.rectangularWall(x, hi-t2, e, move="up", label=f"inner x {i+1}") + # slopes + for i in self.sy: + self.rectangularWall( + x, hi*2**.5 + t/2, + [edges.SlottedEdge(self, self.sx, "e", slots=hi/2**.5), "f", + edges.SlottedEdge(self, self.sx[::-1], "e"), "f"], + move="up", label="slope") + + # top / lid + self.drawLid(x, y, self.top_edge) # XXX deal with front + self.lid(x, y, self.top_edge) + + self.ctx.restore() + self.rectangularWall(x, hi, "ffff", move="right only") + + # y walls + + # outer walls - left/right + + for move in ("up", "up mirror"): + if bh: + self.trapezoidSideWall( + y, h+bh, hi-t*2**.5, + [edges.CompoundEdge(self, ("FE"*len(self.sy))+"F", floors), + "h", "e", "h"], + radius=self.radius, callback=[self.yHoles, ], + move=move, label="side") + else: + self.rectangularWall( + y, h, + [edges.CompoundEdge(self, ("FE"*len(self.sy))+"F", floors), + edges.CompoundEdge(self, "fE", (hi-t*2**.5, h-hi+t*2**.5)), + tl, "f"], + callback=[self.yHoles, ], + #ignore_widths=[6] if self.handle else [1, 6], + move=move, label="side") + + # inner walls + for i in range(len(self.sx) - 1): + e = [edges.CompoundEdge(self, ("fE"*len(self.sy))+"f", floors), + edges.CompoundEdge(self, "fe", (hi-t*2**.5, t*2**.5)), + TopEdge(self, self.sy[::-1], hi), "f"] + self.rectangularWall(y, hi, e, move="up", label=f"inner y {i+1}") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/spicesrack.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/spicesrack.py index bdd5e22..f954e54 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/spicesrack.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/spicesrack.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,6 +15,7 @@ from boxes import * + class FrontEdge(edges.Edge): def __call__(self, length, **kw): @@ -29,15 +29,15 @@ class FrontEdge(edges.Edge): self.corner(-a) self.moveTo(length) - def margin(self): + def margin(self) -> float: return self.edge_width - + class SpicesRack(Boxes): """Rack for cans of spices""" ui_group = "Shelf" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=1.0) @@ -45,7 +45,7 @@ class SpicesRack(Boxes): self.argparser.add_argument( "--diameter", action="store", type=float, default=55., help="diameter of spice cans") - + self.argparser.add_argument( "--height", action="store", type=float, default=60., help="height of the cans that needs to be supported") @@ -100,7 +100,7 @@ class SpicesRack(Boxes): a = self.base_angle l = self.hole_length self.moveTo(0, self.hole_distance) - + with self.saved_context(): self.ctx.scale(1, l/self.base_h) self.moveTo(self.space/2, 0, 90) @@ -149,9 +149,9 @@ class SpicesRack(Boxes): width = self.hole_distance + self.hole_length + self.space/2 inner_width = self.hole_distance + self.hole_length/3 - + self.edge_width = width - inner_width - + for i in range(self.numy): self.rectangularWall(x, inner_width,[ "f", "e", FrontEdge(self, self), "e"], @@ -171,4 +171,3 @@ class SpicesRack(Boxes): if self.feet: self.partsMatrix(self.numx-1, self.numx-1, "up", self.foot, width, (self.h-t)/2) - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/spool.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/spool.py new file mode 100644 index 0000000..52f22ae --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/spool.py @@ -0,0 +1,99 @@ +# Copyright (C) 2013-2016 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class Spool(Boxes): + """A simple spool""" + + ui_group = "Misc" + + def __init__(self) -> None: + Boxes.__init__(self) + + self.addSettingsArgs(edges.FingerJointSettings) + + self.buildArgParser(h=100) + self.argparser.add_argument( + "--outer_diameter", action="store", type=float, default=200.0, + help="diameter of the flanges") + self.argparser.add_argument( + "--inner_diameter", action="store", type=float, default=80.0, + help="diameter of the center part") + self.argparser.add_argument( + "--axle_diameter", action="store", type=float, default=40.0, + help="diameter of the axle hole (axle not part of drawing)") + self.argparser.add_argument( + "--sides", action="store", type=int, default=8, + help="number of pieces for the center part") + self.argparser.add_argument( + "--reinforcements", action="store", type=int, default=8, + help="number of reinforcement ribs per side") + self.argparser.add_argument( + "--reinforcement_height", action="store", type=float, default=0.0, + help="height of reinforcement ribs on the flanges") + + def sideCB(self): + self.hole(0, 0, d=self.axle_diameter) + r, h, side = self.regularPolygon(self.sides, radius=self.inner_diameter/2) + t = self.thickness + for i in range(self.sides): + self.fingerHolesAt(-side/2, h+0.5*self.thickness, side, 0) + self.moveTo(0, 0, 360 / self.sides) + + if self.reinforcement_height: + for i in range(self.reinforcements): + self.fingerHolesAt( + self.axle_diameter / 2, 0, h-self.axle_diameter / 2, 0) + self.fingerHolesAt( + r + t, 0, self.outer_diameter / 2 - r - t, 0) + self.moveTo(0, 0, 360 / self.reinforcements) + + def reinforcementCB(self): + for i in range(self.reinforcements): + self.fingerHolesAt( + self.axle_diameter / 2, 0, + (self.inner_diameter - self.axle_diameter) / 2 + self.thickness, 0) + self.moveTo(0, 0, 360 / self.reinforcements) + + + def render(self): + t = self.thickness + r, h, side = self.regularPolygon(self.sides, radius=self.inner_diameter/2) + for i in range(2): + self.parts.disc( + self.outer_diameter, callback=self.sideCB, move="right") + for i in range(self.sides): + self.rectangularWall(side, self.h, "fefe", move="right") + if self.reinforcement_height: + for i in range(self.reinforcements*2): + edge = edges.CompoundEdge( + self, "fef", + [self.outer_diameter / 2 - r - t, + r - h + t, + h - self.axle_diameter / 2]) + self.trapezoidWall( + self.reinforcement_height - t, + (self.outer_diameter - self.axle_diameter) / 2, + (self.inner_diameter - self.axle_diameter) / 2 + t, + ["e", "f", "e", edge], + move="right") + for i in range(2): + self.parts.disc( + self.inner_diameter + 2*t, + hole=self.axle_diameter, + callback=self.reinforcementCB, + move="right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/stachel.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/stachel.py index 7d9fb2c..cb3538b 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/stachel.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/stachel.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,14 +14,14 @@ # along with this program. If not, see . from boxes import * -import math + class Stachel(Boxes): """Bass Recorder Endpin""" - + ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.argparser.add_argument( @@ -45,7 +44,7 @@ class Stachel(Boxes): tw = 2*ro + 2*rp th = 2*ro + l - + if self.move(tw, th, move, True): return @@ -80,10 +79,10 @@ class Stachel(Boxes): else: self.polyline(l+l1-r) self.polyline(0, (90, r), w-2*r, (90, r)) - + self.move(tw, th, move) - - + + def render(self): @@ -94,5 +93,3 @@ class Stachel(Boxes): self.layer(ri-20, ro, rp, move="up") self.layer(ri, ro, rp, True, move="up") self.layer(ri, ro, rp, move="up") - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/storagerack.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/storagerack.py index 84530f9..89e5576 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/storagerack.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/storagerack.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,6 +15,7 @@ from boxes import * + class StorageRack(Boxes): """StorageRack to store boxes and trays which have their own floor""" @@ -30,11 +30,11 @@ Drawers are not included: """ - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.addSettingsArgs(edges.StackableSettings) - + self.argparser.add_argument( "--depth", action="store", type=float, default=200, help="depth of the rack") @@ -59,7 +59,7 @@ Drawers are not included: for nr, h in enumerate(self.sh[:-1]): posh += h + self.thickness if ((self.bottom_edge == "e" and nr == 0) or - (self.top_edge == "e" and nr == len(self.sh) - 2)): + (self.top_edge == "e" and nr == len(self.sh) - 2)): self.fingerHolesAt(0, posh, self.x, 0) else: self.fingerHolesAt(0, posh, self.rail, 0) @@ -99,7 +99,7 @@ Drawers are not included: num -= 1 if t == "e": num -= 1 - + for i in range(num): self.rectangularWall(d, self.rail, "ffee", move="up") self.rectangularWall(d, self.rail, "feef", move="up") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/storageshelf.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/storageshelf.py index c3da729..fc6beab 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/storageshelf.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/storageshelf.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -17,13 +16,14 @@ from boxes import * from boxes.lids import _TopEdge + class StorageShelf(_TopEdge): """StorageShelf can be used to store Typetray""" ui_group = "Shelf" description = "This is a simple shelf box." - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addTopEdgeSettings(fingerjoint={"surroundingspaces": 0.5}, roundedtriangle={"outset" : 1}) @@ -35,8 +35,8 @@ class StorageShelf(_TopEdge): self.argparser.add_argument( "--retainer_hole_edge", action="store", type=boolarg, default=False, help="use finger hole edge for retainer walls") - - + + def ySlots(self): posy = -0.5 * self.thickness diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/tetris.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/tetris.py new file mode 100644 index 0000000..ab463d9 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/tetris.py @@ -0,0 +1,90 @@ +# Copyright (C) 2013-2016 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * + + +class Tetris(Boxes): + """3D Tetris shapes""" + + ui_group = "Misc" + + def __init__(self): + Boxes.__init__(self) + + self.addSettingsArgs(edges.FingerJointSettings) + + self.argparser.add_argument( + "--blocksize", action="store", type=float, default=40., + help="size of a square") + self.argparser.add_argument( + "--shape", action="store", type=str, default="L", + choices=['I', 'L', 'O', 'S', 'T'], + help="shape of the piece") + + def cb(self, nr): + t = self.thickness + s = self.blocksize + self.ctx.stroke() + self.set_source_color(Color.ETCHING) + + if nr == 0: + if self.shape in "LT": + for i in range(1, 3): + with self.saved_context(): + self.moveTo(s*i - t, 0, 90) + self.edge(s - 2*t) + if self.shape == "L": + self.moveTo(s*2, s - t, 0) + else: # "T" + self.moveTo(s, s - t, 0) + self.edge(s - 2*t) + if self.shape == "I": + for i in range(1, 4): + with self.saved_context(): + self.moveTo(s*i - t, 0, 90) + self.edge(s - 2*t) + if self.shape == "S" and nr in (0, 1, 4, 5): + self.moveTo(s - t, 0, 90) + self.edge(s - 2*t) + if self.shape == "O": + self.moveTo(s - t, 0, 90) + self.edge(s - t) + + + self.ctx.stroke() + + def render(self): + # adjust to the variables you want in the local scope + t = self.thickness + s = self.blocksize + + if self.shape == "L": + borders = [3*s - 2*t, 90, 2*s - 2*t, 90, s - 2*t, 90, + s, -90, 2*s, 90, s - 2*t, 90] + elif self.shape == "I": + borders = [4*s - 2*t, 90, s - 2*t, 90 ] * 2 + elif self.shape == "S": + borders = [2 * s - 2 * t, 90, s, -90, s, 90, s - 2 * t, 90] *2 + elif self.shape == "O": + borders = [2*s - 2*t, 90] * 4 + elif self.shape == "T": + borders = [90, s - 2*t, 90, s, -90, s, 90] + borders = [3*s - 2*t] + borders + [s - 2*t] + list(reversed(borders)) + self.polygonWall(borders=borders, callback=self.cb, move="right") + self.polygonWall(borders=borders, callback=self.cb, + move="mirror right") + + self.polygonWalls(borders=borders, h=s - 2 * t) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/trafficlight.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/trafficlight.py index 4661bbf..5c47cd4 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/trafficlight.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/trafficlight.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -#-*- coding: utf-8 -*- # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +14,12 @@ # along with this program. If not, see . from boxes import * -import math + class ShadyEdge(edges.BaseEdge): char = "s" - def __call__(self, lenght, **kw): + def __call__(self, length, **kw): s = self.shades h = self.h a = math.atan(s/h) @@ -33,16 +31,16 @@ class ShadyEdge(edges.BaseEdge): if i < self.n-1: self.edge(self.thickness) - def margin(self): + def margin(self) -> float: return self.shades class TrafficLight(Boxes): # change class name here and below """Traffic light""" - description = u"""The traffic light was created to visualize the status of a Icinga monitored system. + description = """The traffic light was created to visualize the status of a Icinga monitored system. When turned by 90°, it can be also used to create a bottle holder.""" - - def __init__(self): + + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) @@ -113,7 +111,7 @@ When turned by 90°, it can be also used to create a bottle holder.""" self.edgeCorner(edges[3], edges[3 + 1], 90) self.move(overallwidth, overallheight, move, label=label) - + def addMountH(self, width, height): ds = self.hole_dD[0] @@ -151,7 +149,7 @@ When turned by 90°, it can be also used to create a bottle holder.""" self.mountingHole(x, y1, ds, dh, 180) self.mountingHole(x, y2, ds, dh, 180) - + def render(self): # adjust to the variables you want in the local scope d, h, n = self.depth, self.h, self.n @@ -159,7 +157,7 @@ When turned by 90°, it can be also used to create a bottle holder.""" t = self.thickness th = n * (h + t) - t - + self.addPart(ShadyEdge(self, None)) @@ -206,4 +204,3 @@ When turned by 90°, it can be also used to create a bottle holder.""" # Colored windows for i in range(n): self.parts.disc(h-2*t, move="right", label="colored windows") - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/trayinsert.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/trayinsert.py index 7bd1481..dbd91af 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/trayinsert.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/trayinsert.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -22,7 +21,7 @@ class TrayInsert(Boxes): ui_group = "Tray" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser("sx", "sy", "h", "outside") @@ -46,6 +45,3 @@ class TrayInsert(Boxes): for i in range(len(self.sy) - 1): e = ["e", "e", edges.SlottedEdge(self, self.sx[::-1], "e", slots=0.5 * h), "e"] self.rectangularWall(x, h, e, move="up") - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/traylayout.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/traylayout.py index 7a07a8f..b433a0c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/traylayout.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/traylayout.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -13,119 +12,118 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from __future__ import annotations -import sys, re -from boxes import * import boxes -import argparse +from boxes import * +from boxes import lids -class TrayLayout(Boxes): - """Generate a typetray from a layout file""" +class TrayLayoutFile(Boxes): + """Generate a layout file for a typetray.""" # This class generates the skeleton text file that can then be edited # to describe the actual box - description = """This is a two step process. The layout is based on a grid -of sizes in x and y direction. Choose how many distances you need in both directions. The actual sizes and all other settings can be entered in the second step.""" + description = """This is a two step process. This is step 1. +The layout is based on a grid of sizes in x and y direction. +Choose how many distances you need in both directions. +The actual sizes and all other settings can be entered in the second step.""" - webinterface = True + webinterface = False ui_group = "Tray" - def __init__(self, input=None, webargs=False): + sx: list[float] = [] # arg input + sy: list[float] = [] # arg input + hwalls: list[list[bool]] = [] + vwalls: list[list[bool]] = [] + floors: list[list[bool]] = [] + + def __init__(self, input=None, webargs=False) -> None: Boxes.__init__(self) self.argparser = argparse.ArgumentParser() - self.argparser.add_argument( - "--x", action="store", type=int, default=2, - help="number of compartments side by side") - self.argparser.add_argument( - "--y", action="store", type=int, default=2, - help="number of compartments back to front") + self.buildArgParser("sx", "sy") self.argparser.add_argument( "--output", action="store", type=str, default="traylayout.txt", - help="name of the layout text file") + help="Name of the layout text file.") - # Use empty open and close methods to avoid initializing the whole - # drawing infrastructure - - def open(self): + def open(self) -> None: + # Use empty open and close methods to avoid initializing the whole drawing infrastructure. pass - def close(self): + def close(self) -> None: + # Use empty open and close methods to avoid initializing the whole drawing infrastructure. pass - def fillDefault(self, x, y): - self.x = [0.0] * x - self.y = [0.0] * y - self.hwalls = [[True for i in range(x)] for j in range(y + 1)] - self.vwalls = [[True for i in range(x + 1)] for j in range(y)] - self.floors = [[True for i in range(x)] for j in range(y)] + def fillDefault(self, sx: list[float], sy: list[float]) -> None: + self.sx = sx + self.sy = sy + x = len(sx) + y = len(sy) + self.hwalls = [[True for _ in range(x)] for _ in range(y + 1)] + self.vwalls = [[True for _ in range(x + 1)] for _ in range(y)] + self.floors = [[True for _ in range(x)] for _ in range(y)] - def __str__(self): + def __str__(self) -> str: r = [] - for i, x in enumerate(self.x): - r.append(" |" * (i) + " ,> %.1fmm\n" % x) + for i, x in enumerate(self.sx): + r.append(" |" * i + " ,> %.1fmm\n" % x) - for hwalls, vwalls, floors, y in zip( - self.hwalls, self.vwalls, self.floors, self.y): - r.append("".join(("+" + " -"[h] for h in hwalls)) + "+\n") - r.append("".join((" |"[v] + "X "[f] for v, f in zip(vwalls, floors))) - + " |"[vwalls[-1]] + " %.1fmm\n" % y) - r.append("".join(("+" + " -"[h] for h in self.hwalls[-1])) + "+\n") + for hwalls, vwalls, floors, y in zip(self.hwalls, self.vwalls, self.floors, self.sy): + r.append("".join("+" + " -"[h] for h in hwalls) + "+\n") + r.append("".join((" |"[v] + "X "[f] for v, f in zip(vwalls, floors))) + " |"[vwalls[-1]] + " %.1fmm\n" % y) + r.append("".join("+" + " -"[h] for h in self.hwalls[-1]) + "+\n") return "".join(r) - def render(self): - self.fillDefault(self.x, self.y) + def render(self) -> None: + self.fillDefault(self.sx, self.sy) with open(self.output, 'w') as f: f.write(str(self)) -class TrayLayout2(TrayLayout): - """Generate a typetray from a layout file""" + +class TrayLayout(Boxes): + """Generate a typetray from a layout file.""" # This class reads in the layout either from a file (with --input) or # as string (with --layout) and turns it into a drawing for a box. - webinterface = True + ui_group = "Tray" - description = """Edit the layout text graphics to adjust your tray. + description = """This is a two step process. This is step 2. +Edit the layout text graphics to adjust your tray. Put in the sizes for each column and row. You can replace the hyphens and -vertial bars representing the walls with a space character to remove the walls. +vertical bars representing the walls with a space character to remove the walls. You can replace the space characters representing the floor by a "X" to remove the floor for this compartment. """ - def __init__(self, input=None, webargs=False): - Boxes.__init__(self) + def __init__(self) -> None: + super().__init__() self.addSettingsArgs(boxes.edges.FingerJointSettings) - self.buildArgParser("h", "hi", "outside") - if not webargs: + self.addSettingsArgs(lids.LidSettings) + self.buildArgParser("h", "hi", "outside", "sx", "sy") + if self.UI == "web": self.argparser.add_argument( - "--input", action="store", type=argparse.FileType('r'), - default='traylayout.txt', - help="layout file") - self.layout = None + "--layout", action="store", type=str, default="\n") else: self.argparser.add_argument( - "--layout", action="store", type=str, default="") + "--input", action="store", type=argparse.FileType('r'), + default="traylayout.txt", + help="layout file") + self.layout = None - # Use normal open and close - open = Boxes.open - close = Boxes.close - - def vWalls(self, x, y): - "Number of vertical walls at a crossing" + def vWalls(self, x: int, y: int) -> int: + """Number of vertical walls at a crossing.""" result = 0 if y > 0 and self.vwalls[y - 1][x]: result += 1 - if y < len(self.y) and self.vwalls[y][x]: result += 1 - return result - def hWalls(self, x, y): - "Number of horizontal walls at a crossing" + def hWalls(self, x: int, y: int) -> int: + """Number of horizontal walls at a crossing.""" result = 0 if x > 0 and self.hwalls[y][x - 1]: result += 1 @@ -133,15 +131,18 @@ You can replace the space characters representing the floor by a "X" to remove t result += 1 return result - def vFloor(self, x, y): - "Is there floor under vertical wall" - return ((x > 0 and self.floors[y][x - 1]) or - (x < len(self.x) and self.floors[y][x])) + def vFloor(self, x: int, y: int) -> bool: + """Is there floor under vertical wall.""" + if y >= len(self.y): + return False - def hFloor(self, x, y): - "Is there foor under horizontal wall" - return ((y > 0 and self.floors[y - 1][x]) or - (y < len(self.y) and self.floors[y][x])) + return (x > 0 and self.floors[y][x - 1]) or (x < len(self.x) and self.floors[y][x]) + + def hFloor(self, x: int, y: int) -> bool: + """Is there floor under horizontal wall.""" + if x >= len(self.x): + return False + return (y > 0 and self.floors[y - 1][x]) or (y < len(self.y) and self.floors[y][x]) @restore def edgeAt(self, edge, x, y, length, angle=0): @@ -149,8 +150,7 @@ You can replace the space characters representing the floor by a "X" to remove t edge = self.edges.get(edge, edge) edge(length) - def render(self): - + def prepare(self): if self.layout: self.parse(self.layout.split('\n')) else: @@ -164,23 +164,17 @@ You can replace the space characters representing the floor by a "X" to remove t if self.hi: self.hi = self.adjustSize(self.hi, e2=False) - self.hi = hi = self.hi or self.h - - lx = len(self.x) - ly = len(self.y) - t = self.thickness - b = self.burn - t2 = self.thickness / 2.0 - - hasfloor = False - - for line in self.floors: - for f in line: - hasfloor |= f - - + self.hi = self.hi or self.h self.edges["s"] = boxes.edges.Slot(self, self.hi / 2.0) self.edges["C"] = boxes.edges.CrossingFingerHoleEdge(self, self.hi) + self.edges["D"] = boxes.edges.CrossingFingerHoleEdge(self, self.hi, outset=self.thickness) + + def walls(self, move=None): + lx = len(self.x) + ly = len(self.y) + # t = self.thickness + # b = self.burn + # t2 = self.thickness / 2.0 self.ctx.save() @@ -210,10 +204,13 @@ You can replace the space characters representing the floor by a "X" to remove t if self.hFloor(end, y): edges.append("f") else: - edges.append("e") # XXX E? + edges.append("E") lengths.append(self.x[end]) - edges.append("eCs"[self.vWalls(end + 1, y)]) + if self.hFloor(end, y) == 0 and self.hFloor(end + 1, y) == 0: + edges.append("EDs"[self.vWalls(end + 1, y)]) + else: + edges.append("eCs"[self.vWalls(end + 1, y)]) lengths.append(self.thickness) end += 1 @@ -256,22 +253,25 @@ You can replace the space characters representing the floor by a "X" to remove t if self.vFloor(x, end): edges.append("f") else: - edges.append("e") # XXX E? + edges.append("E") lengths.append(self.y[end]) - edges.append("eCs"[self.hWalls(x, end + 1)]) + if self.vFloor(x, end) == 0 and self.vFloor(x, end + 1) == 0: + edges.append("EDs"[self.hWalls(x, end + 1)]) + else: + edges.append("eCs"[self.hWalls(x, end + 1)]) lengths.append(self.thickness) end += 1 # remove last "slot" lengths.pop() edges.pop() - upper = [{ - "f": "e", - "s": "s", - "e": "e", - "E": "e", - "C": "e"}[e] for e in reversed(edges)] + upper = [{"f": "e", + "s": "s", + "e": "e", + "E": "e", + "C": "e", + "D": "e"}[e] for e in reversed(edges)] edges = ["e" if e == "s" else e for e in edges] self.rectangularWall(sum(lengths), h, [ boxes.edges.CompoundEdge(self, edges, lengths), @@ -283,22 +283,38 @@ You can replace the space characters representing the floor by a "X" to remove t self.ctx.restore() self.rectangularWall(10, h, "ffef", move="up only") - self.moveTo(2 * self.thickness, 2 * self.thickness) - self.ctx.save() - ########################################################## - ### Baseplate - ########################################################## + def base_plate(self, callback=None, move=None): + lx = len(self.x) + ly = len(self.y) + t = self.thickness + w = self.edges["F"].startwidth() + b = self.burn + t2 = self.thickness / 2.0 + + tw = sum(self.x) + (lx - 1) * t + 2 * w + th = sum(self.y) + (ly - 1) * t + 2 * w + + if self.move(tw, th, move, True): + return + + for i, (x, y, a) in enumerate(( + (w, w + b, 0), + (tw - w, w + b, 90), + (tw - w, th - w + b, 180), + (w, th - w + b, 270))): + self.cc(callback, i, x, y, a) # Horizontal lines - posy = 0 + posy = w - t for y in range(ly, -1, -1): - posx = self.thickness + posx = w for x in range(lx): if self.hwalls[y][x]: e = "F" else: e = "e" + if y < ly and self.floors[y][x]: if y > 0 and self.floors[y - 1][x]: # Inside Wall @@ -306,25 +322,30 @@ You can replace the space characters representing the floor by a "X" to remove t self.fingerHolesAt(posx, posy + t2, self.x[x], angle=0) else: # Top edge - self.edgeAt(e, posx + self.x[x], posy + t + b, self.x[x], + self.edgeAt(e, posx + self.x[x], + posy + w + b, self.x[x], -180) - if x == 0 or y == 0 or not self.floors[y - 1][x - 1]: - self.edgeAt("e", posx, posy + t + b, t, -180) - if x == lx - 1 or y == 0 or not self.floors[y - 1][x + 1]: - self.edgeAt("e", posx + self.x[x] + t, posy + t + b, t, -180) + if x == 0 or not self.floors[y][x - 1]: + self.edgeAt("e", posx - w, posy + w + b, w, 0) + elif y == 0 or not self.floors[y - 1][x - 1]: + self.edgeAt("e", posx - t, posy + w + b, t, 0) + if x == lx - 1 or not self.floors[y][x + 1]: + self.edgeAt("e", posx + self.x[x], posy + w + b, w, 0) elif y > 0 and self.floors[y - 1][x]: # Bottom Edge - self.edgeAt(e, posx, posy - b, self.x[x]) - if x == 0 or y == ly or not self.floors[y][x - 1]: - self.edgeAt("e", posx - t, posy - b, t) - if x == lx - 1 or y == ly or not self.floors[y][x + 1]: - self.edgeAt("e", posx + self.x[x], posy - b, t) + self.edgeAt(e, posx, posy - b + t - w, self.x[x]) + if x == 0 or not self.floors[y-1][x - 1]: + self.edgeAt("e", posx - w, posy + t - w - b, w) + elif x == 0 or y == ly or not self.floors[y][x - 1]: + self.edgeAt("e", posx - t, posy + t - w - b, t) + if x == lx - 1 or y == 0 or not self.floors[y-1][x + 1]: + self.edgeAt("e", posx + self.x[x], posy + t -w - b, w) posx += self.x[x] + self.thickness posy += self.y[y - 1] + self.thickness - posx = 0 + posx = w - t for x in range(lx + 1): - posy = self.thickness + posy = w for y in range(ly - 1, -1, -1): if self.vwalls[y][x]: e = "F" @@ -337,22 +358,29 @@ You can replace the space characters representing the floor by a "X" to remove t self.fingerHolesAt(posx + t2, posy, self.y[y]) else: # Right edge - self.edgeAt(e, posx + t + b, posy, self.y[y], 90) - if x == lx or y == 0 or not self.floors[y - 1][x]: - self.edgeAt("e", posx + t + b, posy + self.y[y], t, 90) - if x == lx or y == ly - 1 or not self.floors[y + 1][x]: - self.edgeAt("e", posx + t + b, posy - t, t, 90) + self.edgeAt(e, posx + w + b, posy, self.y[y], 90) + if y == 0 or not self.floors[y-1][x-1]: + self.edgeAt("e", posx + w + b, posy + self.y[y], w, 90) + elif x == lx or y == 0 or not self.floors[y - 1][x]: + self.edgeAt("e", posx + w + b, posy + self.y[y], t, 90) + if y == ly - 1 or not self.floors[y+1][x-1]: + self.edgeAt("e", posx + w + b, posy - w, w, 90) elif x < lx and self.floors[y][x]: # Left edge - self.edgeAt(e, posx - b, posy + self.y[y], self.y[y], -90) - if x == 0 or y == 0 or not self.floors[y - 1][x - 1]: - self.edgeAt("e", posx - b, posy + self.y[y] + t, t, -90) - if x == 0 or y == ly - 1 or not self.floors[y + 1][x - 1]: - self.edgeAt("e", posx -b, posy, t, -90) + self.edgeAt(e, posx + t - w - b, posy + self.y[y], self.y[y], -90) + if y == 0 or not self.floors[y - 1][x]: + self.edgeAt("e", posx + t - w - b, + posy + self.y[y] + w, w, -90) + elif x == 0 or y == 0 or not self.floors[y - 1][x - 1]: + self.edgeAt("e", posx + t - w - b, + posy + self.y[y] + t, t, -90) + if y == ly - 1 or not self.floors[y + 1][x]: + self.edgeAt("e", posx + t - w - b, posy, w, -90) posy += self.y[y] + self.thickness if x < lx: posx += self.x[x] + self.thickness + self.move(tw, th, move) def parse(self, input): x = [] @@ -369,7 +397,7 @@ You can replace the space characters representing the floor by a "X" to remove t continue if line[0] == '+': w = [] - for n, c in enumerate(line[:len(x)*2 + 1]): + for n, c in enumerate(line[:len(x) * 2 + 1]): if n % 2: if c == ' ': w.append(False) @@ -394,20 +422,20 @@ You can replace the space characters representing the floor by a "X" to remove t elif c == ' ': f.append(True) else: - raise ValueError('''Can't parse line %i in layout: expected " ", "x" or "X" for char #%i''' % (nr+1, n+1)) + raise ValueError("""Can't parse line %i in layout: expected " ", "x" or "X" for char #%i""" % (nr + 1, n + 1)) else: if c == ' ': w.append(False) elif c == '|': w.append(True) else: - raise ValueError('''Can't parse line %i in layout: expected " ", or "|" for char #%i''' % (nr+1, n+1)) + raise ValueError("""Can't parse line %i in layout: expected " ", or "|" for char #%i""" % (nr + 1, n + 1)) floors.append(f) vwalls.append(w) m = re.match(r"([ |][ xX])+[ |]\s*(\d*\.?\d+)\s*mm\s*", line) if not m: - raise ValueError('''Can't parse line %i in layout: Can read height of the row''' % (nr+1)) + raise ValueError("""Can't parse line %i in layout: Can read height of the row""" % (nr + 1)) else: y.append(float(m.group(2))) @@ -428,11 +456,17 @@ You can replace the space characters representing the floor by a "X" to remove t raise ValueError("Wrong number of vertical wall lines: %i (%i expected)" % (len(vwalls), ly)) for nr, walls in enumerate(vwalls): if len(walls) != lx + 1: - raise ValueError( - "Wrong number of vertical walls in line %i: %i (%i expected)" % (nr, len(walls), lx + 1)) + raise ValueError("Wrong number of vertical walls in line %i: %i (%i expected)" % (nr, len(walls), lx + 1)) self.x = x self.y = y self.hwalls = hwalls self.vwalls = vwalls self.floors = floors + + def render(self) -> None: + self.prepare() + self.walls() + self.base_plate(move="up") + self.lid(sum(self.x) + (len(self.x)-1) * self.thickness, + sum(self.y) + (len(self.y)-1) * self.thickness) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/trianglelamp.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/trianglelamp.py index 354c14c..c36d713 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/trianglelamp.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/trianglelamp.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,10 +15,11 @@ from boxes import * + class CornerEdge(edges.Edge): char = "C" - - def startwidth(self): + + def startwidth(self) -> float: return self.boxes.thickness * math.tan(math.radians(90-22.5)) def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw): @@ -31,14 +31,14 @@ class CornerEdge(edges.Edge): self.ctx.stroke() self.set_source_color(Color.BLACK) super().__call__(length, bedBolts=None, bedBoltSettings=None, **kw) - + class TriangleLamp(Boxes): """Triangle LED Lamp""" ui_group = "Misc" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, finger=3.0,space=3.0, @@ -105,7 +105,7 @@ class TriangleLamp(Boxes): self.rectangularWall(l, h, "F"+C+"e" + C, callback=[self.CB(l, c*2**.5)], move="up") - + self.rectangularTriangle(c, c, "ffe", num=2, move="right", callback=[ lambda:self.hole(2/3.*c,1/3.*c, r1)]) self.rectangularTriangle(c, c, "fef", num=4, move="up", callback=[ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/two_piece.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/two_piece.py index 78403f9..8ceffa8 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/two_piece.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/two_piece.py @@ -1,4 +1,3 @@ -#!/usr/bin/python3 # Copyright (C) 2013-2018 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -18,26 +17,26 @@ from boxes import * class TwoPiece(Boxes): - """A two piece box where top slips over the bottom half to form + """A two piece box where top slips over the bottom half to form the enclosure. """ description = """ -Set *hi* larger than *h* to leave gap between the inner and outer shell. This can be used to make opening the box easier. Set *hi* smaller to only have a small inner ridge that will allow the content to be momre visible after opening. +Set *hi* larger than *h* to leave gap between the inner and outer shell. This can be used to make opening the box easier. Set *hi* smaller to only have a small inner ridge that will allow the content to be more visible after opening. ![Bottom view](static/samples/TwoPiece2.jpg) """ ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.buildArgParser("x", "y", "h", "hi", "outside") self.addSettingsArgs(edges.FingerJointSettings, finger=2.0, space=2.0) self.argparser.add_argument( "--play", action="store", type=float, default=0.15, - help="play between the two parts as multipleof the wall thickness") + help="play between the two parts as multiple of the wall thickness") def render(self): # adjust to the variables you want in the local scope @@ -67,4 +66,3 @@ Set *hi* larger than *h* to leave gap between the inner and outer shell. This ca self.rectangularWall(x, y, "hhhh", bedBolts=None, move="right") self.rectangularWall(x+d, y+d, "FFFF", bedBolts=None, move="right") - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/typetray.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/typetray.py index ca6d47e..80fd701 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/typetray.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/typetray.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,22 +14,66 @@ # along with this program. If not, see . from boxes import * -from boxes.lids import _TopEdge +from boxes import Color, edges +from boxes.lids import LidSettings, _TopEdge + + +class FingerHoleEdgeSettings(edges.Settings): + """Settings for FingerHoleEdge +Values: +* absolute_params + + * radius : 10.0 : maximal radius (in mm) + * absolute_depth : 0.0 : depth of the hole in mm (in addition to the relative) + * absolute_width : 0.0 : width of the hole in mm (in addition to the relative) + * relative_depth : 0.0 : depth of the hole as fraction of the wall + * relative_width : 0.3 : width of the hole as fraction of the wall width + +""" + absolute_params = { + "radius": 10., + "absolute_depth": 0.0, + "relative_depth" : 0.9, + "absolute_width" : 0.0, + "relative_width" : 0.3, + } + + wallheight = 0.0 + +class FingerHoleEdge(edges.BaseEdge): + """An edge with room to get your fingers around cards""" + + char = "A" + + def __call__(self, length, **kw): + + width = min(self.settings.absolute_width + length * self.settings.relative_width, length) + depth = min(self.settings.absolute_depth + self.settings.wallheight * self.settings.relative_depth, self.settings.wallheight) + + r = min(width/2, depth, self.settings.radius) + + if depth < 1e-9 or width < 1e-9: + self.boxes.edge(length, tabs=2) + return + poly = (((length - width) / 2, 1), 90, depth-r, (-90, r)) + self.polyline(*poly, (width - 2*r, 1), *reversed(poly)) + class TypeTray(_TopEdge): """Type tray - allows only continuous walls""" ui_group = "Tray" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addTopEdgeSettings(fingerjoint={"surroundingspaces": 0.5}, roundedtriangle={"outset" : 1}) + self.addSettingsArgs(LidSettings) self.buildArgParser("sx", "sy", "h", "hi", "outside", "bottom_edge", "top_edge") self.argparser.add_argument( "--back_height", action="store", type=float, default=0.0, - help="additional height of the back wall - e top egde only") + help="additional height of the back wall - e top edge only") self.argparser.add_argument( "--radius", action="store", type=float, default=0.0, help="radius for strengthening side walls with back_height") @@ -41,8 +84,55 @@ class TypeTray(_TopEdge): "--gripwidth", action="store", type=float, default=70, dest="gw", help="width of th grip hole in mm (zero for no hole)") self.argparser.add_argument( - "--handle", type=boolarg, default=False, help="add handle to the bottom (changes bottom edge in the front)", - ) + "--handle", type=boolarg, default=False, help="add handle to the bottom (changes bottom edge in the front)") + self.argparser.add_argument( + "--fingerholes", action="store", type=str, default="none", + choices=["none", "inside-only", "front", "back", "front-and-back"], + help="Decide which outer walls should have finger hole, too") + + label_group = self.argparser.add_argument_group("Compartment Labels") + label_group.add_argument( + "--text_size", action="store", type=int, default=12, + help="Textsize in mm for the traycontent") + label_group.add_argument( + "--text_alignment", action="store", type=str, default="left", + choices=['left', 'center', 'right'], + help="Text Alignment") + label_group.add_argument( + "--text_distance_x", action="store", type=float, default=2.0, + help="Distance in X from edge of tray in mm. Has no effect when text is centered.") + label_group.add_argument( + "--text_distance_y", action="store", type=float, default=2.0, + help="Distance in Y from edge of tray in mm.") + label_group.add_argument( + "--text_at_front", action="store", type=boolarg, default=False, + help="Start compartement labels on the front") + if self.UI == "web": + label_group.add_argument( + "--label_text", action="store", type=str, default="\n", + help="Every line is the text for one compartment. Beginning with front left") + else: + label_group.add_argument( + "--label_file", action="store", type=argparse.FileType('r'), + default="labels.txt", + help="file with compartment labels. One line per compartment") + + self.addSettingsArgs(FingerHoleEdgeSettings) + + @property + def fingerholedepth(self): + if self.fingerhole == 'custom': + return self.fingerhole_depth + elif self.fingerhole == 'regular': + a = self.h/4 + if a < 35: + return a + else: + return 35 + elif self.fingerhole == 'deep': + return self.h-self.thickness-10 + elif self.fingerhole == 'none': + return 0 def xSlots(self): posx = -0.5 * self.thickness @@ -82,6 +172,40 @@ class TypeTray(_TopEdge): r = min(self.gw, self.gh) / 2.0 self.rectangularHole(x / 2.0, self.gh * 1.5, self.gw, self.gh, r) + def textCB(self): + ## declare text-variables + textsize = self.text_size + texty = self.hi - textsize - self.text_distance_y + if self.text_alignment == 'center': + texty -= self.fingerholedepth + textdistance = self.sx[0] + self.thickness + + ## Generate text-fields for each tray + for n in range(len(self.sx)): + # Break for-loop if further list is empty + if self.textnumber >= len(self.textcontent): + break + + textx = n * (self.sx[0] + self.thickness) + # Calculate textposition + if self.text_alignment == 'left': + textx += self.text_distance_x + elif self.text_alignment == 'center': + textx += self.sx[0] / 2 + elif self.text_alignment == 'right': + textx += self.sx[0] - self.text_distance_x + + # Generate text + self.text( + "%s" % self.textcontent[self.textnumber], + textx, + texty, + 0, + align=self.text_alignment, + fontsize=textsize, + color=Color.ETCHING) + self.textnumber +=1 + def render(self): if self.outside: self.sx = self.adjustSize(self.sx) @@ -97,6 +221,12 @@ class TypeTray(_TopEdge): hi = self.hi = self.hi or h t = self.thickness + s = FingerHoleEdgeSettings(self.thickness, True, + **self.edgesettings.get("FingerHoleEdge", {})) + s.wallheight = 0 if self.fingerholes == "none" else self.hi + p = FingerHoleEdge(self, s) + self.addPart(p) + # outer walls b = self.bottom_edge tl, tb, tr, tf = self.topEdges(self.top_edge) @@ -104,31 +234,17 @@ class TypeTray(_TopEdge): bh = self.back_height if self.top_edge == "e" else 0.0 + if hasattr(self, "label_text"): + self.textcontent = self.label_text.split("\r\n") + else: + with open(self.label_file) as f: + self.textcontent = f.readlines() + self.textnumber = 0 + # x sides self.ctx.save() - # outer walls - front/back - if bh: - self.rectangularWall(x, h+bh, [b, "f", tb, "f"], - callback=[self.xHoles], - ignore_widths=[], - move="up", label="back") - self.rectangularWall(x, h, ["f" if self.handle else b, "f", tf, "f"], - callback=[self.mirrorX(self.xHoles, x), - None, self.gripHole], - move="up", label="front") - else: - self.rectangularWall(x, h, [b, "F", tb, "F"], - callback=[self.xHoles], - ignore_widths=[1, 6], - move="up", label="back") - self.rectangularWall(x, h, ["f" if self.handle else b, "F", tf, "F"], - callback=[self.mirrorX(self.xHoles, x), - None, self.gripHole], - ignore_widths=[] if self.handle else [1, 6], - move="up", label="front") - # floor if b != "e": if self.handle: @@ -136,18 +252,63 @@ class TypeTray(_TopEdge): else: self.rectangularWall(x, y, "ffff", callback=[self.xSlots, self.ySlots], move="up", label="bottom") + # front + if self.text_at_front: + frontCBs = [lambda:(self.textCB(), self.mirrorX(self.xHoles, x)()), + None, self.gripHole] + else: + frontCBs = [self.mirrorX(self.xHoles, x), None, self.gripHole] + + # finger holes at front wall + if not self.closedtop and \ + self.fingerholes in ("front", "front-and-back"): + tf = edges.SlottedEdge(self, self.sx[::-1], "A") + + if bh: + self.rectangularWall( + x, h, ["f" if self.handle else b, "f", tf, "f"], + callback=frontCBs, move="up", label="front") + else: + self.rectangularWall( + x, h, ["f" if self.handle else b, "F", tf, "F"], + callback=frontCBs, ignore_widths=[] if self.handle else [1, 6], + move="up", label="front") + + # Inner walls be = "f" if b != "e" else "e" + ###### for i in range(len(self.sy) - 1): + e = [edges.SlottedEdge(self, self.sx, be), "f", - edges.SlottedEdge(self, self.sx[::-1], "e", slots=0.5 * hi), "f"] + edges.SlottedEdge(self, self.sx[::-1], "A", slots=0.5 * hi), "f"] + if self.closedtop and sameh: e = [edges.SlottedEdge(self, self.sx, be), "f", edges.SlottedEdge(self, self.sx[::-1], "f", slots=0.5 * hi), "f"] - self.rectangularWall(x, hi, e, move="up", label=f"inner x {i+1}") + self.rectangularWall(x, hi, e, move="up", callback=[self.textCB], + label=f"inner x {i+1}") + + # back + + # finger holes at back wall + if not self.closedtop and \ + self.fingerholes in ("back", "front-and-back"): + tb = edges.SlottedEdge(self, self.sx, "A") + + if bh: + self.rectangularWall(x, h+bh, [b, "f", tb, "f"], + callback=[self.xHoles], + ignore_widths=[], + move="up", label="back") + else: + self.rectangularWall(x, h, [b, "F", tb, "F"], + callback=[self.xHoles], + ignore_widths=[1, 6], + move="up", label="back") # top / lid if self.closedtop and sameh: @@ -156,6 +317,7 @@ class TypeTray(_TopEdge): self.xSlots, self.ySlots], move="up", label="top") else: self.drawLid(x, y, self.top_edge) + self.lid(x, y, self.top_edge) self.ctx.restore() self.rectangularWall(x, hi, "ffff", move="right only") @@ -192,7 +354,3 @@ class TypeTray(_TopEdge): e = [edges.SlottedEdge(self, self.sy, be, slots=0.5 * hi),"f", edges.SlottedEdge(self, self.sy[::-1], "f"), "f"] self.rectangularWall(y, hi, e, move="up", label=f"inner y {i+1}") - - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ubox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ubox.py index dc4fc5b..05f7e8f 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ubox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/ubox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2017 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,26 +14,23 @@ # along with this program. If not, see . from boxes import * -from boxes.lids import _TopEdge, _ChestLid -import math +from boxes.lids import LidSettings, _TopEdge -class UBox(_TopEdge, _ChestLid): + +class UBox(_TopEdge): """Box various options for different stypes and lids""" ui_group = "FlexBox" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addTopEdgeSettings() self.addSettingsArgs(edges.FlexSettings) + self.addSettingsArgs(LidSettings) self.buildArgParser("top_edge", "x", "y", "h") self.argparser.add_argument( "--radius", action="store", type=float, default=30.0, help="radius of bottom corners") - self.argparser.add_argument( - "--lid", action="store", type=str, default="default (none)", - choices=("default (none)", "chest", "flat"), - help="additional lid") self.angle = 0 def U(self, x, y, r, edge="e", move=None, label=""): @@ -85,7 +81,7 @@ class UBox(_TopEdge, _ChestLid): self.edgeCorner("F", e[nr]) e[nr](h) self.edgeCorner(e[nr], "F") - + self.move(tw, th, move, label=label) def render(self): @@ -93,15 +89,13 @@ class UBox(_TopEdge, _ChestLid): self.radius = r = min(r, x/2.0, y) - + _ = self.translations.gettext t1, t2, t3, t4 = self.topEdges(self.top_edge) - self.U(x, y, r, t1, move="right", label="left") - self.U(x, y, r, t3, move="up", label="right") - self.U(x, y, r, t3, move="left only", label="invisible") - self.Uwall(x, y, h, r, [t2, t4], move="up", label="wall") + self.U(x, y, r, t1, move="right", label=_("left")) + self.U(x, y, r, t3, move="up", label=_("right")) + self.U(x, y, r, t3, move="left only") + self.Uwall(x, y, h, r, [t2, t4], move="up", label=_("wall")) self.drawLid(x, h, self.top_edge) - self.drawAddOnLid(x, h, self.lid) - - + self.lid(x, h, self.top_edge) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/unevenheightbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/unevenheightbox.py index 93bb623..b6318fa 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/unevenheightbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/unevenheightbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2018 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -22,7 +21,7 @@ class UnevenHeightBox(Boxes): ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings) self.addSettingsArgs(edges.GroovedSettings) @@ -99,6 +98,3 @@ class UnevenHeightBox(Boxes): (" only" if h2 == h1 == 0.0 else "")) self.trapezoidWall(x, h1, h0, "FF" + edge_types[0] + "F", move="right" + (" only" if h1 == h0 == 0.0 else "")) - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/universalbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/universalbox.py index 35407f4..9f1f6cf 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/universalbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/universalbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,19 +14,22 @@ # along with this program. If not, see . from boxes import * +from boxes import lids from boxes.edges import Bolts -from boxes.lids import _TopEdge, _ChestLid +from boxes.lids import _TopEdge -class UniversalBox(_TopEdge, _ChestLid): + +class UniversalBox(_TopEdge): """Box with various options for different styles and lids""" ui_group = "Box" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) self.addTopEdgeSettings(roundedtriangle={"outset" : 1}, hinge={"outset" : True}) self.addSettingsArgs(edges.FlexSettings) + self.addSettingsArgs(lids.LidSettings) self.buildArgParser("top_edge", "bottom_edge", "x", "y", "h", "outside") self.argparser.add_argument( @@ -35,10 +37,6 @@ class UniversalBox(_TopEdge, _ChestLid): default="finger joints", choices=("finger joints", "finger holes"), help="connections used for the vertical edges") - self.argparser.add_argument( - "--lid", action="store", type=str, default="default (none)", - choices=("default (none)", "chest", "flat"), - help="additional lid (for straight top_edge only)") def top_hole(self, x, y, top_edge): t = self.thickness @@ -62,7 +60,7 @@ class UniversalBox(_TopEdge, _ChestLid): x, y, h = self.x, self.y, self.h t = self.thickness - t1, t2, t3, t4 = self.topEdges(self.top_edge) + tl, tb, tr, tf = self.topEdges(self.top_edge) b = self.edges.get(self.bottom_edge, self.edges["F"]) d2 = Bolts(2) @@ -78,12 +76,12 @@ class UniversalBox(_TopEdge, _ChestLid): self.h = h = self.adjustSize(h, b, self.top_edge) with self.saved_context(): - self.rectangularWall(x, h, [b, sideedge, t1, sideedge], + self.rectangularWall(x, h, [b, sideedge, tf, sideedge], ignore_widths=[1, 6], - bedBolts=[d2], move="up", label="left") - self.rectangularWall(x, h, [b, sideedge, t3, sideedge], + bedBolts=[d2], move="up", label="front") + self.rectangularWall(x, h, [b, sideedge, tb, sideedge], ignore_widths=[1, 6], - bedBolts=[d2], move="up", label="right") + bedBolts=[d2], move="up", label="back") if self.bottom_edge != "e": self.rectangularWall(x, y, "ffff", bedBolts=[d2, d3, d2, d3], move="up", label="bottom") @@ -93,16 +91,14 @@ class UniversalBox(_TopEdge, _ChestLid): lambda:self.top_hole(x, y, self.top_edge)], move="up", label="top hole") self.set_source_color(Color.BLACK) self.drawLid(x, y, self.top_edge, [d2, d3]) - self.drawAddOnLid(x, y, self.lid) + self.lid(x, y, self.top_edge) - self.rectangularWall(x, h, [b, sideedge, t3, sideedge], + self.rectangularWall(x, h, [b, sideedge, tf, sideedge], ignore_widths=[1, 6], bedBolts=[d2], move="right only", label="invisible") - self.rectangularWall(y, h, [b, "f", t2, "f"], + self.rectangularWall(y, h, [b, "f", tl, "f"], ignore_widths=[1, 6], - bedBolts=[d3], move="up", label="back") - self.rectangularWall(y, h, [b, "f", t4, "f"], + bedBolts=[d3], move="up", label="left") + self.rectangularWall(y, h, [b, "f", tr, "f"], ignore_widths=[1, 6], - bedBolts=[d3], move="up", label="front") - - + bedBolts=[d3], move="up", label="right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallcaliperholder.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallcaliperholder.py index 7f1ee0b..fe98af4 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallcaliperholder.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallcaliperholder.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,13 +13,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from boxes import * from boxes.walledges import _WallMountedBox + class WallCaliper(_WallMountedBox): """Holds a single caliper to a wall""" - def __init__(self): + def __init__(self) -> None: super().__init__() # remove cli params you do not need @@ -30,19 +29,19 @@ class WallCaliper(_WallMountedBox): "--width", action="store", type=float, default=18.0, help="width of the long end") self.argparser.add_argument( - "--heigth", action="store", type=float, default=6.0, - help="heigth of the body") + "--height", action="store", type=float, default=6.0, + help="height of the body") def side(self, move=None): t = self.thickness h = self.h - hc = self.heigth + hc = self.height tw = self.edges["b"].spacing() + hc + 8*t if self.move(tw, h, move, True): return - + self.moveTo(self.edges["b"].startwidth()) self.polyline(5*t+hc, (90, 2*t), h/2-2*t, (180, 1.5*t), 0.25*h, -90, hc, -90, 0.75*h-2*t, (90, 2*t), 2*t, 90) @@ -56,7 +55,7 @@ class WallCaliper(_WallMountedBox): t = self.thickness h = self.h - + self.side(move="right") self.side(move="right") w = self.width diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallchiselholder.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallchiselholder.py index e8a60ee..848aab4 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallchiselholder.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallchiselholder.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -17,6 +16,7 @@ from boxes import * from boxes.walledges import _WallMountedBox + class FrontEdge(edges.Edge): def __call__(self, length, **kw): @@ -30,13 +30,13 @@ class FrontEdge(edges.Edge): for i in range(self.number): self.polyline(l, (180-a, r), 0, (-360+2*a, rh), 0, (180-a, r), l) - - + + class WallChiselHolder(_WallMountedBox): """Wall tool holder for chisels, files and similar tools""" - def __init__(self): + def __init__(self) -> None: super().__init__() self.buildArgParser(h=120) @@ -79,18 +79,18 @@ class WallChiselHolder(_WallMountedBox): return not (i % 3) def braces(self): - return sum((self.brace(i) for i in range(self.number+1))) + return sum(self.brace(i) for i in range(self.number+1)) def backCB(self): n = self.number rt = self.holediameter wt = self.tooldiameter t = self.thickness - + d = min(2*t, (wt-rt)/4.) self.wallHolesAt(d, 0, self.h, 90) self.wallHolesAt(n*wt-d, 0, self.h, 90) - + for i in range(1, n): if self.brace(i): self.wallHolesAt(i*wt, 0, self.h, 90) @@ -101,11 +101,11 @@ class WallChiselHolder(_WallMountedBox): wt = self.tooldiameter t = self.thickness l = self.depth - + d = min(2*t, (wt-rt)/4.) self.fingerHolesAt(d, 0, l, 90) self.fingerHolesAt(n*wt-d, 0, l, 90) - + for i in range(1, n): if self.brace(i): self.fingerHolesAt(i*wt, 0, l, 90) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallconsole.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallconsole.py index 39d1169..6b769c1 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallconsole.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallconsole.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,13 +13,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from boxes import * from boxes.walledges import _WallMountedBox + class WallConsole(_WallMountedBox): """Outset and angled plate to mount stuff to""" - def __init__(self): + def __init__(self) -> None: super().__init__() self.buildArgParser(sx=100, h=100, outside=True) @@ -58,7 +57,7 @@ class WallConsole(_WallMountedBox): bd = self.bottom_depth self.front = (h**2 + (td-bd)**2)**0.5 - + self.rectangularWall(x, h, "eCec", callback=[self.backHoles], move="up") self.rectangularWall(x, self.front, "eFeF", diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/walldrillbox.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/walldrillbox.py index 55d963d..1789126 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/walldrillbox.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/walldrillbox.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -15,14 +14,16 @@ # along with this program. If not, see . from boxes import * -from .drillstand import DrillStand from boxes.walledges import _WallMountedBox +from .drillstand import DrillStand + + class WallDrillBox(DrillStand, _WallMountedBox): """Box for drills with each compartment with a different height""" ui_group = "WallMounted" - def __init__(self): + def __init__(self) -> None: _WallMountedBox.__init__(self) # don't call DrillStand.__init__ self.addSettingsArgs(edges.StackableSettings, height=1.0, width=3) @@ -47,7 +48,7 @@ class WallDrillBox(DrillStand, _WallMountedBox): self.xOutsideWall(sh[-1], "hCec", move="up") self.rectangularWall(x/math.cos(bottom_angle)-t*math.tan(bottom_angle), y, "fefe", callback=[self.bottomCB], move="up") - + self.sideWall(edges="eBf", foot_height=2*t, move="right") for i in range(1, len(sx)): self.yWall(i, move="right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/walledges.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/walledges.py index 9369ac3..71c5978 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/walledges.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/walledges.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,13 +13,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from boxes import * from boxes.walledges import _WallMountedBox + class WallEdges(_WallMountedBox): """Shows the different edge types for wall systems""" - def __init__(self): + def __init__(self) -> None: super().__init__() self.buildArgParser(h=120) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallpinrow.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallpinrow.py index 8194315..18ff72e 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallpinrow.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallpinrow.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -17,6 +16,7 @@ from boxes import * from boxes.walledges import _WallMountedBox + class PinEdge(edges.BaseEdge): def __call__(self, length, **kw): w2 = self.settings.pinwidth/2 @@ -24,7 +24,7 @@ class PinEdge(edges.BaseEdge): s = self.settings.pinspacing inc = self.settings.pinspacing_increment t = self.settings.thickness - + pin = [0, -90, l+t-w2, (180, w2), l+t-w2, -90] self.edge(s/2-w2) @@ -34,13 +34,13 @@ class PinEdge(edges.BaseEdge): s+=inc self.polyline(*pin, s/2-w2-inc/4) - def margin(self): + def margin(self) -> float: return self.settings.thickness+self.settings.pinlength class WallPinRow(_WallMountedBox): """Outset and angled plate to mount stuff to""" - def __init__(self): + def __init__(self) -> None: super().__init__() self.argparser.add_argument( @@ -68,7 +68,7 @@ class WallPinRow(_WallMountedBox): self.argparser.add_argument( "--h", action="store", type=float, default=50.0, help="height of the front plate (in mm) - needs to be at least 7 time the thickness") - + def frontCB(self): s = self.pinspacing inc = self.pinspacing_increment @@ -84,7 +84,7 @@ class WallPinRow(_WallMountedBox): for i in range(1, self.hooks-1): self.fingerHolesAt(i*self.x/(self.hooks-1), self.h/2, self.h/2) - + def backCB(self): t = self.thickness self.fingerHolesAt(0, 2*t, self.x, 0) @@ -100,14 +100,14 @@ class WallPinRow(_WallMountedBox): t = self.thickness sh = math.sin(ar)*6*t + math.cos(ar)*h - + tw = self.edges["a"].margin() + math.sin(ar)*h + math.cos(ar)*6*t th = sh + 6 if self.move(tw, th, move, True): return self.moveTo(self.edges["a"].margin()) - + self.polyline(math.sin(ar)*h, a, 4*t) self.fingerHolesAt(-3.5*t, 0, h/2, 90) self.edgeCorner("e", "h") @@ -115,10 +115,10 @@ class WallPinRow(_WallMountedBox): self.polyline(0, 90-a, math.cos(ar)*6*t, 90) self.edges["a"](sh) self.corner(90) - + self.move(tw, th, move) - + def supportWall(self, move=None): a = self.angle ar = math.radians(a) @@ -126,7 +126,7 @@ class WallPinRow(_WallMountedBox): t = self.thickness sh = math.sin(ar)*6*t + math.cos(ar)*h - + tw = self.edges["a"].margin() + max( math.sin(ar)*h/2 + math.cos(ar)*5*t, math.sin(ar)*h) @@ -149,10 +149,10 @@ class WallPinRow(_WallMountedBox): else: self.edges["a"](h/2) self.corner(90) - + self.move(tw, th, move) - - + + def render(self): self.generateWallEdges() diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallplaneholder.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallplaneholder.py index d78372a..e4e9aca 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallplaneholder.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallplaneholder.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,13 +13,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from boxes import * from boxes.walledges import _WallMountedBox + class WallPlaneHolder(_WallMountedBox): """Hold a plane to a wall""" - def __init__(self): + def __init__(self) -> None: super().__init__() self.argparser.add_argument( @@ -28,10 +27,10 @@ class WallPlaneHolder(_WallMountedBox): help="width of the plane") self.argparser.add_argument( "--length", action="store", type=float, default=250, - help="legth of the plane") + help="length of the plane") self.argparser.add_argument( "--hold_length", action="store", type=float, default=30, - help="legth of the part hiolding the plane over the front") + help="length of the part holding the plane over the front") self.argparser.add_argument( "--height", action="store", type=float, default=80, help="height of the front of plane") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallpliersholder.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallpliersholder.py index cf59736..40a62f3 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallpliersholder.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallpliersholder.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -17,10 +16,11 @@ from boxes import * from boxes.walledges import _WallMountedBox + class WallPliersHolder(_WallMountedBox): """Bar to hang pliers on""" - def __init__(self): + def __init__(self) -> None: super().__init__() self.buildArgParser(sx="100*3", y=50, h=50, outside=True) @@ -31,7 +31,7 @@ class WallPliersHolder(_WallMountedBox): def brace(self, h, d, a, outside=False, move=None): t = self.thickness - + tw = d + self.edges["b"].spacing() + self.edges["f"].spacing() th = self.h_t @@ -75,15 +75,14 @@ class WallPliersHolder(_WallMountedBox): if self.outside: self.sx = self.adjustSize(self.sx) - + sx, y, h = self.sx, self.y, self.h t = self.thickness r = y / 4 self.h_t = h + (y+t-r) * math.tan(math.radians(90-self.angle)) + r - + self.rectangularWall(sum(sx) + (len(sx)-1) * t, h, "efef", callback=[self.frontCB], move="up") self.rectangularWall(sum(sx) + (len(sx)-1) * t, self.h_t, "eCec", callback=[self.backCB], move="up") for i in range(len(sx)+1): self.brace(h, y, self.angle, i<2, move="right") - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallrack.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallrack.py new file mode 100644 index 0000000..d28b7f8 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallrack.py @@ -0,0 +1,92 @@ +# Copyright (C) 2013-2023 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from functools import partial + +from boxes import * + + +class WallRack(Boxes): + """Wall mountable rack for spices or other items""" + + ui_group = "WallMounted" + + def __init__(self): + Boxes.__init__(self) + + self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=1.0) + self.addSettingsArgs(edges.MountingSettings) + self.addSettingsArgs(edges.HandleEdgeSettings) + + self.buildArgParser(x=200, y=50, sh="100*3", outside=False) + self.argparser.add_argument( + "--top_edge", action="store", + type=ArgparseEdgeType("eEGy"), choices=list("eEGy"), + default="G", help="edge type for top edge") + self.argparser.add_argument("--full_height_top", type=boolarg, default=True, help="Add full height of topmost rack to the back panel") + self.argparser.add_argument( + "--wall_height", action="store", type=float, default=20.0, + help="height of walls") + self.argparser.add_argument( + "--back_height", action="store", type=float, default=1.5, + help="height of the back as fraction of the front height") + self.argparser.add_argument( + "--side_edges", action="store", + type=ArgparseEdgeType("Fh"), choices=list("Fh"), + default="h", help="edge type holding the shelfs together") + self.argparser.add_argument( + "--flat_bottom", type=boolarg, default=False, help="Make bottom Flat, so that the rack can also stand") + + def generate_shelves(self, x, y, front_height, back_height): + se = self.side_edges + for i in range(len(self.sh)): + self.rectangularWall(x, y, "ffff", move="up", label=f"shelf {i+1}") + self.rectangularWall(x, front_height, se + "fef", move="up", label=f"front lip {i+1}") + self.trapezoidWall(y, front_height, back_height, se + "fe" + se, move="right", label=f"right lip {i+1}") + self.trapezoidWall(y, front_height, back_height, se + "fe" + se, move="up", label=f"left lip {i+1}") + self.move(y + self.thickness*2, back_height, "left", before=True) + + #Generate finger holes for back part + def generate_finger_holes(self, x, back_height): + t = self.thickness + pos_y = 0 + for h in self.sh: + self.fingerHolesAt(t*0.5, pos_y + 0.5*t, x, 0) + self.fingerHolesAt(0, pos_y + t, back_height, 90) + self.fingerHolesAt(x+t, pos_y + t, back_height, 90) + pos_y += h + + def render(self): + x, y, front_height = self.x, self.y, self.wall_height + back_height = front_height * self.back_height + t = self.thickness + if self.outside: + x = self.adjustSize(x, "h", "f") + y = self.adjustSize(y) + front_height = self.adjustSize(front_height) + back_height = self.adjustSize(back_height) + + if self.full_height_top: + total_height = sum(self.sh) + else: + total_height = sum(self.sh[:-1]) + back_height + + be = "e" if self.flat_bottom and self.side_edges == "F" else "E" + if be == "E" and self.full_height_top: + total_height -= t + + self.rectangularWall(x+self.thickness, total_height, be + "E" + self.top_edge + "E", + callback=[partial(self.generate_finger_holes, x, back_height)], label="back wall", move="right") + self.generate_shelves(x, y, front_height, back_height) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallrollholder.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallrollholder.py new file mode 100644 index 0000000..579c0c4 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallrollholder.py @@ -0,0 +1,118 @@ +# Copyright (C) 2013-2016 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * +from boxes.walledges import _WallMountedBox + + +class WallRollHolder(_WallMountedBox): + """Holder for kitchen rolls or other rolls""" + + description = """Needs a dowel or pipe as axle.""" + + ui_group = "WallMounted" + + def __init__(self) -> None: + super().__init__() + + self.argparser.add_argument( + "--width", action="store", type=float, default=275, + help="length of the axle in mm") + self.argparser.add_argument( + "--diameter", action="store", type=float, default=120, + help="maximum diameter of the roll in mm (choose generously)") + self.argparser.add_argument( + "--height", action="store", type=float, default=80, + help="height of mounting plate in mm") + self.argparser.add_argument( + "--axle", action="store", type=float, default=25, + help="diameter of the axle in mm including play") + + def side(self, move=None): + d = self.diameter + a = self.axle + h = self.height + t = self.thickness + + tw, th = h, (d + a) / 2 + 3 * t + self.edges["B"].spacing() + + if self.move(tw, th, move, True): + return + + self.moveTo(0, self.edges["B"].margin()) + self.edges["B"](h) + self.fingerHolesAt(-(a/2+3*t), self.burn+self.edges["B"].endwidth(), d/2, 90) + self.polyline(0, 90, self.edges["B"].endwidth() + d/2, + (90, a/2 + 3*t)) + + r = a/2 + 3*t + a = math.atan2(float(d/2), (h-a-6*t)) + alpha = math.degrees(a) + + self.corner(alpha, r) + self.edge(((h-2*r)**2+(d/2)**2)**0.5) + self.corner(90-alpha, r) + self.edge(self.edges["B"].startwidth()) + self.corner(90) + + self.move(tw, th, move) + + def backCB(self): + t = self.thickness + a = self.axle + h = self.height + w = self.width + + plate = w + 2*t + h/2 + + self.wallHolesAt(h/4+t/2-3*t, 0, h, 90) + self.fingerHolesAt(h/4-3*t, h-3*t-a/2, h/4, 180) + + self.wallHolesAt(h/4+t/2+t-3*t+w, 0, h, 90) + self.fingerHolesAt(h/4+2*t-3*t+w, h-3*t-a/2, h/4, 0) + + def rings(self): + a = self.axle + r = a/2 + t = self.thickness + + self.moveTo(0, a+1.5*t, -90) + for i in range(2): + self.polyline(r-1.5*t, (180, r+3*t), 0, (180, 1.5*t), 0, + (-180, r), r-1.5*t, (180, 1.5*t)) + self.moveTo(a-t, a+12*t, 180) + + + def render(self): + + self.generateWallEdges() + + t = self.thickness + w = self.width + d = self.diameter + a = self.axle + h = self.height + + self.height = h = max(h, a+10*t) + + self.side(move="right") + self.side(move="right") + + self.rectangularTriangle(h/4, d/2, "ffe", num=2, r=3*t, move="right") + + self.roundedPlate(w+h/2+2*t, h, edge="e", r=3*t, + extend_corners=False, + callback=[self.backCB], move="right") + self.rings() diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallslottedholder.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallslottedholder.py index f8db829..61bf43d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallslottedholder.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallslottedholder.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -17,6 +16,7 @@ from boxes import * from boxes.walledges import _WallMountedBox + class FrontEdge(edges.Edge): def __call__(self, length, **kw): @@ -28,12 +28,12 @@ class FrontEdge(edges.Edge): w = (wt-ws)/2 - r1 for i in range(self.number): self.polyline(w, (90, r1), ds-r1-r2, (-90, r2), ws-2*r2, - (-90, r2), ds-r1-r2, (90, r1), w) + (-90, r2), ds-r1-r2, (90, r1), w) class WallSlottedHolder(_WallMountedBox): """Wall tool holder with slots""" - def __init__(self): + def __init__(self) -> None: super().__init__() self.buildArgParser(h=120) @@ -79,18 +79,18 @@ class WallSlottedHolder(_WallMountedBox): return not (i % 3) def braces(self): - return sum((self.brace(i) for i in range(self.number+1))) + return sum(self.brace(i) for i in range(self.number+1)) def backCB(self): n = self.number ws = self.slot_width wt = self.tool_width t = self.thickness - + d = min(2*t, (wt-ws)/4.) self.wallHolesAt(d, 0, self.h, 90) self.wallHolesAt(n*wt-d, 0, self.h, 90) - + for i in range(1, n): if self.brace(i): self.wallHolesAt(i*wt, 0, self.h, 90) @@ -101,11 +101,11 @@ class WallSlottedHolder(_WallMountedBox): wt = self.tool_width t = self.thickness l = self.additional_depth + self.slot_depth - + d = min(2*t, (wt-ws)/4.) self.fingerHolesAt(d, 0, l, 90) self.fingerHolesAt(n*wt-d, 0, l, 90) - + for i in range(1, n): if self.brace(i): self.fingerHolesAt(i*wt, 0, l, 90) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallstairs.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallstairs.py index a64704f..f67518e 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallstairs.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallstairs.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -14,9 +13,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from boxes import * from boxes.walledges import _WallMountedBox + class WallStairs(_WallMountedBox): """Platforms in different heights e.g. for screw drivers""" @@ -24,17 +23,17 @@ class WallStairs(_WallMountedBox): sh gives height of the stairs from front to back. Note that the overall width and height is bigger than the nominal values as walls and the protrusions are not included in the measurements. """ - def __init__(self): + def __init__(self) -> None: super().__init__() self.buildArgParser(sx="250/3", sy="40*3", sh="30:100:180") self.argparser.add_argument( "--braceheight", action="store", type=float, default=30, help="height of the brace at the bottom back (in mm). Zero for none") - + def yWall(self, move=None): t = self.thickness - x, sx, y, sy, sh = self.x, self.sx, self.y, self.sy, self.sh + x, sx, y, sy, sh = self.x, self.sx, self.y, self.sy, self.sh tw, th = sum(sy), max(sh) + t @@ -51,7 +50,7 @@ sh gives height of the stairs from front to back. Note that the overall width an self.step(sh[i-1]-sh[i]) self.edges["f"](sy[0]) self.polyline(0, 90, sh[0], 90) - + self.move(tw, th, move) def yCB(self, width): @@ -60,7 +59,7 @@ sh gives height of the stairs from front to back. Note that the overall width an for dx in self.sx[:-1]: posx += dx + t self.fingerHolesAt(posx, 0, width, 90) - + def render(self): self.generateWallEdges() @@ -78,6 +77,6 @@ sh gives height of the stairs from front to back. Note that the overall width an self.rectangularWall( x, self.braceheight, "eheh", callback=[lambda:self.yCB(self.braceheight)], move="up") - + for i in range(len(sx) + 1): self.yWall(move="right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/walltypetray.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/walltypetray.py index fb24a26..3d84fb9 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/walltypetray.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/walltypetray.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2014 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -18,10 +17,11 @@ from boxes import * from boxes.lids import _TopEdge from boxes.walledges import _WallMountedBox + class WallTypeTray(_WallMountedBox, _TopEdge): """Type tray - allows only continuous walls""" - def __init__(self): + def __init__(self) -> None: super().__init__() self.addSettingsArgs(edges.StackableSettings) self.buildArgParser("sx", "sy", "h", "hi", "outside", "bottom_edge") @@ -31,7 +31,7 @@ class WallTypeTray(_WallMountedBox, _TopEdge): self.argparser.add_argument( "--radius", action="store", type=float, default=0.0, help="radius for strengthening walls with the hooks") - + def xSlots(self): posx = -0.5 * self.thickness @@ -67,7 +67,7 @@ class WallTypeTray(_WallMountedBox, _TopEdge): self.generateWallEdges() b = self.bottom_edge - + if self.outside: self.sx = self.adjustSize(self.sx) self.sy = self.adjustSize(self.sy) @@ -121,7 +121,3 @@ class WallTypeTray(_WallMountedBox, _TopEdge): e = [edges.SlottedEdge(self, self.sy, be, slots=0.5 * hi), "f", "e", "f"] self.rectangularWall(y, hi, e, move="up") - - - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallwrenchholder.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallwrenchholder.py index 93ba6e0..6d22c2d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallwrenchholder.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wallwrenchholder.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2019 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -17,17 +16,18 @@ from boxes import * from boxes.walledges import _WallMountedBox + class SlottedEdge(edges.Edge): def __call__(self, length, **kw): n = self.number t = self.thickness - + self.polyline(t, 45) l = t - + for i in range(n): w = self.min_width * ((n-i)/n) + self.max_width * (i / n) s = self.min_strength * ((n-i)/n) + self.max_strength * (i / n) @@ -46,7 +46,7 @@ class WallWrenchHolder(_WallMountedBox): """Hold a set of wrenches at a wall""" - def __init__(self): + def __init__(self) -> None: super().__init__() # remove cli params you do not need @@ -83,7 +83,7 @@ class WallWrenchHolder(_WallMountedBox): + self.max_width) t = self.thickness x = self.x-2*t - + self.rectangularWall(self.depth, h, ["e", "B", "e", SlottedEdge(self, None)], move="right") diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/waivyknob.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wavyknob.py similarity index 88% rename from extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/waivyknob.py rename to extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wavyknob.py index b644067..e20883d 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/waivyknob.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/wavyknob.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,13 @@ from boxes import * -class WaivyKnob(Boxes): + +class WavyKnob(Boxes): """Round knob serrated outside for better gripping""" - + ui_group = "Part" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) # Add non default cli params if needed (see argparse std lib) @@ -47,13 +47,11 @@ class WaivyKnob(Boxes): def render(self): t = self.thickness angle = self.serrationangle - self.parts.waivyKnob(self.diameter, self.serrations, angle, + self.parts.wavyKnob(self.diameter, self.serrations, angle, callback=lambda:self.dHole(0, 0, d=self.bolthole, rel_w=self.dhole), move="right") - self.parts.waivyKnob(self.diameter, self.serrations, angle, + self.parts.wavyKnob(self.diameter, self.serrations, angle, callback=lambda: self.nutHole(self.hexhead), move="right") - self.parts.waivyKnob(self.diameter, self.serrations, angle) - - + self.parts.wavyKnob(self.diameter, self.serrations, angle) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/winerack.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/winerack.py index 7ac5bb4..de89971 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/winerack.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/generators/winerack.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Copyright (C) 2013-2016 Florian Festi # # This program is free software: you can redistribute it and/or modify @@ -16,12 +15,13 @@ from boxes import * + class WineRack(Boxes): """Honey Comb Style Wine Rack""" - + ui_group = "Shelf" - def __init__(self): + def __init__(self) -> None: Boxes.__init__(self) # Uncomment the settings for the edge types you use @@ -59,7 +59,7 @@ class WineRack(Boxes): self.moveTo((x-dx*2*cx)/2, (y-ty) / 2) wmin = self.walls == "minimal" - + for i in range(cy//2 + cy % 2): if not frontwall and self.walls == "all": self.hexFingerHoles(0, (2*r+2*dy)*i+dy, r, 90) @@ -97,13 +97,13 @@ class WineRack(Boxes): if j < cx -1: self.hexFingerHoles(j*2*dx+dx, (2*r+2*dy)*i, r, 30) - + def render(self): x, y, h, radius = self.x, self.y, self.h, self.radius t = self.thickness r = self.r = 2 * (radius + t) * math.tan(math.pi/6) - + self.dx = dx = r * math.cos(math.pi/6) self.dy = dy = r * math.sin(math.pi/6) self.cx = cx = int((x-2*t) // (2*dx)) @@ -129,5 +129,3 @@ class WineRack(Boxes): tc += 2 * cx - 2 # very top row self.partsMatrix(tc, cx, "up", self.rectangularWall, r-2*self.delta, h, "fefe") - - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/lids.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/lids.py index 0759ebd..6a8b878 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/lids.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/lids.py @@ -12,26 +12,198 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from __future__ import annotations -from boxes import * +import math +from typing import Any, Callable -class _ChestLid(Boxes): +import boxes +from boxes import Boxes, edges - def getR(self, x, angle=0): + +class LidSettings(edges.Settings): + """Settings for the Lid +Values: +* absolute + + * style : "none" : type of lid to create + * handle : "none" : type of handle + +* relative (in multiples of thickness) + + * height : 4.0 : height of the brim (if any) + * play : 0.1 : play when sliding the lid on (if applicable) + * handle_height : 8.0 : height of the handle (if applicable) + """ + absolute_params = { + "style": ("none", "flat", "chest", "overthetop", "ontop"), + "handle": ("none", "long_rounded", "long_trapezoid", "long_doublerounded", "knob"), + } + + relative_params = { + "height": 4.0, + "play": 0.1, + "handle_height": 8.0, + } + + +class Lid: + def __init__(self, boxes, settings: LidSettings) -> None: + self.boxes = boxes + self.settings = settings + + def __getattr__(self, name: str) -> Any: + """Hack for using unaltered code form Boxes class""" + if hasattr(self.settings, name): + return getattr(self.settings, name) + return getattr(self.boxes, name) + + def __call__(self, x: float, y: float, edge=None) -> bool: + t = self.thickness + style = self.settings.style + height = self.height + if style == "flat": + self.rectangularWall(x, y, "eeee", + callback=[self.handleCB(x, y)], + move="up", label="lid bottom") + self.rectangularWall(x, y, "EEEE", + callback=[self.handleCB(x, y)], + move="up", label="lid top") + elif style == "chest": + self.chestSide(x, move="right", label="lid right") + self.chestSide(x, move="up", label="lid left") + self.chestSide(x, move="left only", label="invisible") + self.chestTop(x, y, + callback=[None, self.handleCB(x, 3*t)], + move="up", label="lid top") + elif style in ("overthetop", "ontop"): + x2 = x + y2 = y + b = { + "Š": "š", + "S": "š", + }.get(edge, "e") + if style == "overthetop": + x2 += 2*t + self.play + y2 += 2*t + self.play + self.rectangularWall(x2, y2, "ffff", + callback=[self.handleCB(x2, y2)], + move="up") + self.rectangularWall(x2, self.height, b +"FFF", + ignore_widths=[1, 2, 5, 6], move="up") + self.rectangularWall(x2, self.height, b + "FFF", + ignore_widths=[1, 2, 5, 6], move="up") + self.rectangularWall(y2, self.height, b + "fFf", + ignore_widths=[1, 2, 5, 6], move="up") + self.rectangularWall(y2, self.height, b + "fFf", + ignore_widths=[1, 2, 5, 6], move="up") + if style == "ontop": + self.rectangularWall(y - self.play, height + 2*t, "eeee", + move="up") + self.rectangularWall(y - self.play, height + 2*t, "eeee", + move="up") + else: + return False + + self.handleParts(x, y) + return True + + ###################################################################### + ### Handles + ###################################################################### + + def handleCB(self, x: float, y: float) -> Callable: + t = self.thickness + + def cb() -> None: + if self.handle.startswith("long"): + self.rectangularHole(x/2, y/2, x/2, t) + elif self.handle.startswith("knob"): + h = v = 3 * t # adjust for different styles + self.moveTo((x - t) / 2 + self.burn, (y - t) / 2 + self.burn, 180) + self.ctx.stroke() + with self.saved_context(): + self.set_source_color(boxes.Color.INNER_CUT) + for l in (h, v, h, v): + self.polyline(l, -90, t, -90, l, 90) + self.ctx.stroke() + + return cb + + def longHandle(self, x:float, y: float, style="long_rounded", move=None) -> None: + t = self.settings.thickness + hh = self.handle_height + tw, th = x/2 + 2*t, self.handle_height + 2*t + + if self.move(tw, th, move, True): + return + + self.moveTo(0.5*t) + + poly = [(90, t/2), t/2, 90, t, -90] + + if style == "long_rounded": + r = min(hh/2, x/4) + poly += [t + hh - r, (90, r)] + l = x/2 - 2*r + elif style == "long_trapezoid": + poly += [t, (45, t), (hh - t) * 2**.5, (45, t)] + l = x/2 - 2 * hh + elif style == "long_doublerounded": + poly += [t, 90, 0, (-90, hh /2), 0, (90, hh/2)] + l = x/2 - 2*hh + + poly = [x/2+t] + poly + [l] + list(reversed(poly)) + self.polyline(*poly) + + self.move(tw, th, move) + + def knobHandle(self, x: float, y: float, style, move=None) -> None: + t = self.settings.thickness + hh = self.handle_height + tw, th = 2 * 7 * t + self.spacing, self.handle_height + 2*t + + if self.move(tw, th, move, True): + return + + poly = [(90, t/2), t/2, 90, t/2, -90] + + poly += [hh - 2*t, (90, 3*t)] + + for bottom, top in (([3*t, 90, 2*t + hh/2, -90, t, -90, hh/2 + 2*t, 90, 3*t], [t]), + ([7*t], [0, 90, hh/2, -90, t, -90, hh/2, 90, 0])) : + self.moveTo(0.5*t) + p = bottom + poly + top + list(reversed(poly)) + self.polyline(*p) + self.moveTo(tw/2 + self.spacing) + + self.move(tw, th, move) + + def handleParts(self, x: float, y: float) -> None: + if self.handle.startswith("long"): + self.longHandle(x, y, self.handle, move="up") + elif self.handle.startswith("knob"): + self.knobHandle(x, y, self.handle, move="up") + + ###################################################################### + ### Chest Lid + ###################################################################### + + def getChestR(self, x: float, angle: float = 0) -> float: t = self.thickness d = x - 2*math.sin(math.radians(angle)) * (3*t) r = d / 2.0 / math.cos(math.radians(angle)) return r - def side(self, x, angle=0, move="", label=""): + def chestSide(self, x: float, angle: float = 0, move="", label: str = "") -> None: if "a" not in self.edges: s = edges.FingerJointSettings(self.thickness, True, finger=1.0, space=1.0) s.edgeObjects(self, "aA.") t = self.thickness - r = self.getR(x, angle) + r = self.getChestR(x, angle) if self.move(x+2*t, 0.5*x+3*t, move, True, label=label): return @@ -45,14 +217,13 @@ class _ChestLid(Boxes): self.move(x+2*t, 0.5*x+3*t, move, False, label=label) - def top(self, x, y, angle=0, move=None, label=""): + def chestTop(self, x: float, y: float, angle: float = 0, callback=None, move=None, label: str = "") -> None: if "a" not in self.edges: - s = edges.FingerJointSettings(self.thickness, True, - finger=1.0, space=1.0) + s = edges.FingerJointSettings(self.thickness, True, finger=1.0, space=1.0) s.edgeObjects(self, "aA.") t = self.thickness - l = math.radians(180-2*angle) * self.getR(x, angle) + l = math.radians(180-2*angle) * self.getChestR(x, angle) tw = l + 6*t th = y+2*t @@ -60,44 +231,35 @@ class _ChestLid(Boxes): if self.move(tw, th, move, True, label=label): return + self.cc(callback, 0, self.edges["A"].startwidth()+self.burn) self.edges["A"](3*t) self.edges["X"](l, y+2*t) self.edges["A"](3*t) self.corner(90) + self.cc(callback, 1) self.edge(y+2*t) self.corner(90) + self.cc(callback, 2, self.edges["A"].startwidth()+self.burn) self.edges["A"](3*t) self.edge(l) self.edges["A"](3*t) self.corner(90) + self.cc(callback, 3) self.edge(y+2*t) self.corner(90) self.move(tw, th, move, label=label) - def drawAddOnLid(self, x, y, style): - if style == "flat": - self.rectangularWall(x, y, "eeee", move="right", label="lid bottom") - self.rectangularWall(x, y, "EEEE", move="up", label="lid top") - elif style == "chest": - self.side(x, move="right", label="lid right") - self.side(x, move="up", label="lid left") - self.side(x, move="left only", label="invisible") - self.top(x, y, move="up", label="lid top") - else: - return False - return True class _TopEdge(Boxes): - def addTopEdgeSettings(self, fingerjoint={}, stackable={}, hinge={}, - cabinethinge={}, lid={}, click={}, + cabinethinge={}, slideonlid={}, click={}, roundedtriangle={}, mounting={}, handle={}): self.addSettingsArgs(edges.FingerJointSettings, **fingerjoint) self.addSettingsArgs(edges.StackableSettings, **stackable) self.addSettingsArgs(edges.HingeSettings, **hinge) self.addSettingsArgs(edges.CabinetHingeSettings, **cabinethinge) - self.addSettingsArgs(edges.LidSettings, **lid) + self.addSettingsArgs(edges.SlideOnLidSettings, **slideonlid) self.addSettingsArgs(edges.ClickSettings, **click) self.addSettingsArgs(edges.RoundedTriangleEdgeSettings, **roundedtriangle) self.addSettingsArgs(edges.MountingSettings, **mounting) @@ -111,17 +273,17 @@ class _TopEdge(Boxes): if tl.char == "i": tb = tf = "e" - tr = "j" + tl = "j" elif tl.char == "k": - tb = tf = "e" + tl = tr = "e" elif tl.char == "L": tl = "M" - tb = "e" + tf = "e" tr = "N" elif tl.char == "v": - tb = tr = tf = "e" + tl = tr = tf = "e" elif tl.char == "t": - tl = tr = "e" + tf = tb = "e" elif tl.char == "G": tl = tb = tr = tf = "e" if self.edges["G"].settings.side == edges.MountingSettings.PARAM_LEFT: @@ -146,7 +308,7 @@ class _TopEdge(Boxes): tb = tf = "Y" return [tl, tb, tr, tf] - def drawLid(self, x, y, top_edge, bedBolts=[None, None]): + def drawLid(self, x: float, y: float, top_edge, bedBolts=[None, None]) -> bool: d2, d3 = bedBolts if top_edge == "c": self.rectangularWall(x, y, "CCCC", bedBolts=[d2, d3, d2, d3], move="up", label="top") @@ -155,28 +317,21 @@ class _TopEdge(Boxes): elif top_edge in "FhŠY": self.rectangularWall(x, y, "ffff", move="up", label="top") elif top_edge == "L": - self.rectangularWall(x, y, "nlmE", move="up", label="lid top") + self.rectangularWall(x, y, "Enlm", move="up", label="lid top") elif top_edge == "i": - self.rectangularWall(x, y, "IEJe", move="up", label="lid top") + self.rectangularWall(x, y, "EJeI", move="up", label="lid top") elif top_edge == "k": outset = self.edges["k"].settings.outset self.edges["k"].settings.setValues(self.thickness, outset=True) lx = x/2.0-0.1*self.thickness self.edges['k'].settings.setValues(self.thickness, grip_length=5) self.rectangularWall(lx, y, "IeJe", move="right", label="lid top left") - self.rectangularWall(lx, y, "IeJe", move="up", label="lid top right") + self.rectangularWall(lx, y, "IeJe", move="mirror up", label="lid top right") self.rectangularWall(lx, y, "IeJe", move="left only", label="invisible") self.edges["k"].settings.setValues(self.thickness, outset=outset) - elif top_edge == "S": - self.rectangularWall(x, y, "ffff", move="up", label="lid top") - self.rectangularWall(x, 0, "sFeF", move="up", label="lid top left") - self.rectangularWall(x, 0, "sFeF", move="up", label="lid top right") - self.rectangularWall(y, 0, "sfef", move="up", label="lid top front") - self.rectangularWall(y, 0, "sfef", move="up", label="lid top back") elif top_edge == "v": self.rectangularWall(x, y, "VEEE", move="up", label="lid top") self.edges["v"].parts(move="up") else: return False return True - diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/parts.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/parts.py index 9487bb3..fa3a035 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/parts.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/parts.py @@ -1,42 +1,49 @@ +from __future__ import annotations + from math import * +from typing import Any, Callable + from boxes import vectors -def arcOnCircle(spanning_angle, outgoing_angle, r=1.0): + +def arcOnCircle(spanning_angle: float, outgoing_angle: float, r: float = 1.0) -> tuple[float, float]: angle = spanning_angle + 2 * outgoing_angle radius = r * sin(radians(0.5 * spanning_angle)) / sin(radians(180 - outgoing_angle - 0.5 * spanning_angle)) return angle, abs(radius) class Parts: - def __init__(self, boxes): + def __init__(self, boxes) -> None: self.boxes = boxes """ - def roundKnob(self, diameter, n=20, callback=None, move=""): + def roundKnob(self, diameter: float, n: int = 20, callback: Callable | None = None, move: str = ""): size = diameter+diameter/n if self.move(size, size, move, before=True): return self.moveTo(size/2, size/2) self.cc(callback, None, 0, 0) - + self.move(size, size, move) """ - def __getattr__(self, name): + def __getattr__(self, name: str) -> Any: return getattr(self.boxes, name) - def disc(self, diameter, hole=0, callback=None, move="", label=""): + def disc(self, diameter: float, hole: float = 0, dwidth: float = 1.0, callback: Callable | None = None, move: str = "", label: str = "") -> None: """Simple disc :param diameter: diameter of the disc :param hole: (Default value = 0) :param callback: (Default value = None) called in the center - :param move: (Defaultvalue = None) + :param dwidth: (Default value = 1) flatten on right side to given ratio + :param move: (Default value = "") + :param label: (Default value = "") """ size = diameter r = diameter / 2.0 - if self.move(size, size, move, before=True, label=label): + if self.move(size*dwidth, size, move, before=True, label=label): return self.moveTo(size / 2, size / 2) @@ -45,19 +52,28 @@ class Parts: self.hole(0, 0, hole / 2) self.cc(callback, None, 0, 0) - self.moveTo(r + self.burn, 0, 90) - self.corner(360, r, tabs=6) - self.move(size, size, move, label=label) + if dwidth == 1.0: + self.moveTo(r + self.burn, 0, 90) + self.corner(360, r, tabs=6) + else: + w = (2.0 * dwidth - 1) * r + a = degrees(acos(w / r)) + self.moveTo(0, 0, -a) + self.moveTo(r, 0, -90) + self.corner(-360+2*a, r) + self.corner(-a) + self.edge(2*r*sin(radians(a))) + self.move(size*dwidth, size, move, label=label) - def waivyKnob(self, diameter, n=20, angle=45, hole=0, callback=None, move=""): - """Disc with a waivy edge to be easier to be gripped + def wavyKnob(self, diameter: float, n: int = 20, angle: float = 45, hole: float = 0, callback: Callable | None = None, move: str = "") -> None: + """Disc with a wavy edge to be easier to be gripped :param diameter: diameter of the knob :param n: (Default value = 20) number of waves :param angle: (Default value = 45) maximum angle of the wave :param hole: (Default value = 0) :param callback: (Default value = None) called in the center - :param move: (Defaultvalue = None) + :param move: (Default value = "") """ if n < 2: @@ -84,17 +100,17 @@ class Parts: self.move(size, size, move) - def concaveKnob(self, diameter, n=3, rounded=0.2, angle=70, hole=0, - callback=None, move=""): + def concaveKnob(self, diameter: float, n: int = 3, rounded: float = 0.2, angle: float = 70, hole: float = 0, + callback: Callable | None = None, move: str = "") -> None: """Knob with dents to be easier to be gripped :param diameter: diameter of the knob :param n: (Default value = 3) number of dents - :param rounded: (Default value = 0.2) proportion of circumferen remaining + :param rounded: (Default value = 0.2) proportion of circumference remaining :param angle: (Default value = 70) angle the dents meet the circumference :param hole: (Default value = 0) :param callback: (Default value = None) called in the center - :param move: (Defaultvalue = None) + :param move: (Default value = "") """ size = diameter @@ -119,35 +135,35 @@ class Parts: for i in range(n): self.boxes.corner(a, r) self.corner(angle) - self.corner(360. / n * rounded, diameter / 2, tabs= - (i % max(1, (n+1) // 6) == 0)) + self.corner(360. / n * rounded, diameter / 2, tabs=(i % max(1, (n+1) // 6) == 0)) self.corner(angle) self.move(size, size, move) - def ringSegment(self, r_outside, r_inside, angle, n=1, move=None): + def ringSegment(self, r_outside: float, r_inside: float, angle: float, n: int = 1, move: str = "") -> None: """Ring Segment :param r_outside: outer radius :param r_inside: inner radius - :param angle: anlge the segment is spanning + :param angle: angle the segment is spanning :param n: (Default value = 1) number of segments - :param move: (Defaultvalue = None) + :param move: (Default value = "") """ - space = 360 * r_inside / self.spacing - n = min(n, 360 / (angle+space)) + space = 360 * self.spacing / r_inside / 2 / pi + nc = int(min(n, 360 / (angle+space))) - # XXX be smarter about space - if self.move(r_outside, r_outside, move, True): - return - - self.moveTo(r_outside) - for i in range(n): - self.polyline( - 0, (angle, r_outside), 0, 90, (r_outside-r_inside, 2), - 90, (angle, r_inside), 0, 90, (r_outside-r_inside, 2), - 90) - x, y = vectors.circlepoint(r_outside, math.radians(angle+space)) - self.moveTo(y, r_outside-x, angle+space) - self.move(r_outside, r_outside) - return n + while n > 0: + if self.move(2*r_outside, 2*r_outside, move, True): + return + self.moveTo(0, r_outside, -90) + for i in range(nc): + self.polyline( + 0, (angle, r_outside), 0, 90, (r_outside-r_inside, 2), + 90, 0, (-angle, r_inside), 0, 90, (r_outside-r_inside, 2), + 90) + x, y = vectors.circlepoint(r_outside, radians(angle+space)) + self.moveTo(y, r_outside-x, angle+space) + n -=1 + if n == 0: + break + self.move(2*r_outside, 2*r_outside, move) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/pulley.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/pulley.py index 27c5c7b..c13f7d9 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/pulley.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/pulley.py @@ -3,15 +3,16 @@ // by droftarts January 2012 // Based on pulleys by: -// http://www.thingiverse.com/thing:11256 by me! +// https://www.thingiverse.com/thing:11256 by me! // https://github.com/prusajr/PrusaMendel by Josef Prusa -// http://www.thingiverse.com/thing:3104 by GilesBathgate -// http://www.thingiverse.com/thing:2079 by nophead +// https://www.thingiverse.com/thing:3104 by GilesBathgate +// https://www.thingiverse.com/thing:2079 by nophead // dxf tooth data from http://oem.cadregister.com/asp/PPOW_Entry.asp?company=915217&elementID=07807803/METRIC/URETH/WV0025/F -// pulley diameter checked and modelled from data at http://www.sdp-si.com/D265/HTML/D265T016.html +// pulley diameter checked and modelled from data at https://www.sdp-si.com/D265/HTML/D265T016.html """ from math import * + from boxes.vectors import * @@ -78,25 +79,13 @@ class Pulley: "GT2_5mm" : [[-1.975908,-0.75],[-1.975908,0],[-1.797959,0.03212],[-1.646634,0.121224],[-1.534534,0.256431],[-1.474258,0.426861],[-1.446911,0.570808],[-1.411774,0.712722],[-1.368964,0.852287],[-1.318597,0.989189],[-1.260788,1.123115],[-1.195654,1.25375],[-1.12331,1.380781],[-1.043869,1.503892],[-0.935264,1.612278],[-0.817959,1.706414],[-0.693181,1.786237],[-0.562151,1.851687],[-0.426095,1.9027],[-0.286235,1.939214],[-0.143795,1.961168],[0,1.9685],[0.143796,1.961168],[0.286235,1.939214],[0.426095,1.9027],[0.562151,1.851687],[0.693181,1.786237],[0.817959,1.706414],[0.935263,1.612278],[1.043869,1.503892],[1.123207,1.380781],[1.195509,1.25375],[1.26065,1.123115],[1.318507,0.989189],[1.368956,0.852287],[1.411872,0.712722],[1.447132,0.570808],[1.474611,0.426861],[1.534583,0.256431],[1.646678,0.121223],[1.798064,0.03212],[1.975908,0],[1.975908,-0.75]], } - def __init__(self, boxes): + def __init__(self, boxes) -> None: self.boxes = boxes @classmethod def getProfiles(cls): return list(sorted(cls.teeth.keys())) - def drawPoints(self, lines, kerfdir=1): - if kerfdir != 0: - lines = kerf(lines, self.boxes.burn * kerfdir) - self.boxes.ctx.save() - self.boxes.ctx.move_to(*lines[0]) - - for x, y in lines[1:]: - self.boxes.ctx.line_to(x, y) - - self.boxes.ctx.line_to(*lines[0]) - self.boxes.ctx.restore() - def diameter(self, teeth, profile): if self.spacing[profile][0]: return tooth_spaceing_curvefit(teeth, *self.spacing[profile][1:]) @@ -146,7 +135,7 @@ class Pulley: m = [[tooth_width_scale, 0, 0], [0, tooth_depth_scale, -tooth_distance_from_centre]] m = mmul(m, rotm(i * 2 * pi / teeth)) - points.extend((vtransl(pt, m) for pt in self.teeth[profile][1:-1])) + points.extend(vtransl(pt, m) for pt in self.teeth[profile][1:-1]) - self.drawPoints(points, kerfdir=-1 if insideout else 1) + self.boxes.drawPoints(points, kerfdir=-1 if insideout else 1) self.boxes.move(total_width, total_width, move) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/qrcode_factory.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/qrcode_factory.py new file mode 100644 index 0000000..8760f42 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/qrcode_factory.py @@ -0,0 +1,63 @@ +from decimal import Decimal + +import qrcode.image.base +import qrcode.image.svg + + +class BoxesQrCodeFactory(qrcode.image.base.BaseImage): + """ + SVG image builder + Creates a QR-code image as a SVG document fragment. + """ + _SVG_namespace = "http://www.w3.org/2000/svg" + kind = "SVG" + allowed_kinds = ("SVG",) + + def __init__(self, *args, ctx=None, x=0, y=0, **kwargs): + super().__init__(*args, **kwargs) + self.ctx = ctx + self.x, self.y = x, y + # Save the unit size, for example the default box_size of 10 is '1mm'. + self.unit_size = self.units(self.box_size) + + def drawrect(self, row, col): + self.ctx.rectangle(*self._rect(row, col)) + self._img.append(self._rect(row, col)) + + def units(self, pixels, text=True): + """ + A box_size of 10 (default) equals 1mm. + """ + units = Decimal(pixels) / 10 + if not text: + return units + return '%smm' % units + + def save(self, stream, kind=None): + self.check_kind(kind=kind) + self._write(stream) + + def to_string(self): + return f"".join(self._img) + + def new_image(self, **kwargs): + self._img = [] + return self._img + + def _rect(self, row, col): + size = self.box_size / 10 + x = self.x + (row + self.border) * size + y = self.y + (col + self.border) * size + return x, y, size, size + + def _write(self, stream): + stream.write("".join(self._img)) + +if __name__=="__main__": + import qrcode + import qrcode.image + q = qrcode.QRCode(image_factory=BoxesQrCodeFactory, box_size=10) + q.add_data('hello') + ctx = "a context" + img = q.make_image(ctx="a context") + print(img.to_string()) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/robot.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/robot.py index 9392ff5..fcaa4b0 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/robot.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/robot.py @@ -1,5 +1,3 @@ -import boxes - __all__ = [ "RobotArg", "RobotArmMM", @@ -11,7 +9,7 @@ __all__ = [ class RobotArg: - def __init__(self, includenone=False): + def __init__(self, includenone=False) -> None: self.robotarms = [ (name, globals()[name].__doc__[23:]) for name in __all__ if name.startswith("RobotArm")] @@ -29,12 +27,12 @@ class RobotArg: ("""""" % (name, ' selected="selected"' if name == default else "", name, descr) for name, descr in self.robotarms)) - return """\n""" % (name, options) + return f"""\n""" class _RobotArm: - def __init__(self, boxes, servo, servo2=None): + def __init__(self, boxes, servo, servo2=None) -> None: self.boxes = boxes self.servo = servo self.servo2 = servo2 or servo diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/servos.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/servos.py index 41e69b7..08d1c77 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/servos.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/servos.py @@ -1,16 +1,18 @@ -import boxes.vectors +from __future__ import annotations + import math -class EyeEdge(boxes.edges.FingerHoleEdge): +import boxes.vectors + +class EyeEdge(boxes.edges.FingerHoleEdge): char = "m" - def __init__(self, boxes, servo, fingerHoles=None, driven=False, - outset=False, **kw): + def __init__(self, boxes, servo, fingerHoles=None, driven: bool = False, outset: bool = False, **kw) -> None: self.servo = servo self.outset = outset self.driven = driven - super(EyeEdge, self).__init__(boxes, fingerHoles, **kw) + super().__init__(boxes, fingerHoles, **kw) def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw): t = self.fingerHoles.settings.thickness @@ -18,56 +20,54 @@ class EyeEdge(boxes.edges.FingerHoleEdge): pos_axle = self.servo.hinge_depth() self.ctx.save() - self.hole(length/2.0, -pos_axle, - self.servo.axle/2.0 if self.driven else - self.servo.servo_axle/2.0) + self.hole(length / 2.0, + -pos_axle, + self.servo.axle / 2.0 if self.driven else self.servo.servo_axle / 2.0) if self.outset: - self.fingerHoles(t, self.thickness / 2, length-2*t, 0) + self.fingerHoles(t, self.thickness / 2, length - 2 * t, 0) else: self.fingerHoles(0, self.thickness / 2, length, 0) self.ctx.restore() r = self.servo.servo_axle * 2 - a, l = boxes.vectors.tangent(length/2, pos_axle, r) + a, l = boxes.vectors.tangent(length / 2, pos_axle, r) angle = math.degrees(a) - self.polyline(0, -angle, l, (2*angle, r), l, -angle, 0) + self.polyline(0, -angle, l, (2 * angle, r), l, -angle, 0) - def startwidth(self): + def startwidth(self) -> float: return self.fingerHoles.settings.thickness - def margin(self): + def margin(self) -> float: return self.servo.hinge_depth() + self.fingerHoles.settings.thickness + self.servo.servo_axle * 2 -def buildEdges(boxes, servo, chars="mMnN"): + +def buildEdges(boxes, servo, chars: str = "mMnN"): result = {} for n, char in enumerate(chars): - e = EyeEdge(boxes, servo, outset=(n<2), driven=(n % 2)) + e = EyeEdge(boxes, servo, outset=(n < 2), driven=((n % 2) == 1)) e.char = char result[char] = e return result -class ServoArg: - def __init__(self, includenone=False): +class ServoArg: + def __init__(self, includeNone: bool = False) -> None: self.servos = ["Servo9g"] - if includenone: + if includeNone: self.servos[0:0] = ["none"] - def __call__(self, arg): + def __call__(self, arg) -> str: return str(arg) - def choices(self): + def choices(self) -> list[str]: return [name for name in self.servos] - def html(self, name, default, translate): - options = "\n".join( - ("""""" % - (name, ' selected="selected"' if name == default else "", - name) for name in self.servos)) - return """\n""" % (name, options) + def html(self, name: str, default: str, translate) -> str: + options = "\n".join("""""".format(name, ' selected="selected"' if name == default else "", name) for name in self.servos) + return f"""\n""" class Servo: - def __init__(self, boxes, axle=3): + def __init__(self, boxes, axle: float = 3) -> None: self.boxes = boxes self.axle = axle self._edges = buildEdges(boxes, self) @@ -75,46 +75,47 @@ class Servo: def edges(self, edges): return [self._edges.get(e, e) for e in edges] -class Servo9g(Servo): - height = 22.5 - length = 28.0 # one tab in the wall - width = 12.0 - axle_pos = 6.0 - servo_axle = 4.6 # 6.9 for servo arm - - def top(self, x=0.0, y=0.0, angle=90.0): +class Servo9g(Servo): + height: float = 22.5 + length: float = 28.0 # one tab in the wall + width: float = 12.0 + axle_pos: float = 6.0 + servo_axle: float = 4.6 # 6.9 for servo arm + + def top(self, x: float = 0.0, y: float = 0.0, angle: float = 90.0) -> None: self.boxes.moveTo(x, y, angle) self.boxes.hole(6, 0, 6) self.boxes.hole(12, 0, 3) - def bottom(self, x=0.0, y=0.0, angle=90.0): + def bottom(self, x: float = 0.0, y: float = 0.0, angle: float = 90.0) -> None: self.boxes.moveTo(x, y, angle) - self.boxes.hole(6, 0, self.axle/2.0) + self.boxes.hole(6, 0, self.axle / 2.0) - def front(self, x=0.0, y=0.0, angle=90.0): + def front(self, x: float = 0.0, y: float = 0.0, angle: float = 90.0) -> None: self.boxes.moveTo(x, y, angle) self.boxes.rectangularHole(5.4, 0, 2.4, 12) self.boxes.rectangularHole(17, 0, 4, 16) - def hinge_width(self): + def hinge_width(self) -> float: return self.height + self.boxes.thickness + 4.5 - def hinge_depth(self): - return self.height # XXX + def hinge_depth(self) -> float: + return self.height # XXX + class Servo9gt(Servo9g): height = 35 - def top(self, x=0.0, y=0.0, angle=90.0): + def top(self, x: float = 0.0, y: float = 0.0, angle: float = 90.0) -> None: self.boxes.moveTo(x, y, angle) self.boxes.hole(6, 0, 6) self.boxes.hole(12, 0, 5) - def bottom(self, x=0.0, y=0.0, angle=90.0): + def bottom(self, x: float = 0.0, y: float = 0.0, angle: float = 90.0) -> None: self.boxes.moveTo(x, y, angle) self.boxes.hole(6, 0, self.axle) - def front(self, x=0.0, y=0.0, angle=90.0): + def front(self, x: float = 0.0, y: float = 0.0, angle: float = 90.0) -> None: self.boxes.moveTo(x, y, angle) self.boxes.rectangularHole(5.4, 0, 2.4, 12) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/svgutil.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/svgutil.py deleted file mode 100755 index 8ed733c..0000000 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/svgutil.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (C) 2016 Florian Festi -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import re, datetime - -from xml.etree import cElementTree as ElementTree -ElementTree.register_namespace("","http://www.w3.org/2000/svg") -ElementTree.register_namespace("xlink", "http://www.w3.org/1999/xlink") - -unit2mm = {"mm" : 1.0, - "cm" : 10.0, - "in" : 25.4, - "px" : 90.0/25.4, - "pt" : 90.0/25.4/1.25, - "pc" : 90.0/25.4/15, -} - -def getSizeInMM(tree): - root = tree.getroot() - m = re.match(r"(-?\d+\.?\d*)(\D+)", root.get("height")) - height, units = m.groups() - height = float(height) * unit2mm.get(units, 1.0) - - m = re.match(r"(-?\d+\.?\d*)(\D+)", root.get("width")) - width, units = m.groups() - width = float(width) * unit2mm.get(units, 1.0) - - return width, height - -def getViewBox(tree): - root = tree.getroot() - m = re.match(r"\s*(-?\d+\.?\d*)\s+" - "(-?\d+\.?\d*)\s+" - "(-?\d+\.?\d*)\s+" - "(-?\d+\.?\d)\s*", root.get("viewBox")) - - return [float(m) for m in m.groups()] - -def ticksPerMM(tree): - width, height = getSizeInMM(tree) - x1, y1, x2, y2 = getViewBox(tree) - - return x2/width, y2/height - -def svgMerge(box, inkscape, output): - - src_tree = ElementTree.parse(box) - dest_tree = ElementTree.parse(inkscape) - dest_root = dest_tree.getroot() - - src_width, src_height = getSizeInMM(src_tree) - dest_width, dest_height = getSizeInMM(dest_tree) - - src_scale_x, src_scale_y = ticksPerMM(src_tree) - dest_scale_x, dest_scale_y = ticksPerMM(dest_tree) - - scale_x = dest_scale_x / src_scale_x - scale_y = dest_scale_y / src_scale_y - - src_view = getViewBox(src_tree) - - off_x = src_view[0] * -scale_x - off_y = (src_view[1]+src_view[3]) * -scale_y + dest_height * scale_y - - for el in src_tree.getroot(): - import sys - dest_root.append(el) - if el.tag.endswith("g"): - el.set("transform", "matrix(%f,0,0,%f, %f, %f)" % ( - scale_x, scale_y, off_x, off_y)) - - # write the xml file - ElementTree.ElementTree(dest_root).write(output, encoding='utf-8', xml_declaration=True) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/vectors.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/vectors.py index 6381d42..7fe19e6 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/vectors.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/vectors.py @@ -16,7 +16,7 @@ import math def normalize(v): - "set lenght of vector to one" + """set length of vector to one""" l = (v[0] ** 2 + v[1] ** 2) ** 0.5 if l == 0.0: return (0.0, 0.0) @@ -35,35 +35,34 @@ def vclip(v, length): def vdiff(p1, p2): - "vector from point1 to point2" + """vector from point1 to point2""" return (p2[0] - p1[0], p2[1] - p1[1]) def vadd(v1, v2): - "Sum of two vectors" + """Sum of two vectors""" return (v1[0] + v2[0], v1[1] + v2[1]) def vorthogonal(v): - "orthogonal vector" - "Orthogonal vector" + """Orthogonal vector""" return (-v[1], v[0]) def vscalmul(v, a): - "scale vector by a" + """scale vector by a""" return (a * v[0], a * v[1]) def dotproduct(v1, v2): - "Dot product" + """Dot product""" return v1[0] * v2[0] + v1[1] * v2[1] def circlepoint(r, a): return (r * math.cos(a), r * math.sin(a)) def tangent(x, y, r): - "angle and length of a tangent to a circle at x,y with raduis r" + """angle and length of a tangent to a circle at x,y with radius r""" l1 = vlength((x, y)) a1 = math.atan2(y, x) a2 = math.asin(r / l1) @@ -72,7 +71,7 @@ def tangent(x, y, r): return (a1+a2, l2) def rotm(angle): - "Rotation matrix" + """Rotation matrix""" return [[math.cos(angle), -math.sin(angle), 0], [math.sin(angle), math.cos(angle), 0]] diff --git a/extensions/fablabchemnitz/boxes.py/boxes/boxes/walledges.py b/extensions/fablabchemnitz/boxes.py/boxes/boxes/walledges.py index 879cba0..4fb5c71 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/boxes/walledges.py +++ b/extensions/fablabchemnitz/boxes.py/boxes/boxes/walledges.py @@ -1,11 +1,17 @@ -from .edges import Settings, BaseEdge -from boxes import Boxes, edges +from __future__ import annotations + import math +from typing import Any + +from boxes import Boxes, edges + +from .edges import BaseEdge, Settings + class _WallMountedBox(Boxes): ui_group = "WallMounted" - def __init__(self): + def __init__(self) -> None: super().__init__() self.addWallSettingsArgs() @@ -17,7 +23,7 @@ class _WallMountedBox(Boxes): self.addSettingsArgs(FrenchCleatSettings) self.argparser.add_argument( "--walltype", action="store", type=str, default="plain", - choices=["plain", "plain reenforced", "slatwall", "dinrail", + choices=["plain", "plain reinforced", "slatwall", "dinrail", "french cleat"], help="Type of wall system to attach to") @@ -41,7 +47,7 @@ class _WallMountedBox(Boxes): s.edgeObjects(self) self.wallHolesAt = self.edges["|"] - if self.walltype.endswith("reenforced"): + if self.walltype.endswith("reinforced"): self.edges["c"] = self.edges["d"] self.edges["C"] = self.edges["D"] @@ -52,7 +58,7 @@ class _WallMountedBox(Boxes): class WallEdge(BaseEdge): _reversed = False - + def lengths(self, length): return [length] @@ -84,7 +90,7 @@ class WallJoinedEdge(WallEdge): self.edges["f"](length) self.step(t) - def startwidth(self): + def startwidth(self) -> float: return self.settings.thickness class WallBackEdge(WallEdge): @@ -98,7 +104,7 @@ class WallBackEdge(WallEdge): self.edges["F"](length) self.step(-t) - def margin(self): + def margin(self) -> float: return self.settings.thickness class WallHoles(WallEdge): @@ -110,7 +116,7 @@ class WallHoles(WallEdge): def _joint(self, length): self.fingerHolesAt(0, 0, length, 0) self.moveTo(length, 0) - + def __call__(self, x, y, length, angle, **kw): """ Draw holes for a matching WallJoinedEdge @@ -137,7 +143,7 @@ class WallHoleEdge(WallHoles): """Edge with holes for a parallel finger joint""" description = "Edge (parallel slot wall Holes)" - def __init__(self, boxes, wallHoles, **kw): + def __init__(self, boxes, wallHoles, **kw) -> None: super().__init__(boxes, wallHoles.settings, **kw) self.wallHoles = wallHoles @@ -149,15 +155,14 @@ class WallHoleEdge(WallHoles): px, dist, length, angle) self.edge(length, tabs=2) - def startwidth(self): + def startwidth(self) -> float: """ """ return self.wallHoles.settings.edge_width + self.settings.thickness - def margin(self): + def margin(self) -> float: return 0.0 - -class WallSettings(Settings): +class WallSettings(Settings): """Settings for plain WallEdges Values: @@ -167,7 +172,7 @@ Values: """ - absolute_params = { + absolute_params: dict[str, Any] = { } relative_params = { @@ -180,7 +185,7 @@ Values: bc = self.base_class bn = bc.__name__ wallholes = type(bn+"Hole", (WallHoles, bc), {})(boxes, self) - + edges = [bc(boxes, self), type(bn+"Reversed", (bc,), {'_reversed' : True})(boxes, self), type(bn+"Joined", (WallJoinedEdge, bc), {})(boxes, self), @@ -256,11 +261,10 @@ class SlatWallEdge(WallEdge): poly = reversed(poly) self.polyline(*poly) - def margin(self): + def margin(self) -> float: return self.settings.hook_depth + self.settings.hook_distance class SlatWallSettings(WallSettings): - """Settings for SlatWallEdges Values: @@ -332,11 +336,10 @@ class DinRailEdge(WallEdge): poly = reversed(poly) self.polyline(*poly) - def margin(self): + def margin(self) -> float: return self.settings.depth class DinRailSettings(WallSettings): - """Settings for DinRailEdges Values: @@ -410,11 +413,10 @@ class FrenchCleatEdge(WallEdge): poly = reversed(poly) self.polyline(*poly) - def margin(self): + def margin(self) -> float: return self.settings.depth class FrenchCleatSettings(WallSettings): - """Settings for FrenchCleatEdges Values: diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/boxes.1 b/extensions/fablabchemnitz/boxes.py/boxes/documentation/boxes.1 new file mode 100644 index 0000000..fa88c02 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/boxes.1 @@ -0,0 +1,63 @@ +'\" t +.\" Title: Boxes +.\" Author: Georges Khaznadar +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 10/26/2019 +.\" Manual: boxes User Manual +.\" Source: boxes +.\" Language: English +.\" +.TH "BOXES" "1" "10/26/2019" "boxes" "boxes User Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +boxes \- program design boxes +.SH "DESCRIPTION" +.PP +\fBboxes\fR +is a program that generates SVG images that can be viewed directly in a web browser but also postscript and \- with pstoedit as external helper \- other vector formats including dxf, plt (aka hpgl) and gcode\&. +.PP +Of course the library and the generators allow selecting the "thickness" of the material used and automatically adjusts lengths and width of joining fingers and other elements\&. +.PP +The "burn" parameter compensates for the material removed by the laser\&. This allows fine tuning the gaps between joins up to the point where plywood can be press fitted even without any glue\&. +.PP +inger Joints are the work horse of the library\&. They allow 90\(de edges and T connections\&. Their size is scaled up with the material "thickness" to maintain the same appearance\&. The library also allows putting holes and slots for screws (bed bolts) into finger joints, although this is currently not supported for the included generators\&. +.PP +Dovetail joints can be used to join pieces in the same plane\&. +.PP +Flex cuts allows bending and stretching the material in one direction\&. This is used for rounded edges and living hinges\&. +.SH "AUTHOR" +.PP +\fBGeorges Khaznadar\fR <\&georgesk@debian\&.org\&> +.RS 4 +Wrote this manpage for the Debian system\&. +.RE +.SH "COPYRIGHT" +.br +Copyright \(co 2019 Georges Khaznadar +.br +.PP +This manual page was written for the Debian system (and may be used by others)\&. +.PP +Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 2 or (at your option) any later version published by the Free Software Foundation\&. +.PP +On Debian systems, the complete text of the GNU General Public License can be found in +/usr/share/common\-licenses/GPL\&. +.sp diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/boxes.xml b/extensions/fablabchemnitz/boxes.py/boxes/documentation/boxes.xml new file mode 100644 index 0000000..10253f5 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/boxes.xml @@ -0,0 +1,135 @@ + +.
will be generated. You may view the +manual page with: nroff -man .
| less'. A typical entry +in a Makefile or Makefile.am is: + +DB2MAN = /usr/share/sgml/docbook/stylesheet/xsl/docbook-xsl/manpages/docbook.xsl +XP = xsltproc -''-nonet -''-param man.charmap.use.subset "0" + +manpage.1: manpage.xml + $(XP) $(DB2MAN) $< + +The xsltproc binary is found in the xsltproc package. The XSL files are in +docbook-xsl. A description of the parameters you can use can be found in the +docbook-xsl-doc-* packages. Please remember that if you create the nroff +version in one of the debian/rules file targets (such as build), you will need +to include xsltproc and docbook-xsl in your Build-Depends control field. +Alternatively use the xmlto command/package. That will also automatically +pull in xsltproc and docbook-xsl. + +Notes for using docbook2x: docbook2x-man does not automatically create the +AUTHOR(S) and COPYRIGHT sections. In this case, please add them manually as + ... . + +To disable the automatic creation of the AUTHOR(S) and COPYRIGHT sections +read /usr/share/doc/docbook-xsl/doc/manpages/authors.html. This file can be +found in the docbook-xsl-doc-html package. + +Validation can be done using: `xmllint -''-noout -''-valid manpage.xml` + +General documentation about man-pages and man-page-formatting: +man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ + +--> + + + + + + + + + + + + + +]> + + + + &dhtitle; + &dhpackage; + + + &dhfirstname; + &dhsurname; + Wrote this manpage for the Debian system. +
+ &dhemail; +
+
+
+ + 2019 + &dhusername; + + + This manual page was written for the Debian system + (and may be used by others). + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU General Public License, + Version 2 or (at your option) any later version published by + the Free Software Foundation. + On Debian systems, the complete text of the GNU General Public + License can be found in + /usr/share/common-licenses/GPL. + +
+ + &dhucpackage; + &dhsection; + + + &dhpackage; + program design boxes + + + DESCRIPTION + + &dhpackage; is a program that generates + SVG images that can be viewed directly in a web browser but also + postscript and - with pstoedit as external helper - + other vector formats including dxf, plt (aka hpgl) and gcode. + + + Of course the library and the generators allow selecting the + "thickness" of the material used and automatically adjusts lengths and + width of joining fingers and other elements. + + + The "burn" parameter compensates for the material removed by the + laser. This allows fine tuning the gaps between joins up to the point + where plywood can be press fitted even without any glue. + + + inger Joints are the work horse of the library. They allow 90° + edges and T connections. Their size is scaled up with the material + "thickness" to maintain the same appearance. The library also allows + putting holes and slots for screws (bed bolts) into finger joints, + although this is currently not supported for the included generators. + + + Dovetail joints can be used to join pieces in the same plane. + + + Flex cuts allows bending and stretching the material in one + direction. This is used for rounded edges and living hinges. + + +
diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/boxesserver.1 b/extensions/fablabchemnitz/boxes.py/boxes/documentation/boxesserver.1 new file mode 100644 index 0000000..a55ea92 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/boxesserver.1 @@ -0,0 +1,64 @@ +'\" t +.\" Title: BoxesServer +.\" Author: Georges Khaznadar +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 10/31/2019 +.\" Manual: boxesserver User Manual +.\" Source: boxesserver +.\" Language: English +.\" +.TH "BOXESSERVER" "1" "10/31/2019" "boxesserver" "boxesserver User Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +boxesserver \- a web server to make boxes\&. +.SH "SYNOPSIS" +.HP \w'\fBboxesserver\fR\ 'u +\fBboxesserver\fR [\fB\fIport_number\fR\fR] +.SH "DESCRIPTION" +.PP +\fBboxesserver\fR +is a web server which allows one to compute sketches to cut materials with a laser and make various types of boxes\&. +.SH "OPTIONS" +.PP +\fB\fIport_number\fR\fR +.RS 4 +If this optional parameter is set, the web service will be done using this port number\&. By default, the port number is 8000\&. +.sp +Please notice that port numbers lesser than 1024 are privileged, and can be used only by root\&. +.RE +.SH "AUTHOR" +.PP +\fBGeorges Khaznadar\fR <\&georgesk@debian\&.org\&> +.RS 4 +Wrote this manpage for the Debian system\&. +.RE +.SH "COPYRIGHT" +.br +Copyright \(co 2019 Georges Khaznadar +.br +.PP +This manual page was written for the Debian system (and may be used by others)\&. +.PP +Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 2 or (at your option) any later version published by the Free Software Foundation\&. +.PP +On Debian systems, the complete text of the GNU General Public License can be found in +/usr/share/common\-licenses/GPL\&. +.sp diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/boxesserver.xml b/extensions/fablabchemnitz/boxes.py/boxes/documentation/boxesserver.xml new file mode 100644 index 0000000..d6d17a2 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/boxesserver.xml @@ -0,0 +1,132 @@ + +.
will be generated. You may view the +manual page with: nroff -man .
| less'. A typical entry +in a Makefile or Makefile.am is: + +DB2MAN = /usr/share/sgml/docbook/stylesheet/xsl/docbook-xsl/manpages/docbook.xsl +XP = xsltproc -''-nonet -''-param man.charmap.use.subset "0" + +manpage.1: manpage.xml + $(XP) $(DB2MAN) $< + +The xsltproc binary is found in the xsltproc package. The XSL files are in +docbook-xsl. A description of the parameters you can use can be found in the +docbook-xsl-doc-* packages. Please remember that if you create the nroff +version in one of the debian/rules file targets (such as build), you will need +to include xsltproc and docbook-xsl in your Build-Depends control field. +Alternatively use the xmlto command/package. That will also automatically +pull in xsltproc and docbook-xsl. + +Notes for using docbook2x: docbook2x-man does not automatically create the +AUTHOR(S) and COPYRIGHT sections. In this case, please add them manually as + ... . + +To disable the automatic creation of the AUTHOR(S) and COPYRIGHT sections +read /usr/share/doc/docbook-xsl/doc/manpages/authors.html. This file can be +found in the docbook-xsl-doc-html package. + +Validation can be done using: `xmllint -''-noout -''-valid manpage.xml` + +General documentation about man-pages and man-page-formatting: +man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ + +--> + + + + + + + + + + + + + +]> + + + + &dhtitle; + &dhpackage; + + + &dhfirstname; + &dhsurname; + Wrote this manpage for the Debian system. +
+ &dhemail; +
+
+
+ + 2019 + &dhusername; + + + This manual page was written for the Debian system + (and may be used by others). + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU General Public License, + Version 2 or (at your option) any later version published by + the Free Software Foundation. + On Debian systems, the complete text of the GNU General Public + License can be found in + /usr/share/common-licenses/GPL. + +
+ + &dhucpackage; + &dhsection; + + + &dhpackage; + a web server to make boxes. + + + + &dhpackage; + + + + + DESCRIPTION + &dhpackage; is a web server which allows + one to compute sketches to + cut materials with a laser and make various types of boxes. + + + OPTIONS + + + + + If this optional parameter is set, the web service + will be done using this port number. By default, the port number + is 8000. + + + Please notice that port numbers lesser than 1024 are privileged, + and can be used only by root. + + + + + +
diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/presentation.odp b/extensions/fablabchemnitz/boxes.py/boxes/documentation/presentation.odp new file mode 100644 index 0000000..0fd25d3 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/documentation/presentation.odp differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/CONTRIBUTING.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/CONTRIBUTING.rst new file mode 100644 index 0000000..ac7b6bc --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/CONTRIBUTING.rst @@ -0,0 +1 @@ +.. include:: ../../CONTRIBUTING.rst diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/Makefile b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/Makefile new file mode 100644 index 0000000..43276bb --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = ../build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/README.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/README.rst new file mode 100644 index 0000000..4d547b7 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/README.rst @@ -0,0 +1,2 @@ +.. include:: ../../README.rst + :end-before: Documentation diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_architecture.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_architecture.rst new file mode 100644 index 0000000..c723b95 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_architecture.rst @@ -0,0 +1,97 @@ +Architecture +------------ + +Boxes.py it structured into several distinct tiers. + +User Interfaces +............... + +User interfaces allow users to render the different generators. They +handle the parameters of Generators and convert them to a readable +form. The user interfaces are located in :code:`scripts/`. Currently there is + +* scripts/boxes -- the command line interface +* scripts/boxesserver -- the web interface +* scripts/boxes2inx -- generates Inkscape extensions +* scripts/boxes_example.ipynb -- Jupyter notebook + + +Generators +.......... + +A (box) generator is a subclass of boxes.Boxes. It generates one +drawing. The subclasses overload .__init__() to set their parameters +and implement .render() that does the actual drawing. + +Generators are found in ``boxes/generators/``. They are included into +the web UI and the CLI tool by the name of their class. So whenever +you copy either an existing generator or the sceleton in +``boxes/generators/_template.py`` you need to change the name of the +main class first. + +Parts +..... + +Parts are a single call that draws something according to a set of parameters. +There are a number of standard parts. Their typical params are +explained in the API docs. + +The only real requirement for a part is that it must support the move +parameter for placement. + +Part Callbacks +++++++++++++++ + +Most parts support callbacks - either one in the middle for round +parts or one for each edge. They allow placing holes or other features +on the part, independent of edge type. Without using callbacks, you +will not have consistent placement of internal features. + +Navigation and Turtle Graphics +.............................. + +Many drawing commands in Boxes.py are Turtle Graphics commands. They +start at the current position and in the current direction and move +the coordinate system with them. This way the absolute coordinates are +never used and placement and movement is always relative to the +current position. + +There are a few functions to move the origin to a convenient position +or to return to a previously saved position. + +Edges +..... + +Edges are turtle graphic commands. But they have been elevated to +proper Classes to handle outsets. They can be passed as parameters to parts. +There is a set of standard edges found in ``.edges``. They are +associated with a single char which can be used instead of the +Edge object itself at most places. This allows passing the edge +description of a part as a string. + +Turtle graphics +............... + +There are a few turtle graphics commands that do the actual +drawing. Corners with an positive angle (going counter clockwise) +close the part while negative angles (going clockwise) create protrusions. +This is inversed for holes which need to be drawn clockwise. + +Getting this directions right is important to make the burn correction +(aka kerf) work properly. + +Simple drawing commands +....................... + +These also are simple drawing commands. Some of them get ``x``, ``y`` and +``angle`` parameters to draw somewhere specific. Some just draw right +at the current coordinate origin. Often these commands create holes or +hole patterns. + +Back end +........ + +Boxes.py used to use cairo as graphics library. It now uses its own - +pure Python - back end. It is not fully encapsulated +within the drawing methods of the Boxes class. Although this is the +long term goal. Boxes.ctx is the context all drawing is made on. diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_arguments.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_arguments.rst new file mode 100644 index 0000000..7b93e45 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_arguments.rst @@ -0,0 +1,75 @@ +Generator Arguments +------------------- + +Boxes.py uses the ``argparse`` standard library for handling the +arguments for the generators. It is used directly for the ``boxes`` +command line tool. But it also handles -- with some additional code -- +the web interface and the Inkscape extensions. To make this work one +has to limit the kind of parameters used. Boxes.py supports the +following types: + + * ``int`` + * ``float`` + * ``str`` + * ``boxes.boolarg`` -- an alternative to ``bool`` that works with the + web interface + * ``boxes.argparseSections`` -- multiple lengths e.g. for dividing up + a box in one direction + +and + +.. autoclass:: boxes.ArgparseEdgeType + +For the standard types there is code to create HTML and Inkscape +extensions. The other types can have ``.html()`` and ``.inx()`` +methods. + +The argument parser need to be built in the ``.__init__()`` method +after calling the method of the super class. Have a look at + +.. automethod:: boxes.generators._template.BOX.__init__ + +As many arguments are used over and over there is a function that can +add the most common ones: + +.. automethod:: boxes.Boxes.buildArgParser + +Check the source for details about the single arguments. + +Other arguments can be added with the normal argparser API - namely + +.. automethod:: argparse.ArgumentParser.add_argument + +of the ``Boxes.argparser`` attribute. + +Edge style arguments +.................... + +Edges that work together share a Settings class (and object). These +classes can create ``argparse`` groups: + +.. automethod:: boxes.edges.Settings.parserArguments + +See + +.. automethod:: boxes.generators._template.BOX.__init__ + +for a list of possible edge settings. These regular settings are used +in the standard edge instances used everywhere. For special edge +instances you can call them with a ``prefix`` parameter. But you then +need to deal with the results on your own. + +Default Arguments +................. + +The :ref:`default-args` get added automatically by the super class's +constructor. + +Accessing the Arguments +....................... + +For convenience content of the arguments are written to attributes of +the Boxes instance before ``.render()`` is called. This is done by +``Boxes.parseArgs``. But most people won't need to care as this is +handled by the framework. Be careful to **not overwrite important +methods or attributes by using conflicting argument names**. diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_burn.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_burn.rst new file mode 100644 index 0000000..ea65527 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_burn.rst @@ -0,0 +1,80 @@ +Burn correction +=============== + +The burn correction -- aka kerf -- is done in two separate steps. The +first mechanism is used during drawing. After rendering there is +a post processing step that replaces the inverted arcs of the inner corners by +Bezier loops that can be cut in a continuous motion. + +The first mechanism is integrated into the low level +commands of Boxes.py. So for the most part developers do not need to +care about it. Nevertheless they need to understand how it works to +catch the places the do need to care. + +Burn correction is done by increasing the radius of all outer +corners. This moves all the straight lines outward by the same +amount. This has the added benefit of not needing to change the length +of the straight lines -- making them independent of the adjacent +angles. An issue arises when it comes to inner corners. If they do +have a radius reducing it by the burn value does the right thing. But +for small radii and sharp corners (radius zero) this results in a +negative values. It turns out flipping over the arc for negative radii +allows keeping the lengths of the straight lines unchanged. So this is +what Boxes.py does: + +.. image:: burn.svg + +This results in the straight lines touching the piece. This would lead to +overcuts that are not as nice as proper dog bones as might be used by +a dedicated CAM software. But as Boxes.py is meant to be used for laser +cutting this deemed acceptable for a long time: + +.. image:: overcuts.svg + +Programmer's perspective +------------------------ + +For this to work it is important that outside is drawn in a +counterclockwise direction while holes are drawn in a +clockwise direction. + +:py:meth:`boxes.Boxes.corner` adjusts the radius automatically +according to **.burn**. This propagates to higher level +functions. Parts shipped with Boxes.py do take the +burn outset into account and execute callbacks at the correct position. + +In case developers move to a feature inside of a part or executing +callbacks while implementing a part they need to be aware of the burn +correction. :py:meth:`boxes.Boxes.cc` does correct for the outset if +called without an **y** parameter. But if a value is given one has to +add **self.burn** to compensate. Note that the **x** value typically +does not have to be corrected as the callbacks are executed from right +underneath the part. + +A similar approach is necessary when moving to a feature drawn inside +the part without the use of callbacks. Here you typically have to +correct for the outset at the outside of the part and again for inset +of the hole one is about to cut. This can be done in **x** or **y** +direction depending on whether the cut is started vertical or +horizontally. + +Replacing the inverted arcs +--------------------------- + +The inverted arcs have several drawbacks. For one they remove more +material than needed. This is not a big deal for laser cutters. But if +the boxes are cut with a CNC milling machine that can be +annoying. Another drawback is that the direction is reversed twice +which requires the tool (typically the laser head) to come to a total stop. + +To solve this issue all paths are scanned for intersecting lines that +are connected by an inverted arc. There the lines are shortened to the +intersection point and the arc is replaced by a Bezier loop that is +continues the lines and loops on the outside of the corner. That way +the path still removes additional material to make sure the full +inner corner is cleared out. The current implementation uses the +former end points of the lines as control points. This gives +reasonable results but errs on the save side. The amount of material +removed can probably be further optimized. + +.. image:: burn2.svg diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_drawing.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_drawing.rst new file mode 100644 index 0000000..8b56ecd --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_drawing.rst @@ -0,0 +1,90 @@ +Drawing commands +================ + + +Turtle Graphics commands +------------------------ + +These commands all move the coordinate system with them. + +.. automethod:: boxes.Boxes.edge +.. automethod:: boxes.Boxes.corner +.. automethod:: boxes.Boxes.curveTo +.. automethod:: boxes.Boxes.polyline + +Special Functions +................. + +.. automethod:: boxes.Boxes.bedBoltHole + +Latch and Grip +.............. + +These should probably be Edge classes. But right now they are still functions. + +.. automethod:: boxes.Boxes.grip +.. automethod:: boxes.Boxes.latch +.. automethod:: boxes.Boxes.handle + +Tab support +........... + +Tabs are small interruptions in the border of a part to keep it in +place. They are enabled with the **tabs** parameter. All +**Edges** automatically create about two tabs. So parts like +:py:meth:`boxes.Boxes.rectangularWall` will have 8 tabs holding them +in place. Because of this developers often don't need to be concerned +about tabs. But some part may be completely drawn by low level Turtle +Graphics commands. For those both :py:meth:`boxes.Boxes.edge` and +:py:meth:`boxes.Boxes.corner` do support a **tabs** parameter. In +addition the length of the line segments in :py:meth:`boxes.Boxes.polyline` can +be given as a tuple **(length, tabs)**. + +Draw Commands +------------- + +These commands do not change the coordinate system but get the +coordinates passed as parameters. All of them are either some sort of +hole or text. These artifacts are placed somewhere independently of +some continuous outline of the part their on. + +.. automethod:: boxes.Boxes.hole +.. automethod:: boxes.Boxes.rectangularHole +.. automethod:: boxes.Boxes.dHole +.. automethod:: boxes.Boxes.flatHole +.. automethod:: boxes.Boxes.text +.. automethod:: boxes.Boxes.NEMA +.. automethod:: boxes.Boxes.TX +.. automethod:: boxes.Boxes.flex2D +.. py:class:: NutHole + +An instance is available as **boxes.Boxes.nutHole()** + +An instance of + +.. autoclass:: boxes.edges.FingerHoles + :noindex: + +is accessible as **Boxes.fingerHolesAt**. + + +Hexagonal Hole patterns +....................... + +Hexagonal hole patterns are one way to have some ventilation for +housings made with Boxes.py. Right now both ``.rectangularWall()`` +and ``.roundedPlate()`` do supports this pattern directly by passing +the parameters to the calls. For other use cases these more low level +methods can be used. + +For now this is the only supported pattern for ventilation slots. More +may be added in the future. + +There is a global Boxes.hexHolesSettings object that is used if no settings are +passed. It currently is just a tuple of (r, dist, style) defaulting to +(5, 3, 'circle') but might be replace by a Settings instance in the future. + +.. automethod:: boxes.Boxes.hexHolesRectangle +.. automethod:: boxes.Boxes.hexHolesCircle +.. automethod:: boxes.Boxes.hexHolesPlate +.. automethod:: boxes.Boxes.hexHolesHex diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_edges.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_edges.rst new file mode 100644 index 0000000..7e77477 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_edges.rst @@ -0,0 +1,193 @@ +Edges +===== + +Edges are what makes Boxes.py work. They draw a -- more or less -- straight +border to the current piece. They are part of the turtle graphics part +of Boxes.py. This means they start at the current position and current +direction and move the current position to the end of the edge. + +Edge instances have a Settings object associated with them that keeps +the details about what the edge should look like. Edges that are +supposed to work together share the same Settings object to ensure +they fit together - assuming they have the same length. Most edges are +symmetrical to ensure they fit together even when drawn from different +directions. Although there are a few exceptions - mainly edges that +provide special features like hinges. + +As edges started out as methods of the main Boxes class they still are +callables. It turned out that the edges need to provide a bit more +information to allow the surrounding code to handle them +properly. When drawing an Edge there is a virtual straight line that +is the border the shape of the part (e.g. an rectangle). But the +actual Edge often has to be drawn elsewhere. The best example is probably +the ``F`` Edge that matches the normal finger joints. It has to start +one material thickness outside of the virtual border of the part so the +cutouts for the opposing fingers just touch the border. The Edge +classes have a number of methods to deal with these kinds of offsets. + +A set of instances are kept in the ``.edges`` attribute of the +``Boxes`` class. It is a dict with strings of length one as keys: + +* aAbB : reserved to be used in generators +* c : ClickConnector +* C : ClickEdge +* d : DoveTailJoint +* D : DoveTailJointCounterPart +* e : Edge +* E : OutSetEdge +* f : FingerJointEdge +* F : FingerJointEdgeCounterPart +* g : GrippingEdge +* G : MountingEdge +* h : FingerHoleEdge +* ijk : Hinge (start, end, both sides) +* IJK : HingePin (start, end, both sides) +* L : LidHoleEdge +* l : LidEdge +* M : LidSideLeft +* m : LidLeft +* N : LidSideRight +* n : LidRight +* Oo : ChestHinge +* Pp : ChestHingeTop +* Q : ChestHingeFront +* q : ChestHingePin +* R : RackEdge +* s : StackableEdge +* S : StackableEdgeTop +* š : StackableFeet +* Š : StackableHoleEdgeTop +* T : RoundedTriangleFingerHolesEdge +* t : RoundedTriangleEdge +* uUvV : CabinetHingeEdge +* X : FlexEdge +* y : HandleEdge +* Y : HandleHoleEdge +* Z : GroovedEdgeCounterPart +* z : GroovedEdge + +Edge base class +--------------- + +.. autoclass:: boxes.edges.BaseEdge + :members: + +.. automethod:: boxes.edges.BaseEdge.__call__ + +Settings Class +-------------- + +.. autoclass:: boxes.edges.Settings + :members: + + +Straight Edges +-------------- + +.. autoclass:: boxes.edges.Edge +.. autoclass:: boxes.edges.OutSetEdge + +Grip +---- + +.. autoclass:: boxes.edges.GripSettings +.. autoclass:: boxes.edges.GrippingEdge + +Stackable Edges +--------------- + +.. autoclass:: boxes.edges.StackableEdge +.. autoclass:: boxes.edges.StackableEdgeTop + +Stackable Edge Settings +....................... + +.. autoclass:: boxes.edges.StackableSettings + :members: + +Finger joints +------------- + +Finger joints are a simple way of joining two sheets (e.g. of plywood). They +work best at an 90° angle. There are two different sides matching each +other. As a third alternative there are holes that the fingers of one +sheet can plug into. This allows stable T connections especially +useful for inner walls. + +.. autoclass:: boxes.edges.FingerJointEdge +.. autoclass:: boxes.edges.FingerJointEdgeCounterPart +.. autoclass:: boxes.edges.FingerHoleEdge +.. autoclass:: boxes.edges.CrossingFingerHoleEdge + +In addition there is + +.. autoclass:: boxes.edges.FingerHoles + +which is no Edge but fits ``FingerJointEdge``. + +An instance of is accessible as **Boxes.fingerHolesAt**. + +Finger Joint Settings +..................... + +.. autoclass:: boxes.edges.FingerJointSettings + :members: + +Bed Bolts +......... + +.. autoclass:: boxes.edges.BoltPolicy + +.. autoclass:: boxes.edges.Bolts + +Dovetail Joints +---------------- +Dovetails joints can only be used to join two pieces flatly. This +limits their use to closing some round form created with flex areas or +for joining several parts to a bigger one. For this use case they are +much stronger than simple finger joints and can also bare pulling forces. + +.. autoclass:: boxes.edges.DoveTailJoint +.. autoclass:: boxes.edges.DoveTailJointCounterPart + +Dovetail Settings +.................. + +.. autoclass:: boxes.edges.DoveTailSettings + :members: + +Flex +---- +.. autoclass:: boxes.edges.FlexEdge + +Flex Settings +............. + +.. autoclass:: boxes.edges.FlexSettings + +Slots +----- +.. autoclass:: boxes.edges.Slot +.. autoclass:: boxes.edges.SlottedEdge + +CompoundEdge +------------ +.. autoclass:: boxes.edges.CompoundEdge + +Hinges +------ + +Hinge Settings +.............. + +.. autoclass:: boxes.edges.HingeSettings + +Hinge +..... + +.. autoclass:: boxes.edges.Hinge + +HingePin +........ + +.. autoclass:: boxes.edges.HingePin diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_examples.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_examples.rst new file mode 100644 index 0000000..2e69e47 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_examples.rst @@ -0,0 +1,97 @@ +Examples +-------- + +Decide whether you want to start from scratch or want to rework an +existing generator. + +You should go over the arguments first. Get at least the most basic +arguments done. For things you are still unsure you can just use a +attribute set in the .__init__() method and turn it into a proper +argument later on. + +Depending on what you want to do you can work on the different levels +of the API. You can either use what is there and combine it into +something new or you can implements new things in the appropriate level. + +Here are some examples: + +Housing for some electronics +............................ + +You can use the ElectronicsBox or the ClosedBox as a basis. Write some +callbacks to place holes in the walls to allow accessing the ports of +the electronics boards. Place some holes to screw spacers into the +bottom to mount the PBC on. + +NemaMount +......... + +This is a good non box example to look at. + +.. autoclass:: boxes.generators.nemamount.NemaMount + +Note that although it produces a cube like object it uses separate +variables (``x``, ``y``, ``h``) for the different axis. Probably +because it started as a copy of another generator like ``ClosedBox``. + +DisplayShelf +............ + +.. autoclass:: boxes.generators.displayshelf.DisplayShelf + +The DisplayShelf is completely made out of rectangularWalls(). It uses +a callback to place all the fingerHolesAt() at the right places on the sides. +While the use of the Boxes.py API is pretty straightforward the +calculations needed are a bit more tricky. You can use the ``debug`` +default param to check if you got things right when attempting +something like this yourself. + +Note that the front walls and the shelfs form a 90° angle so they work +with the default FingerJoints. + +BinTray +....... + +.. autoclass:: boxes.generators.bintray.BinTray + +The BinTray is based on the TypeTray generator: + +.. autoclass:: boxes.generators.typetray.TypeTray + +TypeTray is an already pretty complicated generator. + +BinTray replaces the now vertical front (former top) edges with a +special purpose one that does add the triangles: + +.. autoclass:: boxes.generators.bintray.BinFrontEdge + +The ``hi`` (height of inner walls) argument was removed although the +variable is still used internally - out of laziness. + +To complete the bin the front walls are added. Follow up patches then +switched the slots between the vertical and horizontal walls to have +better support for the now bottoms of the bins. Another patch adds +angled finger joints for connecting the front walls with the bottoms +of the bins. + +The TrafficLight generator uses a similar technique implementing its +own Edge class. But it uses its own code to generate all the wall needed. + +Stachel +....... + +.. autoclass:: boxes.generators.stachel.Stachel + +Stachel allows mounting a monopod to a bass recorder. It is basically +just one part repeated with different parameters. It can't really make +use of much of the Boxes.py library. It implements this one part +including the ``move`` parameter and draws everything using the +``.polyline()`` method. This is pretty painful as lots of angles and +distances need to be calculated by hand. + +For symmetric sections it passes the parameters to ``.polyline`` twice +-- first in normal order and then reversed to get the mirrored section. + +This generator is beyond what Boxes.py is designed for. If you need +something similar you may want to use another tool like OpenScad or a +traditional CAD program. diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_existing_parts.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_existing_parts.rst new file mode 100644 index 0000000..b3fb4bf --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_existing_parts.rst @@ -0,0 +1,27 @@ +Existing Parts +-------------- + +A couple of commands can create whole parts like walls. Typically the +sizes given are the inner dimension not including additional space +needed for burn compensation or joints. + +Currently there are the following parts: + +.. automethod:: boxes.Boxes.rectangularWall +.. automethod:: boxes.Boxes.flangedWall +.. automethod:: boxes.Boxes.rectangularTriangle +.. automethod:: boxes.Boxes.regularPolygonWall +.. automethod:: boxes.Boxes.polygonWall +.. automethod:: boxes.Boxes.roundedPlate +.. automethod:: boxes.Boxes.surroundingWall + +Parts Class +........... + +More parts are available in a separate class. An instance is available as +**Boxes.parts** + +.. automethod:: boxes.parts.Parts.disc +.. automethod:: boxes.parts.Parts.wavyKnob +.. automethod:: boxes.parts.Parts.concaveKnob +.. automethod:: boxes.parts.Parts.ringSegment diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_generator.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_generator.rst new file mode 100644 index 0000000..26be044 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_generator.rst @@ -0,0 +1,45 @@ + +Generators +========== + +Generators are subclasses of + +.. autoclass:: boxes.Boxes + +Most code is directly in this class. Subclass are supposed to over +write the ``.__init__()`` and ``.render()`` method. + +The Boxes class keeps a canvas object (self.ctx) that all +drawing is made on. In addition it keeps a couple of global settings +used for various drawing operations. See the ``.__init__()`` method +for the details. + +For implementing a new generator forking an existing one or using the +``boxes/generators/_template.py`` is probably easier than starting +from scratch. + +Many methods and attributes are for use of the subclasses. These +methods are the interface for the user interfaces to interact with the +generators: + +.. automethod:: boxes.Boxes.__init__ + +.. automethod:: boxes.Boxes.parseArgs +.. automethod:: boxes.Boxes.render + +.. automethod:: boxes.Boxes.open +.. automethod:: boxes.Boxes.close + +Handling Generators +------------------- + +To handle the generators there is code in the ``boxes.generators`` +package. + +.. automodule:: boxes.generators + :members: + :undoc-members: + +This adds generators to the user interfaces automatically. For this to +work it is important that the class names are unique. So whenever you +start a new generator please change the class name right away. diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_navigation.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_navigation.rst new file mode 100644 index 0000000..98265de --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_navigation.rst @@ -0,0 +1,42 @@ +Navigation +---------- + +The back end can both move the origin and the current point from +which the next line is going to start. Boxes.py hides this by using +Turtle Graphics commands that also move the origin to the end of the +last line. Other drawing commands restore the current position after +they are finished. + +Moving the origin like this allows ignoring the absolute coordinates +and for all movement and drawing to be relative to the current +position. The current position not only consists of a point on +the drawing canvas but also a direction. + +To move the origin to a different location there are these two methods: + +.. automethod:: boxes.Boxes.moveTo +.. automethod:: boxes.Boxes.moveArc + +Often it is necessary to return to a position e.g. after placing a +row of parts. This can be done with the following context manager: + +.. automethod:: boxes.Boxes.saved_context() + +It can be used with the following code pattern: + +.. code-block:: python + + with self.saved_context(): + self.rectangularWall(x, h, move="right") + self.rectangularWall(y, h, move="right") + self.rectangularWall(y, h, move="right") + self.rectangularWall(x, h, move="right") + self.rectangularWall(x, h, move="up only") + + # continue above the row + +Parts of the code still directly use the back end primitives **Boxes.ctx.save()** +and **Boxes.ctx.restore()**. But this has several disadvantages and is +discouraged. For one it requires matching calls. It also does not +reset the starting point of the next line. This is "healed" by a +follow up **.moveTo()**. Use **.moveTo(0, 0)** if in doubt. diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_parts.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_parts.rst new file mode 100644 index 0000000..1e46f29 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/api_parts.rst @@ -0,0 +1,86 @@ +Parts +----- + + + + +There are a few parameters shared by many of the parts: + +The callback parameter +...................... + +The callback parameter can take on of the following forms: + +* A function (or bound method) that expects one parameter: the number of the side the callback is currently called for. +* A dict with some of the numbers of the sides as keys and functions without parameters as values. +* A list of functions without parameters. The list may contain None as place holder and be shorter than the number of sides. + +The callback functions are called with the side of the part at the +positive x and y axis. If the edge uses up space this space is below +the x axis. You do not have to restore the coordinate settings in the +callback. + +Instead of functions it can be handy to use a lambda expression +calling the one building block function you need (e.g. fingerHolesAt). + +For your own parts you can use this helper function: + +.. automethod:: boxes.Boxes.cc + +For finding the right piece to the *callback* parameter this function is used: + +.. automethod:: boxes.Boxes.getEntry + + +The move parameter +.................. + +For placing the parts the ``move`` parameter can be used. It is string +with space separated words - at most one of each of those options: + +* left / right +* up / down +* only + +If "only" is given the part is not drawn but only the move is +done. This can be useful to go in one direction after having placed +multiple parts in the other and have returned with ``.ctx.restore()``. + +For implementing parts the following helper function can be used to +implement a ``move`` parameter: + +.. automethod:: boxes.Boxes.move + +It needs to be called before and after drawing the actual part with +the proper ``before`` parameter set. + +The edges parameter +................... + +The ``edges`` parameter needs to be an iterable of Edge instances to be +used as edges of the part. Instead of instances it is possible to pass +a single character that is looked up in the ``.edges`` dict. This +allows to pass a string with the desired characters per edge. By +default the following character are supported: + +* e : straight edge +* E : as above but extended outside by one thickness +* f, F : finger joints +* h : edge with holes for finger joints +* d, D : dove tail joints + +Generators can register their own Edges by putting them into the +``.edges`` dictionary. + +Same applies to the parameters of ``.surroundingWall`` although they +denominate single edge (types) only. + +PartsMatrix +........... + +To place a grid of identical parts, partMatrix can used: + +.. automethod:: boxes.Boxes.partsMatrix + +It creates one big block of parts. The move param treats this block like one big +part. diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/apidoc.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/apidoc.rst new file mode 100644 index 0000000..cd3b80f --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/apidoc.rst @@ -0,0 +1,20 @@ +Using the Boxes.py API +====================== + +If there is no generator fitting your needs you can either adjust an +existing one (may be by copying it to another name first) or writing a +new one from scratch. + +.. toctree:: + :maxdepth: 1 + + api_architecture + api_generator + api_arguments + api_navigation + api_parts + api_existing_parts + api_edges + api_drawing + api_burn + api_examples diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/boxes.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/boxes.rst new file mode 100644 index 0000000..3778556 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/boxes.rst @@ -0,0 +1,120 @@ +boxes package +============= + +Subpackage boxes.generators +--------------------------- + +.. automodule:: boxes.generators + :members: + :undoc-members: + :show-inheritance: + +:doc:`generators` + +Submodules +---------- + +boxes.Color module +------------------ + +.. automodule:: boxes.Color + :members: + :undoc-members: + :show-inheritance: + +boxes.edges module +------------------ + +.. automodule:: boxes.edges + :members: + :undoc-members: + :show-inheritance: + +boxes.formats module +-------------------- + +.. automodule:: boxes.formats + :members: + :undoc-members: + :show-inheritance: + +boxes.gears module +------------------ + +.. automodule:: boxes.gears + :members: + :undoc-members: + :show-inheritance: + +boxes.lids module +----------------- + +.. automodule:: boxes.lids + :members: + :undoc-members: + :show-inheritance: + +boxes.mounts module +------------------- + +.. automodule:: boxes.mounts + :members: + :undoc-members: + :show-inheritance: + +boxes.parts module +------------------ + +.. automodule:: boxes.parts + :members: + :undoc-members: + :show-inheritance: + +boxes.pulley module +------------------- + +.. automodule:: boxes.pulley + :members: + :undoc-members: + :show-inheritance: + +boxes.robot module +------------------ + +.. automodule:: boxes.robot + :members: + :undoc-members: + :show-inheritance: + +boxes.servos module +------------------- + +.. automodule:: boxes.servos + :members: + :undoc-members: + :show-inheritance: + +boxes.svgutil module +-------------------- + +.. automodule:: boxes.svgutil + :members: + :undoc-members: + :show-inheritance: + +boxes.vectors module +-------------------- + +.. automodule:: boxes.vectors + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: boxes + :members: + :undoc-members: + :show-inheritance: diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/boxes2rst.py b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/boxes2rst.py new file mode 100755 index 0000000..8dd98bc --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/boxes2rst.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# Copyright (C) 2017 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os.path +import sys + +try: + import boxes.generators +except ImportError: + sys.path.append(os.path.dirname(__file__) + "/../..") + import boxes.generators + + +class Boxes2rst: + def __init__(self) -> None: + self.boxes = {b.__name__: b() for b in boxes.generators.getAllBoxGenerators().values() if b.webinterface} + self.groups = boxes.generators.ui_groups + self.groups_by_name = boxes.generators.ui_groups_by_name + + for name, box in self.boxes.items(): + self.groups_by_name.get(box.ui_group, self.groups_by_name["Misc"]).add(box) + + def write(self, targetFile: str) -> None: + with open(targetFile, "w") as f: + for name, group in self.groups_by_name.items(): + f.write(f"{name}\n----------------\n\n") + for box in group.generators: + f.write(box.__class__.__name__) + f.write("\n..........................................\n\n") + f.write(f"\n\n.. autoclass:: {box.__class__.__module__}.{box.__class__.__name__}") + f.write("\n\n") + if os.path.exists(f"../../static/samples/{box.__class__.__name__}.jpg"): + f.write(f".. image:: ../../static/samples/{box.__class__.__name__}.jpg\n\n") + + +def main() -> None: + if len(sys.argv) != 2: + print("Usage: boxes2rst.py TARGETFILE") + return + b = Boxes2rst() + b.write(sys.argv[1]) + + +if __name__ == "__main__": + main() diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/burn.svg b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/burn.svg new file mode 100644 index 0000000..44a3633 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/burn.svg @@ -0,0 +1,61 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/burn2.svg b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/burn2.svg new file mode 100644 index 0000000..ed1298c --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/burn2.svg @@ -0,0 +1,99 @@ + + + + + + RoundedBox + + + + FlexBox - RoundedBox + 2021-04-25 18:47:19 + http://localhost:8000/RoundedBox?FingerJoint_angle=90.0&FingerJoint_style=rectangular&FingerJoint_surroundingspaces=1&FingerJoint_edge_width=1.0&FingerJoint_finger=2.0&FingerJoint_play=0.0&FingerJoint_space=2.0&FingerJoint_width=1.0&Flex_stretch=1.05&Flex_connection=1.0&Flex_distance=0.5&Flex_width=5.0&x=50&y=50&h=50&outside=0&radius=5&wallpieces=1&edge_style=f&top=closed&thickness=3.0&format=svg&tabs=0.0&debug=0&reference=100&burn=1&render=1 + Box with vertical edges rounded + +Created with Boxes.py (https://festi.info/boxes.py) +Command line: boxes RoundedBox --FingerJoint_angle=90.0 --FingerJoint_style=rectangular --FingerJoint_surroundingspaces=1 --FingerJoint_edge_width=1.0 --FingerJoint_finger=2.0 --FingerJoint_play=0.0 --FingerJoint_space=2.0 --FingerJoint_width=1.0 --Flex_stretch=1.05 --Flex_connection=1.0 --Flex_distance=0.5 --Flex_width=5.0 --x=50 --y=50 --h=50 --outside=0 --radius=5 --wallpieces=1 --edge_style=f --top=closed --thickness=3.0 --format=svg --tabs=0.0 --debug=0 --reference=100 --burn=1 +Url: http://localhost:8000/RoundedBox?FingerJoint_angle=90.0&FingerJoint_style=rectangular&FingerJoint_surroundingspaces=1&FingerJoint_edge_width=1.0&FingerJoint_finger=2.0&FingerJoint_play=0.0&FingerJoint_space=2.0&FingerJoint_width=1.0&Flex_stretch=1.05&Flex_connection=1.0&Flex_distance=0.5&Flex_width=5.0&x=50&y=50&h=50&outside=0&radius=5&wallpieces=1&edge_style=f&top=closed&thickness=3.0&format=svg&tabs=0.0&debug=0&reference=100&burn=1&render=1 +SettingsUrl: http://localhost:8000/RoundedBox?FingerJoint_angle=90.0&FingerJoint_style=rectangular&FingerJoint_surroundingspaces=1&FingerJoint_edge_width=1.0&FingerJoint_finger=2.0&FingerJoint_play=0.0&FingerJoint_space=2.0&FingerJoint_width=1.0&Flex_stretch=1.05&Flex_connection=1.0&Flex_distance=0.5&Flex_width=5.0&x=50&y=50&h=50&outside=0&radius=5&wallpieces=1&edge_style=f&top=closed&thickness=3.0&format=svg&tabs=0.0&debug=0&reference=100&burn=1 + + image/svg+xml + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/conf.py b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/conf.py new file mode 100644 index 0000000..304b482 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/conf.py @@ -0,0 +1,94 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +import os +import sys +from datetime import datetime + +sys.path.append(os.path.abspath('../..')) + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'boxes.py' +project_copyright = datetime.now().year.__str__() + ', Florian Festi' +author = 'Florian Festi' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.coverage', + 'sphinx.ext.ifconfig', + 'sphinx.ext.viewcode', + 'sphinx.ext.githubpages', +] + +templates_path = ['_templates'] +#exclude_patterns = [] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The root toctree document. +root_doc = 'index' + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'nature' +html_static_path = ['_static'] + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = "../../static/boxes-logo.svg" + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = "../../static/favicon.ico" + +# Output file base name for HTML help builder. +htmlhelp_basename = 'boxespydoc' + +# If this is not None, a ‘Last updated on:’ timestamp is inserted at every page bottom. +html_last_updated_fmt = '' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = {} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'boxespy.tex', 'boxes.py Documentation', + 'Florian Festi', 'manual'), +] + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'boxespy', 'boxes.py Documentation', + ['Florian Festi'], 1) +] + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'boxespy', 'boxes.py Documentation', + 'Florian Festi', 'boxespy', 'One line description of project.', + 'Miscellaneous'), +] diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/faq.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/faq.rst new file mode 100644 index 0000000..93320aa --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/faq.rst @@ -0,0 +1,54 @@ +========================== +Frequently Asked Questions +========================== + +.. toctree:: + :maxdepth: 1 + +Can I sell boxes I created with Boxes.py +---------------------------------------- + +Yes. Boxes.py is under the GPLv3 license (see https://www.gnu.org/licenses/gpl-3.0.html). This license grants you far reaching rights on what you can do with the software including using it and the drawings it produces to any means. The license also puts some obligations on you. But those are about changing and distributing the software itself. The resulting drawings do not fall under the GPL license. + +Why do my parts not fit together? +--------------------------------- + +Well, this could be a bug in Boxes.py but there are a few more likely causes to check for: + +* The material you use does not have the thickness you think. Measure it with at least a caliper. Even a few hundredth of a millimeter will make the difference between a loose fit, a light or a heavy pressfit or no fit at all. + +* You might have chosen the "burn" value too big. As it compensates for the material cut away by the laser smaller values make a looser fit, bigger values make a tighter fit. The right value may be different for different materials and different thicknesses. + +Why is my box a bit too big? +---------------------------- + +By default all sizes are inner sizes. So on the outside the box is bigger as the walls need to go somewhere. Some generators offer an "outside" param that includes the walls in the measurements. In general you should check the generated parts for plausibility before hitting the start button on your laser cutter. + +Why is my box a bit too small? +------------------------------ + +See above. + +Why are my parts in the totally wrong size? +------------------------------------------- + +Unfortunately some formats do not save the units of measurement or don't do so properly. DXF and SVG fall into this category. So different tools may see the same file in different sizes. You can use the "reference" param to get a rectangle of a defined size to check if the size is still right at the end of your tool chain. + +Why are there tiny, weird loops in the corners? +----------------------------------------------- + +These are called dog bones and make sure the corner is completely cut out. As lasers and milling tools are round they can't cut sharp inner corners. Have a look at :doc:`burn correction details ` for details. + +I really don't want those weird, tiny loops? +-------------------------------------------- + +You can set the ``inner_corners`` default setting to ``corner`` + +What settings were used to generate a drawing? +---------------------------------------------- + +If you do have a SVG or PostScript you can look into the meta data of the file. Most document viewers will have a ``Document properties`` window. You can also just open the file with a text editor and find the details at the first few lines. + +Note that you can just use the URL in there to get back to the settings page to change some values. The difference between the settings and the rendered drawing is just ``render=0`` or ``render=1`` at the end of the URL. + +For other formats you are currently out of luck. diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/generators.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/generators.rst new file mode 100644 index 0000000..c6e1348 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/generators.rst @@ -0,0 +1,9 @@ +All Box Generators +================== + +Generators are organized in several Groups + +.. contents:: + :local: + +.. include:: generators.inc diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/index.html b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/index.html new file mode 100644 index 0000000..bab73b0 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/index.html @@ -0,0 +1,15 @@ + + + + + + + + Page Redirection + + + If you are not redirected automatically, follow this link. + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/index.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/index.rst new file mode 100644 index 0000000..3cf232b --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/index.rst @@ -0,0 +1,27 @@ +.. boxes.py documentation master file, created by + sphinx-quickstart on Sun Mar 27 12:04:59 2016. + +Boxes.py +======== + +Create boxes and more with a laser cutter! + +Contents: + +.. toctree:: + :maxdepth: 1 + + README + faq + install + usermanual + CONTRIBUTING.rst + apidoc + generators + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install.rst new file mode 100644 index 0000000..6966fad --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install.rst @@ -0,0 +1,112 @@ +Installation +============ + +Boxes.py is a pure Python project that does support the regular setuptools +method of shipping with :code:`setup.py`. :code:`setup.py --help-commands` and +:code:`setup.py CMD --help` provide the necessary documentation for building, +installing or building binary formats. + +Requirements +------------ + +Affine +........ +:code:`Affine` (package name may be :code:`python-affine` or +:code:`python3-affine`) is used for vector calculation. + +Shapely +....... +:code:`shapely` (package name may be :code:`python-shapely` or +:code:`python3-shapely`) is used for filling shapes (with holes). + + +Markdown +........ +:code:`Markdown` (package name may be :code:`python-markdown` or +:code:`python3-markdown`) is used to format the description texts. + + +setuptools +.......... + +Setup.py uses the :code:`setuptools` library (package name may be +:code:`python*-setuptools`). You only need it if you want to build the +package. + +pstoedit +........ + +While not a hard requirement Boxes.py uses :code:`pstoedit` (sometimes :code:`ps2edit`) to offer formats +that are not supported by Cairo: DXF, gcode, PLT. Currently the location +Boxes.py looks for :code:`pstoedit` is hard coded to :code:`/usr/bin/pstoedit` +in the :code:`boxes.formats.Formats` class. + +Python +...... + +Boxes.py is implemented in Python 3. For supported minor version see :code:`setup.py`. + +Sphinx +...... + +For building the documentation locally you need the *Sphinx* documentation +generator (package name may be python-sphinx or python3-sphinx). It is +not needed for anything else. Boxes.py can be run and changed just +fine without. + +Running from working dir +------------------------ + +Due to lazy developer(s) Boxes.py can also run from the Git checkout. +The scripts in :code:`scripts/` are all supposed to just work right +after :code:`git clone`. The Inkscape needs a bit manual work to get +running. See below. + +Inkscape +-------- + +**As binary** + +Boxes.py can be used as a set of Inkscape plugins. The package does +install the necessary .inx files to :code:`/usr/share/inkscape/extensions` +on unix operating systems. The .inx files assume that the :code:`boxes` +executable is available in the path (which it is when installing the +binary package) + +**git repository easy way** + +After cloning it may be most convenient to generate the .inx files +right in place by executing :code:`scripts/boxes2inkscape` with the target +path as only parameter. + +- global: :code:`scripts/boxes2inkscape /usr/share/inkscape/extensions/` +- userspace: :code:`scripts/boxes2inkscape ~/.config/inkscape/extensions/` + +On non unix operating the target directories may differ. You can look +up the directories *"User extensions"* and *"Inkscape extensions"* within +the Inkscape preferences *Edit -> Preferences... -> System*. + +**git repository manual way** + +:code:`setup.py build` creates the :code:`*.inx` files in the :code:`inkex/` directory. + +They then have to be copied in either the global or the per user +extension directory of Inkscape. These are +:code:`/usr/share/inkscape/extensions/` and +:code:`~/.config/inkscape/extensions/` on a unix operating system. +On non unix operating the target directories may differ. You can look +up the directories *"User extensions"* and *"Inkscape extensions"* within +the Inkscape preferences *Edit -> Preferences... -> System*. + +As an alternative you can create a symlink to the :code:`inkex/` directory +within the desired inkscape extension directory. + + +Platform specific instructions +------------------------------ + +.. toctree:: + :maxdepth: 2 + :glob: + + install/* diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/log-wsl-win11.txt b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/log-wsl-win11.txt new file mode 100644 index 0000000..d1c474b --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/log-wsl-win11.txt @@ -0,0 +1,173 @@ +usuario@Soporte-3:~$ git clone https://github.com/florianfesti/boxes.git +Cloning into 'boxes'... +remote: Enumerating objects: 7867, done. +remote: Counting objects: 100% (545/545), done. +remote: Compressing objects: 100% (328/328), done. +remote: Total 7867 (delta 234), reused 514 (delta 216), pack-reused 7322 +Receiving objects: 100% (7867/7867), 64.82 MiB | 5.45 MiB/s, done. +Resolving deltas: 100% (5330/5330), done. +usuario@Soporte-3:~$ cd boxes +usuario@Soporte-3:~/boxes$ ls +boxes documentation LICENSE.txt po scripts +CODE_OF_CONDUCT.md examples locale README.rst setup.py +CONTRIBUTING.rst inkex MANIFEST.in requirements.txt static +usuario@Soporte-3:~/boxes$ python3 -m pip install -r requirements.txt +Collecting affine + Downloading affine-2.3.1-py2.py3-none-any.whl (16 kB) +Collecting markdown + Downloading Markdown-3.4.1-py3-none-any.whl (93 kB) + |████████████████████████████████| 93 kB 1.7 MB/s +Requirement already satisfied: setuptools in /usr/lib/python3/dist-packages (from -r requirements.txt (line 3)) (52.0.0) +Collecting sphinx + Downloading sphinx-5.2.3-py3-none-any.whl (3.2 MB) + |████████████████████████████████| 3.2 MB 8.2 MB/s +Collecting shapely + Downloading Shapely-1.8.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (2.1 MB) + |████████████████████████████████| 2.1 MB 5.4 MB/s +Collecting importlib-metadata>=4.4 + Downloading importlib_metadata-5.0.0-py3-none-any.whl (21 kB) +Collecting zipp>=0.5 + Downloading zipp-3.9.0-py3-none-any.whl (5.8 kB) +Collecting docutils<0.20,>=0.14 + Downloading docutils-0.19-py3-none-any.whl (570 kB) + |████████████████████████████████| 570 kB 5.2 MB/s +Collecting snowballstemmer>=2.0 + Downloading snowballstemmer-2.2.0-py2.py3-none-any.whl (93 kB) + |████████████████████████████████| 93 kB 1.2 MB/s +Collecting Pygments>=2.12 + Downloading Pygments-2.13.0-py3-none-any.whl (1.1 MB) + |████████████████████████████████| 1.1 MB 4.4 MB/s +Collecting sphinxcontrib-serializinghtml>=1.1.5 + Downloading sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl (94 kB) + |████████████████████████████████| 94 kB 3.6 MB/s +Collecting packaging>=21.0 + Downloading packaging-21.3-py3-none-any.whl (40 kB) + |████████████████████████████████| 40 kB 4.4 MB/s +Collecting sphinxcontrib-devhelp + Downloading sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl (84 kB) + |████████████████████████████████| 84 kB 4.6 MB/s +Collecting sphinxcontrib-htmlhelp>=2.0.0 + Downloading sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl (100 kB) + |████████████████████████████████| 100 kB 6.5 MB/s +Collecting requests>=2.5.0 + Downloading requests-2.28.1-py3-none-any.whl (62 kB) + |████████████████████████████████| 62 kB 1.1 MB/s +Collecting Jinja2>=3.0 + Downloading Jinja2-3.1.2-py3-none-any.whl (133 kB) + |████████████████████████████████| 133 kB 8.4 MB/s +Collecting sphinxcontrib-jsmath + Downloading sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl (5.1 kB) +Collecting imagesize>=1.3 + Downloading imagesize-1.4.1-py2.py3-none-any.whl (8.8 kB) +Collecting alabaster<0.8,>=0.7 + Downloading alabaster-0.7.12-py2.py3-none-any.whl (14 kB) +Collecting babel>=2.9 + Downloading Babel-2.10.3-py3-none-any.whl (9.5 MB) + |████████████████████████████████| 9.5 MB 9.2 MB/s +Collecting sphinxcontrib-applehelp + Downloading sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl (121 kB) + |████████████████████████████████| 121 kB 9.5 MB/s +Collecting sphinxcontrib-qthelp + Downloading sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl (90 kB) + |████████████████████████████████| 90 kB 7.2 MB/s +Collecting pytz>=2015.7 + Downloading pytz-2022.4-py2.py3-none-any.whl (500 kB) + |████████████████████████████████| 500 kB 10.4 MB/s +Collecting MarkupSafe>=2.0 + Downloading MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (25 kB) +Collecting pyparsing!=3.0.5,>=2.0.2 + Downloading pyparsing-3.0.9-py3-none-any.whl (98 kB) + |████████████████████████████████| 98 kB 6.0 MB/s +Collecting charset-normalizer<3,>=2 + Downloading charset_normalizer-2.1.1-py3-none-any.whl (39 kB) +Collecting idna<4,>=2.5 + Downloading idna-3.4-py3-none-any.whl (61 kB) + |████████████████████████████████| 61 kB 206 kB/s +Collecting certifi>=2017.4.17 + Downloading certifi-2022.9.24-py3-none-any.whl (161 kB) + |████████████████████████████████| 161 kB 9.3 MB/s +Collecting urllib3<1.27,>=1.21.1 + Downloading urllib3-1.26.12-py2.py3-none-any.whl (140 kB) + |████████████████████████████████| 140 kB 8.0 MB/s +Installing collected packages: zipp, urllib3, pytz, pyparsing, MarkupSafe, idna, charset-normalizer, certifi, sphinxcontrib-serializinghtml, sphinxcontrib-qthelp, sphinxcontrib-jsmath, sphinxcontrib-htmlhelp, sphinxcontrib-devhelp, sphinxcontrib-applehelp, snowballstemmer, requests, Pygments, packaging, Jinja2, importlib-metadata, imagesize, docutils, babel, alabaster, sphinx, shapely, markdown, affine + WARNING: The script normalizer is installed in '/home/usuario/.local/bin' which is not on PATH. + Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. + WARNING: The script pygmentize is installed in '/home/usuario/.local/bin' which is not on PATH. + Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. + WARNING: The script docutils is installed in '/home/usuario/.local/bin' which is not on PATH. + Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. + WARNING: The script pybabel is installed in '/home/usuario/.local/bin' which is not on PATH. + Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. + WARNING: The scripts sphinx-apidoc, sphinx-autogen, sphinx-build and sphinx-quickstart are installed in '/home/usuario/.local/bin' which is not on PATH. + Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. + WARNING: The script markdown_py is installed in '/home/usuario/.local/bin' which is not on PATH. + Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. +Successfully installed Jinja2-3.1.2 MarkupSafe-2.1.1 Pygments-2.13.0 affine-2.3.1 alabaster-0.7.12 babel-2.10.3 certifi-2022.9.24 charset-normalizer-2.1.1 docutils-0.19 idna-3.4 imagesize-1.4.1 importlib-metadata-5.0.0 markdown-3.4.1 packaging-21.3 pyparsing-3.0.9 pytz-2022.4 requests-2.28.1 shapely-1.8.5 snowballstemmer-2.2.0 sphinx-5.2.3 sphinxcontrib-applehelp-1.0.2 sphinxcontrib-devhelp-1.0.2 sphinxcontrib-htmlhelp-2.0.0 sphinxcontrib-jsmath-1.0.1 sphinxcontrib-qthelp-1.0.3 sphinxcontrib-serializinghtml-1.1.5 urllib3-1.26.12 zipp-3.9.0 +usuario@Soporte-3:~/boxes$ ls +boxes documentation LICENSE.txt po scripts +CODE_OF_CONDUCT.md examples locale README.rst setup.py +CONTRIBUTING.rst inkex MANIFEST.in requirements.txt static +usuario@Soporte-3:~/boxes$ ls scripts +boxes boxes2inkscape boxes2pot boxes_example.ipynb boxesserver Dockerfile gen_thumbnails.sh +usuario@Soporte-3:~/boxes$ python3 scripts/boxesserver +BoxesServer serving on host:port :8000... +127.0.0.1 - - [13/Oct/2022 10:53:34] "GET / HTTP/1.1" 200 32493 +127.0.0.1 - - [13/Oct/2022 10:53:34] "GET /static/self.css HTTP/1.1" 200 2357 +127.0.0.1 - - [13/Oct/2022 10:53:34] "GET /static/fonts.css HTTP/1.1" 200 1592 +127.0.0.1 - - [13/Oct/2022 10:53:35] "GET /static/normalize.css HTTP/1.1" 200 4693 +127.0.0.1 - - [13/Oct/2022 10:53:35] "GET /static/boxes-logo.svg HTTP/1.1" 200 12702 +127.0.0.1 - - [13/Oct/2022 10:53:35] "GET /static/nothing.png HTTP/1.1" 200 89 +127.0.0.1 - - [13/Oct/2022 10:53:35] "GET /static/fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc6SjT.woff2 HTTP/1.1" 200 42432 +127.0.0.1 - - [13/Oct/2022 10:53:35] "GET /static/nothing.png HTTP/1.1" 200 89 +127.0.0.1 - - [13/Oct/2022 10:53:35] "GET /static/favicon.ico HTTP/1.1" 200 16958 +127.0.0.1 - - [13/Oct/2022 10:53:36] "GET /static/samples/TrafficLight-thumb.jpg HTTP/1.1" 200 4100 +127.0.0.1 - - [13/Oct/2022 10:53:37] "GET /static/samples/BurnTest-thumb.jpg HTTP/1.1" 200 3422 +127.0.0.1 - - [13/Oct/2022 10:53:37] "GET /static/samples/ HTTP/1.1" 404 9 +127.0.0.1 - - [13/Oct/2022 10:53:38] "GET /static/samples/UniversalBox-thumb.jpg HTTP/1.1" 200 5133 +127.0.0.1 - - [13/Oct/2022 10:53:38] "GET /static/samples/RoundedBox-thumb.jpg HTTP/1.1" 200 2803 +127.0.0.1 - - [13/Oct/2022 10:53:38] "GET /static/samples/TypeTray-thumb.jpg HTTP/1.1" 200 3553 +127.0.0.1 - - [13/Oct/2022 10:53:38] "GET /static/samples/DisplayShelf-thumb.jpg HTTP/1.1" 200 4419 +127.0.0.1 - - [13/Oct/2022 10:53:38] "GET /static/samples/WallTypeTray-thumb.jpg HTTP/1.1" 200 4143 +127.0.0.1 - - [13/Oct/2022 10:53:41] "GET /static/samples/ HTTP/1.1" 404 9 +127.0.0.1 - - [13/Oct/2022 10:53:42] "GET /static/samples/ HTTP/1.1" 404 9 +127.0.0.1 - - [13/Oct/2022 10:53:42] "GET /static/samples/Atreus21-thumb.jpg HTTP/1.1" 200 6502 +127.0.0.1 - - [13/Oct/2022 10:53:42] "GET /static/samples/BottleTag-thumb.jpg HTTP/1.1" 200 6969 +127.0.0.1 - - [13/Oct/2022 10:53:42] "GET /static/samples/CanStorage-thumb.jpg HTTP/1.1" 200 5444 +127.0.0.1 - - [13/Oct/2022 10:53:43] "GET /static/samples/CoinDisplay-thumb.jpg HTTP/1.1" 200 4469 +127.0.0.1 - - [13/Oct/2022 10:53:43] "GET /static/samples/CoffeeCapsuleHolder-thumb.jpg HTTP/1.1" 200 4235 +127.0.0.1 - - [13/Oct/2022 10:53:47] "GET /static/samples/BottleStack-thumb.jpg HTTP/1.1" 200 89 +127.0.0.1 - - [13/Oct/2022 10:53:50] "GET /static/samples/Arcade-thumb.jpg HTTP/1.1" 200 89 +127.0.0.1 - - [13/Oct/2022 10:53:53] "GET /BottleTag?language=en HTTP/1.1" 200 7965 +127.0.0.1 - - [13/Oct/2022 10:53:53] "GET /static/self.css HTTP/1.1" 200 2357 +127.0.0.1 - - [13/Oct/2022 10:53:54] "GET /static/fonts.css HTTP/1.1" 200 1592 +127.0.0.1 - - [13/Oct/2022 10:53:54] "GET /static/normalize.css HTTP/1.1" 200 4693 +127.0.0.1 - - [13/Oct/2022 10:53:54] "GET /static/boxes-logo.svg HTTP/1.1" 200 12702 +127.0.0.1 - - [13/Oct/2022 10:53:54] "GET /static/samples/BottleTag.jpg HTTP/1.1" 200 82889 +127.0.0.1 - - [13/Oct/2022 10:53:54] "GET /static/fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc6SjT.woff2 HTTP/1.1" 200 42432 +127.0.0.1 - - [13/Oct/2022 10:53:54] "GET /static/fonts/luckiestguy/v8/_gP_1RrxsjcxVyin9l9n_j2hTd52.woff2 HTTP/1.1" 200 17024 +127.0.0.1 - - [13/Oct/2022 10:53:57] "GET /BottleTag?width=72&height=98&min_diameter=24&max_diameter=50&radius=15&segment_width=3&thickness=3.0&format=svg&tabs=0.0&debug=0&labels=0&labels=1&reference=100&inner_corners=loop&burn=0.1&render=1 HTTP/1.1" 200 6346 +127.0.0.1 - - [13/Oct/2022 10:53:57] "GET /favicon.ico HTTP/1.1" 200 32493 +127.0.0.1 - - [13/Oct/2022 10:55:22] "GET /BottleTag?width=70&height=90&min_diameter=44&max_diameter=60&radius=8&segment_width=3&thickness=3.0&format=svg&tabs=0.0&debug=0&labels=0&labels=1&reference=100&inner_corners=loop&burn=0.1&render=1 HTTP/1.1" 200 7988 +127.0.0.1 - - [13/Oct/2022 10:55:22] "GET /favicon.ico HTTP/1.1" 200 32493 +127.0.0.1 - - [13/Oct/2022 12:04:45] "GET / HTTP/1.1" 200 32493 +127.0.0.1 - - [13/Oct/2022 12:04:45] "GET /static/self.css HTTP/1.1" 200 2357 +127.0.0.1 - - [13/Oct/2022 12:04:45] "GET /static/fonts.css HTTP/1.1" 200 1592 +127.0.0.1 - - [13/Oct/2022 12:04:45] "GET /static/normalize.css HTTP/1.1" 200 4693 +127.0.0.1 - - [13/Oct/2022 12:04:45] "GET /static/boxes-logo.svg HTTP/1.1" 200 12702 +127.0.0.1 - - [13/Oct/2022 12:04:45] "GET /static/nothing.png HTTP/1.1" 200 89 +127.0.0.1 - - [13/Oct/2022 12:04:46] "GET /static/fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc6SjT.woff2 HTTP/1.1" 200 42432 +127.0.0.1 - - [13/Oct/2022 12:04:46] "GET /static/samples/RoundedBox-thumb.jpg HTTP/1.1" 200 2803 +127.0.0.1 - - [13/Oct/2022 12:04:53] "GET /static/samples/RoundedBox-thumb.jpg HTTP/1.1" 200 2803 +127.0.0.1 - - [13/Oct/2022 12:04:54] "GET /static/samples/UniversalBox-thumb.jpg HTTP/1.1" 200 5133 +127.0.0.1 - - [13/Oct/2022 12:04:54] "GET /static/samples/ HTTP/1.1" 404 9 +127.0.0.1 - - [13/Oct/2022 12:04:54] "GET /static/samples/BurnTest-thumb.jpg HTTP/1.1" 200 3422 +127.0.0.1 - - [13/Oct/2022 12:04:55] "GET /static/samples/TrafficLight-thumb.jpg HTTP/1.1" 200 4100 +127.0.0.1 - - [13/Oct/2022 12:04:55] "GET /static/samples/ HTTP/1.1" 404 9 +127.0.0.1 - - [13/Oct/2022 12:04:55] "GET /static/samples/TypeTray-thumb.jpg HTTP/1.1" 200 3553 +127.0.0.1 - - [13/Oct/2022 12:04:57] "GET /static/samples/ HTTP/1.1" 404 9 +127.0.0.1 - - [13/Oct/2022 12:07:01] "GET /static/samples/RoundedBox-thumb.jpg HTTP/1.1" 200 2803 +127.0.0.1 - - [13/Oct/2022 12:07:01] "GET /static/samples/UniversalBox-thumb.jpg HTTP/1.1" 200 5133 +127.0.0.1 - - [13/Oct/2022 12:07:03] "GET /static/samples/TypeTray-thumb.jpg HTTP/1.1" 200 3553 +127.0.0.1 - - [13/Oct/2022 12:07:03] "GET /static/samples/TrafficLight-thumb.jpg HTTP/1.1" 200 4100 +127.0.0.1 - - [13/Oct/2022 12:07:05] "GET /static/samples/ HTTP/1.1" 404 9 +^CBoxesServer stops. diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/macos.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/macos.rst new file mode 100644 index 0000000..f385d15 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/macos.rst @@ -0,0 +1,124 @@ +macOS +===== + +It is recommended to use Homebrew to install the dependencies for Boxes.py. +See `brew.sh `__ on how to install Homebrew. + +General +------- + +1. Install Python 3 and other dependencies: + + .. code:: + + brew install python3 git + + Optional: + + .. code:: + + brew install pstoedit + + +2. Install cairio: + + .. code:: + + brew install pkg-config + + +3. Install required Python modules: + + .. code:: + + pip3 install Markdown affine shapely + +4. Download Boxes.py via Git: + + .. code:: + + git clone https://github.com/florianfesti/boxes.git + +5. Run Boxes.py: + + Local web server on port 8000: + + .. code:: + + ./scripts/boxesserver + + Command line variant (CLI): + + .. code:: + + ./scripts/boxes + + +System-wide with Inkscape extension +----------------------------------- + +To install Boxes.py system-wide with the Inkscape extension, following steps +are required: + +1. Install Inkscape with Homebrew Cask + (requires `XQuartz `__): + + .. code:: + + brew install inkscape + +2. From the root directory of the repository, run: + + .. code:: + + ./setup.py install + +3. Now :code:`boxes` and :code:`boxesserver` can be executed like other commands + and the Inkscape extension should be available. + + +Troubleshooting +............... + +When using the Inkscape extension something like the following error +might occur: + +:: + + Traceback (most recent call last): + File "/Users/martin/.config/inkscape/extensions/boxes", line 107, in + main() + File "/Users/martin/.config/inkscape/extensions/boxes", line 47, in main + run_generator(name, sys.argv[2:]) + File "/Users/martin/.config/inkscape/extensions/boxes", line 73, in run_generator + box.close() + File "/usr/local/lib/python3.7/site-packages/boxes-0.1-py3.7.egg/boxes/__init__.py", line 594, in close + svgutil.svgMerge(self.output, self.inkscapefile, out) + File "/usr/local/lib/python3.7/site-packages/boxes-0.1-py3.7.egg/boxes/svgutil.py", line 144, in svgMerge + from lxml import etree as et + ImportError: dlopen(/Applications/Inkscape.app/Contents/Resources/lib/python2.7/site-packages/lxml/etree.so, 2): Symbol not found: _PyBaseString_Type + Referenced from: /Applications/Inkscape.app/Contents/Resources/lib/python2.7/site-packages/lxml/etree.so + Expected in: flat namespace + +This is because Inkscape on macOS ships its own version of Python 2.7 where +:code:`lxml` and other dependencies are missing. + +A workaround is to edit the file at +:code:`/Applications/Inkscape.app/Contents/Resources/bin/inkscape`. +At line 79 there should be following code: + +.. code:: + + export PYTHONPATH="$TOP/lib/python$PYTHON_VERS/site-packages/" + +which needs to be changed to + +.. code:: + + #export PYTHONPATH="$TOP/lib/python$PYTHON_VERS/site-packages/" + +This forces Inkscape to use the Python version installed by Homebrew which +has all the necessary dependencies installed. + +Note: This might break other extensions. In this case simply change the line +back and restart Inkscape. diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/win11-wsl-boxesserver-localhost.png b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/win11-wsl-boxesserver-localhost.png new file mode 100644 index 0000000..83c00a6 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/win11-wsl-boxesserver-localhost.png differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows.rst new file mode 100644 index 0000000..ce6a3ed --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows.rst @@ -0,0 +1,93 @@ +Windows +======= + +Getting the Inkscape plugins to run will likely need manual +installation (see above). Note that Inkscape may come with its own +Python. If you run into trouble or have better installation +instructions please open a ticket on GitHub. + +Native +------ + +Following steps are known to work under Windows 10/11 (64-bit): + +1. Go to https://www.python.org/downloads/windows/ + and download the current stable "Windows x86-64 executable installer" + for Python 3. + When this guide was written, Python 3.7 was the current version. + + .. figure:: windows_browser_download_python.png + :scale: 50% + :alt: Screenshot of python.org with download of Python 3.7 (64-bit) + :align: center + +2. Install Python 3 and make sure to check "Add Python 3.X to PATH" + while doing so + + .. figure:: windows_install_python_path.png + :scale: 50% + :alt: Screenshot of Python 3.7 (64-bit) installer with PATH checked + :align: center + +3. Run the command :code:`pip install Markdown affine shapely qrcode` + (Note: If the command pip is not found, you probably forgot to add the + Python installation to the PATH environment variable in step 2) + +4. Download Boxes.py as ZIP archive from GitHub + + .. figure:: windows_browser_download_boxespy.png + :scale: 50% + :alt: Screenshot of download from Boxes.py project on GitHub + :align: center + +5. Extract the ZIP archive + (e.g. via the built-in Windows feature or other tools like 7-Zip) + + .. figure:: windows_boxespy_zip_extract.png + :scale: 50% + :alt: Screenshot of Windows tools to extract the ZIP archive + :align: center + +6. Change into the folder for Boxes.py, + e.g. with the command :code:`cd \\Users\\[USERNAME]\\Downloads\\boxes-master` +7. Run the development server with the command + :code:`python scripts\\boxesserver` + Note: You likely will be notified by your firewall that it blocked network + access. If you want to use boxesserver you need to allow connections. + + .. figure:: windows_cmd_python_boxesserver_firewall.png + :scale: 50% + :alt: Screenshot of command for running boxesserver and firewall notice + :align: center + +8. Open the address http://localhost:8000/ in your browser and have fun :) + + .. figure:: windows_browser_boxespy.png + :scale: 50% + :alt: Screenshot of a browser window running Boxes.py locally + :align: center + + +Additionally the command line version of Boxes.py can be used with +the command :code:`python scripts\\boxes`. + +Windows Subsystem for Linux +--------------------------- + +Another way of installing Boxes.py on Windows is to use the Windows Subsystem +for Linux (WSL). This requires newer versions of Windows 10. Once it is +installed (e.g. via the Ubuntu App from the Microsoft Store), the installation +is identical to the installation on Linux systems. + +Once wsl is installed, run it and enter the following commands: + +- :code:`cd ~` +- :code:`git clone https://github.com/florianfesti/boxes.git` +- :code:`cd ~/boxes` +- :code:`python3 -m pip install -r ~/boxes/requirements.txt` +- :code:`python3 ~/boxes/scripts/boxesserver` + +.. figure:: win11-wsl-boxesserver-localhost.png + :scale: 50% + :alt: Screenshot of a browser window running Boxes.py locally on WSL + :align: center diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_boxespy_zip_extract.png b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_boxespy_zip_extract.png new file mode 100644 index 0000000..2c45312 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_boxespy_zip_extract.png differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_browser_boxespy.png b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_browser_boxespy.png new file mode 100644 index 0000000..5adeed8 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_browser_boxespy.png differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_browser_download_boxespy.png b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_browser_download_boxespy.png new file mode 100644 index 0000000..0fb9578 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_browser_download_boxespy.png differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_browser_download_python.png b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_browser_download_python.png new file mode 100644 index 0000000..06b14a1 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_browser_download_python.png differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_cmd_python_boxesserver_firewall.png b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_cmd_python_boxesserver_firewall.png new file mode 100644 index 0000000..b3f8e76 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_cmd_python_boxesserver_firewall.png differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_install_python_path.png b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_install_python_path.png new file mode 100644 index 0000000..014257a Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/install/windows_install_python_path.png differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/make.bat b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/make.bat new file mode 100644 index 0000000..b12580f --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=../build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/modules.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/modules.rst new file mode 100644 index 0000000..8a714cb --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/modules.rst @@ -0,0 +1,9 @@ +:orphan: + +boxes +===== + +.. toctree:: + :maxdepth: 4 + + boxes diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/overcuts.svg b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/overcuts.svg new file mode 100644 index 0000000..39e6a8e --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/overcuts.svg @@ -0,0 +1,62 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/static b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/static new file mode 120000 index 0000000..1bf4a07 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/static @@ -0,0 +1 @@ +../../static/ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/usermanual.rst b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/usermanual.rst new file mode 100644 index 0000000..4428a7c --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/documentation/src/usermanual.rst @@ -0,0 +1,253 @@ +============== +Using Boxes.py +============== + +.. toctree:: + :maxdepth: 2 + +Boxes.py is made of a library that is not visible to the user and +multiple generators -- each having its own set of parameters and +creating a drawing for it own type of object. These generators are +divided up into different groups to make it easier to find them: + +* Boxes +* Boxes with flex +* Trays and Drawer Inserts +* Shelves +* Parts and Samples +* Misc +* Unstable + +The parameters for each generators also come in groups. + +Units of measurements +--------------------- + +In general all measurements are in Millimeters (mm). There is no +option to change the units of measurement and there is no plan to add +such a option. + +A second way to define lengths is as multiple of the material +thickness which is one of the standard parameters described +below. This allows features to retain their proportions even if some +parts depend on the material thickness. + +The description texts should state the unit of each argument - +please open a ticket if the units are missing somewhere. + +.. _default-args: + +Default arguments +----------------- +In the web interface this is the bottom group right before the +``Render`` button. These are basically all technical settings that +have little to do with the object being rendered but more with the +material used and the way the drawing and the material is processed. + +The settings are + +thickness +......... + +The thickness of the material used. This value is used at many places +to define the sizes of features like finger joints, hinges, ... It is +very important to get the value right - especially if there are +fingers that need to fit into some holes. Be aware that many materials +may differ from their nominal value. You should **always measure the +thickness** for every sheet unless you have a very reliable supply +that is known to stick very closely to specifications. For (ply) wood +even a 100th of a millimeter makes a notable difference in how stiff +the fit is. Harder more brittle materials may be even more picky. + +burn +.... + +The burn correction aka kerf is the distance the laser has to keep +from the edge of the parts. If the laser would cut right on the edge +it would cut away the outside perimeter of the part. So the burn value is +basically the radius of the laser - or half the width of the laser cut. + +The value of the burn parameter depends on your laser cutter, the +material cut and the thickness of the material. In addition it depends +on whether you want the parts to be over or under sized. Materials +that are spongy like wood can be cut oversized (larger burn value) so +they can be press fitted with some force and may be assembled without +glue. Brittle materials (like Acrylic) need to be cut undersized to +leave a gap for the glue. + +**Note:** The way the burn param works is a bit counter intuitive. Bigger +burn values make a tighter fit. Smaller values make a looser fit. + +Small changes in the burn param can make a notable difference. Typical +steps for adjustment are 0.01 or even 0.005mm to choose between +different amounts of force needed to press plywood together. + +To find the right burn value cut out a rectangle and then measure how +much smaller it is than its nominal size. The burn value should be +around half of the difference. To test the fit for several values at +once you can use the **BurnTest** generator in the "Parts and Samples" section. + +format +...... + +Boxes.py is able to create multiple formats. For most of them it +requires ``pstoedit``. Without ``pstoedit`` only ``SVG`` +and ``postscript`` (ps) is supported. Otherwise you can also +select + +* dxf +* gcode +* pdf +* plt + +Other formats supported by ``pstoedit`` can be added easily. Please +open a ticket on GitHub if you need one. + +tabs +.... + +Tabs are small bridges between the parts and surrounding material that +keep the part from falling out. In theory their width should be +affected by the burn parameter. But it is more practical to have both +independent so you can tune them separately. Most parts and generators +support this features but there may be some that don't. + +For plywood values of 0.2 to 0.3mm still allow getting the parts out +by hand (Depending on you laser cutter and the exact material). With +little more you will need a knife to cut them loose. + +inner_corners +............. + +How to handle inner corners. Inner corners are an issue as a round +tool like a laser or mill cannot create sharp inner corners. There are +different options: + +* ``loop`` create a loop that fills the corner +* ``corner`` just a simple sharp corner in the path that will leave a + radius untouched. +* ``backarc`` naive implementation with inverted arcs connection the + straight lines. + +See also :doc:`burn correction details ` + +debug +..... + +Most regular users won't need this option. + +It adds some construction lines that are helpful for +developing new generators. Only few pieces actually support the +parameter. The most notable being finger holes that show the border of +the piece they belong to. This helps checking whether the finger holes +are placed correctly. + +reference +......... + +Converting vector graphics is error prone. Many formats have very +weird ideas how their internal units translates to real world +dimensions. If reference is set to non zero Boxes.py renders a rectangle of +the given length. It can be used to check if the drawing is still at +the right scale or may give clues on how to scale it back to the right +proportions. + +Common Parameters and Types +--------------------------- + +Section parameters +.................. + +Some generators support an arbitrary number of sections. This can be used for rows or columns of compartments, staggered heights or otherwise dividing some length in multiple sub sections. The standard parameter making use of this are ``sx``, ``sy`` and ``sh`` (instead of ``x``, ``y`` and ``h``). + +Most generators will add walls between the comparments, so the total size might be larger depending on the number of compartments (and additional walls). + +The sizes of the sections are divided by a colon (``:``) e.g. ``30:25.5:70``. Instead of repeating the same value they can be replaced by ``value*numberofsections`` e.g. ``50*3`` meaning the same as ``50:50:50``. To equally divide a length into several sections ``overallwidth/numberofsections`` can be used - e.g. ``120/4`` being the same as ``30:30:30:30``. All these formats can be freely mixed. + +mounting_holes +.................. +Some generators provide the option to create pear shaped mounting holes. To generate the right size holes, the shaft and the head diameter of the mounting screw must be configured. The format is "shaft:head", both diameters given in mm (e.g ``3.5:6.5``). If only the shaft diameter is given (e.g. ``3.5``), a round mounting hole is generated. Setting the mounting hole diameter parameter to ``0`` disables the creation of mounting holes. + +outside +....... + +Most measurements are internal sizes. If a generator offers this parameter it will re-calculate the inner sizes to fit walls and outside features within the given dimensions. This can be a bit surprising for edge types that have protrusions like hinge eyes, handles, feet, etc as those are typically also taken into account. If the dimensions are not sufficient to accommodate these features the box may not work properly. Most generators do not have checks for such issues (like negative height) and it is left in the responsibility of the user to check if the result still is sane. + +For generators offering multiple compartments this will also fit-in the inner walls. It will sum up all sections then subtract the space needed for the walls and then scale all compartments so they will fill the remaining space. + + +Edge Type parameters +-------------------- + +All but the simplest edge types have a number of settings controlling +how exactly they should look. Generators are encouraged to offer these +settings to the user. In the web interface they are folded up. In the +command line interface they are grouped together. Users should be +aware that not all settings are practical to change. For now Boxes.py +does not allow hiding some settings. + +Finger Joint Settings +..................... + +.. glossary:: + + finger + width of the fingers in multiples of the thickness + + space + width of the spaces between fingers in multiples of the thickness + + surroundingspaces + amount of space before the first and after the last finger. This is in multiples of regular space between fingers. The actual space is larger when needed but can be smaller for very short edges. + + style + how finger joints should look like. There may be more styles to choose from in the future. Note that snap fingers will only be drawn for fingers of width 1.9 and above. + + extra_length + Make the outset part of the finger joint longer to allow grinding off burn marks. Note that this may not be great for non 90° joints where the corner is butted against the opposing cutout. + + bottom_lip + Generate pieces to be glued on the inside of finger hole edges. This allows stacking boxes on top of each other. Note that finger hole edges that are used elsewhere may not have use of these pieces and you should probably just delete them before cutting. + +Stackable Edge Settings +....................... + +For boxes to actually stack they need to be the same width and depth and ``angle``, ``width`` and ``height`` of the feet need to be the same. + +.. glossary:: + + angle + inside angle of the feet. + + height + height of the feet + + holedistance + distance from finger holes to bottom edge. May be reduced to save height by sacrificing stability of the connection to the bottom of the box. + + width + width of the feet + + bottom_stabilizers + generate pieces to be glued inside of the bottom edges to stack more securely. Use a value a bit less than ``height`` ( + ``holedistance`` ) to leave some of the feet sticking out. + +Colors +------ +The generated files uses the following color conventions: + +.. glossary:: + + Black + The outer edges of a part + + Blue + Inner edges of a part + + Red + Comments or help lines that are not meant to be cut or etched + + Green + Etchings + +Normally you will cut things in the order: Green, Blue, Black. If other +colors are present, the meaning should hopefully be obvious. diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/box2.svg b/extensions/fablabchemnitz/boxes.py/boxes/examples/box2.svg new file mode 100644 index 0000000..f667722 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/box2.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/box3.svg b/extensions/fablabchemnitz/boxes.py/boxes/examples/box3.svg new file mode 100644 index 0000000..386cef5 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/box3.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/castle.svg b/extensions/fablabchemnitz/boxes.py/boxes/examples/castle.svg new file mode 100644 index 0000000..be58646 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/castle.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/drillbox.svg b/extensions/fablabchemnitz/boxes.py/boxes/examples/drillbox.svg new file mode 100644 index 0000000..4fb65b4 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/drillbox.svg @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/flexbox.svg b/extensions/fablabchemnitz/boxes.py/boxes/examples/flexbox.svg new file mode 100644 index 0000000..467a1d2 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/flexbox.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/flexbox2.svg b/extensions/fablabchemnitz/boxes.py/boxes/examples/flexbox2.svg new file mode 100644 index 0000000..6c01d2a --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/flexbox2.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/flexbox3.svg b/extensions/fablabchemnitz/boxes.py/boxes/examples/flexbox3.svg new file mode 100644 index 0000000..e277c8b --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/flexbox3.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/folder.svg b/extensions/fablabchemnitz/boxes.py/boxes/examples/folder.svg new file mode 100644 index 0000000..419b438 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/folder.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/generate.sh b/extensions/fablabchemnitz/boxes.py/boxes/examples/generate.sh new file mode 100755 index 0000000..73e7110 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/generate.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +BOXES=../scripts/boxes + +set -x +$BOXES closedbox --x=50 --y=50 --h=70 --output=closedbox.svg +$BOXES hingebox --x=50 --y=50 --h=70 --output=hingebox.svg +$BOXES castle --output=castle.svg +$BOXES drillbox --output=drillbox.svg +$BOXES flexbox --x=70 --y=100 --h=50 --radius=20 --output=flexbox.svg +$BOXES flexbox2 --x=70 --y=100 --h=50 --radius=20 --output=flexbox2.svg +$BOXES flexbox3 --x=70 --y=100 --z=50 --h=8 --radius=30 --output=flexbox3.svg +$BOXES folder --x=165 --y=240 --h=20 --r=10 --output=folder.svg +#$BOXES lamp --x=50 --y=50 --r=10 --output=lamp.svg +$BOXES magazinefile --output=magazinefile.svg +#$BOXES printer --output=printer.svg +#$BOXES Silverwaree --output=silverwarebox.svg +#$BOXES traylayout --x=4 --y=4 --output=traylayout.txt +$BOXES traylayout2 --input=traylayout.txt --h=50 --hi=40 --output=traylayout.svg +$BOXES trayinsert --sx=70:100:70 --sy=100*3 --h=50 --output=trayinsert.svg +$BOXES typetray --sx=70:100:70 --sy=100*3 --h=60 --hi=50 --output=typetray.svg diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/lamp.svg b/extensions/fablabchemnitz/boxes.py/boxes/examples/lamp.svg new file mode 100644 index 0000000..061e2eb --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/lamp.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/magazinefile.svg b/extensions/fablabchemnitz/boxes.py/boxes/examples/magazinefile.svg new file mode 100644 index 0000000..0930a13 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/magazinefile.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/silverwarebox.svg b/extensions/fablabchemnitz/boxes.py/boxes/examples/silverwarebox.svg new file mode 100644 index 0000000..26cf5a0 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/silverwarebox.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/trayinsert.svg b/extensions/fablabchemnitz/boxes.py/boxes/examples/trayinsert.svg new file mode 100644 index 0000000..d861588 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/trayinsert.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/traylayout.svg b/extensions/fablabchemnitz/boxes.py/boxes/examples/traylayout.svg new file mode 100644 index 0000000..4e24fcc --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/traylayout.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/traylayout.txt b/extensions/fablabchemnitz/boxes.py/boxes/examples/traylayout.txt new file mode 100644 index 0000000..bd05b85 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/traylayout.txt @@ -0,0 +1,13 @@ + ,> 40.0mm + | ,> 50.0mm + | | ,> 50.0mm + | | | ,> 40.0mm ++-+-+-+-+ +| | | 60.0mm ++-+-+-+-+ +| | | | 50.0mm ++ +-+-+ + +| | | | 50.0mm ++-+-+-+-+ +| | | 40.0mm ++ + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/examples/typetray.svg b/extensions/fablabchemnitz/boxes.py/boxes/examples/typetray.svg new file mode 100644 index 0000000..2c01f77 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/examples/typetray.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/boxes.py/boxes/locale/de/LC_MESSAGES/boxes.py.mo b/extensions/fablabchemnitz/boxes.py/boxes/locale/de/LC_MESSAGES/boxes.py.mo new file mode 100644 index 0000000..e409ac4 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/locale/de/LC_MESSAGES/boxes.py.mo differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/locale/en/LC_MESSAGES/boxes.py.mo b/extensions/fablabchemnitz/boxes.py/boxes/locale/en/LC_MESSAGES/boxes.py.mo index c8824bf..78e21f2 100644 Binary files a/extensions/fablabchemnitz/boxes.py/boxes/locale/en/LC_MESSAGES/boxes.py.mo and b/extensions/fablabchemnitz/boxes.py/boxes/locale/en/LC_MESSAGES/boxes.py.mo differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/locale/fr/LC_MESSAGES/boxes.py.mo b/extensions/fablabchemnitz/boxes.py/boxes/locale/fr/LC_MESSAGES/boxes.py.mo index daf8b80..64bd4f9 100644 Binary files a/extensions/fablabchemnitz/boxes.py/boxes/locale/fr/LC_MESSAGES/boxes.py.mo and b/extensions/fablabchemnitz/boxes.py/boxes/locale/fr/LC_MESSAGES/boxes.py.mo differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/locale/zh_CN/LC_MESSAGES/boxes.py.mo b/extensions/fablabchemnitz/boxes.py/boxes/locale/zh_CN/LC_MESSAGES/boxes.py.mo index 95c5e35..636db54 100644 Binary files a/extensions/fablabchemnitz/boxes.py/boxes/locale/zh_CN/LC_MESSAGES/boxes.py.mo and b/extensions/fablabchemnitz/boxes.py/boxes/locale/zh_CN/LC_MESSAGES/boxes.py.mo differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/po/boxes.py.pot b/extensions/fablabchemnitz/boxes.py/boxes/po/boxes.py.pot new file mode 100644 index 0000000..307f7af --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/po/boxes.py.pot @@ -0,0 +1,8668 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-07-16 21:47+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. e edge description +#: boxes/edges.py +msgid "Straight Edge" +msgstr "" + +#. e edge description +#: boxes/edges.py +msgid "e Straight Edge" +msgstr "" + +#. E edge description +#: boxes/edges.py +msgid "Straight Edge (outset by thickness)" +msgstr "" + +#. E edge description +#: boxes/edges.py +msgid "E Straight Edge (outset by thickness)" +msgstr "" + +#. G edge description +#: boxes/edges.py +msgid "Edge with pear shaped mounting holes" +msgstr "" + +#. G edge description +#: boxes/edges.py +msgid "G Edge with pear shaped mounting holes" +msgstr "" + +#. z edge description +#: boxes/edges.py +msgid "Edge with grooves" +msgstr "" + +#. z edge description +#: boxes/edges.py +msgid "z Edge with grooves" +msgstr "" + +#. Z edge description +#: boxes/edges.py +msgid "Edge with grooves (opposing side)" +msgstr "" + +#. Z edge description +#: boxes/edges.py +msgid "Z Edge with grooves (opposing side)" +msgstr "" + +#. g edge description +#: boxes/edges.py +msgid "Corrugated edge useful as an gipping area" +msgstr "" + +#. g edge description +#: boxes/edges.py +msgid "g Corrugated edge useful as an gipping area" +msgstr "" + +#. f edge description +#: boxes/edges.py +msgid "Finger Joint" +msgstr "" + +#. f edge description +#: boxes/edges.py +msgid "f Finger Joint" +msgstr "" + +#. F edge description +#: boxes/edges.py +msgid "Finger Joint (opposing side)" +msgstr "" + +#. F edge description +#: boxes/edges.py +msgid "F Finger Joint (opposing side)" +msgstr "" + +#. h edge description +#: boxes/edges.py +msgid "Edge (parallel Finger Joint Holes)" +msgstr "" + +#. h edge description +#: boxes/edges.py +msgid "h Edge (parallel Finger Joint Holes)" +msgstr "" + +#. | edge description +#: boxes/edges.py +msgid "Edge (orthogonal Finger Joint Holes)" +msgstr "" + +#. | edge description +#: boxes/edges.py +msgid "| Edge (orthogonal Finger Joint Holes)" +msgstr "" + +#. s edge description +#: boxes/edges.py +msgid "Stackable (bottom, finger joint holes)" +msgstr "" + +#. s edge description +#: boxes/edges.py +msgid "s Stackable (bottom, finger joint holes)" +msgstr "" + +#. S edge description +#: boxes/edges.py +msgid "Stackable (top)" +msgstr "" + +#. S edge description +#: boxes/edges.py +msgid "S Stackable (top)" +msgstr "" + +#. š edge description +#: boxes/edges.py +msgid "Stackable feet (bottom)" +msgstr "" + +#. š edge description +#: boxes/edges.py +msgid "š Stackable feet (bottom)" +msgstr "" + +#. Š edge description +#: boxes/edges.py +msgid "Stackable edge with finger holes (top)" +msgstr "" + +#. Š edge description +#: boxes/edges.py +msgid "Š Stackable edge with finger holes (top)" +msgstr "" + +#. i edge description +#: boxes/edges.py +msgid "Straight edge with hinge eye" +msgstr "" + +#. i edge description +#: boxes/edges.py +msgid "i Straight edge with hinge eye" +msgstr "" + +#. I edge description +#: boxes/edges.py +msgid "Edge with hinge pin" +msgstr "" + +#. I edge description +#: boxes/edges.py +msgid "I Edge with hinge pin" +msgstr "" + +#. o edge description +#: boxes/edges.py +msgid "Edge with chest hinge" +msgstr "" + +#. o edge description +#: boxes/edges.py +msgid "o Edge with chest hinge" +msgstr "" + +#. p edge description +#: boxes/edges.py +msgid "p Edge with chest hinge" +msgstr "" + +#. q edge description +#: boxes/edges.py +msgid "Edge with pins for an chest hinge" +msgstr "" + +#. q edge description +#: boxes/edges.py +msgid "q Edge with pins for an chest hinge" +msgstr "" + +#. Q edge description +#: boxes/edges.py +msgid "Edge opposing a chest hinge" +msgstr "" + +#. Q edge description +#: boxes/edges.py +msgid "Q Edge opposing a chest hinge" +msgstr "" + +#. u edge description +#: boxes/edges.py +msgid "Edge with cabinet hinges" +msgstr "" + +#. u edge description +#: boxes/edges.py +msgid "u Edge with cabinet hinges" +msgstr "" + +#. l edge description +#: boxes/edges.py +msgid "Edge for slide on lid (back)" +msgstr "" + +#. l edge description +#: boxes/edges.py +msgid "l Edge for slide on lid (back)" +msgstr "" + +#. L edge description +#: boxes/edges.py +msgid "Edge for slide on lid (box back)" +msgstr "" + +#. L edge description +#: boxes/edges.py +msgid "L Edge for slide on lid (box back)" +msgstr "" + +#. n edge description +#: boxes/edges.py +msgid "Edge for slide on lid (right)" +msgstr "" + +#. n edge description +#: boxes/edges.py +msgid "n Edge for slide on lid (right)" +msgstr "" + +#. m edge description +#: boxes/edges.py +msgid "Edge for slide on lid (left)" +msgstr "" + +#. m edge description +#: boxes/edges.py +msgid "m Edge for slide on lid (left)" +msgstr "" + +#. N edge description +#: boxes/edges.py +msgid "Edge for slide on lid (box right)" +msgstr "" + +#. N edge description +#: boxes/edges.py +msgid "N Edge for slide on lid (box right)" +msgstr "" + +#. M edge description +#: boxes/edges.py +msgid "Edge for slide on lid (box left)" +msgstr "" + +#. M edge description +#: boxes/edges.py +msgid "M Edge for slide on lid (box left)" +msgstr "" + +#. c edge description +#: boxes/edges.py +msgid "Click on (bottom side)" +msgstr "" + +#. c edge description +#: boxes/edges.py +msgid "c Click on (bottom side)" +msgstr "" + +#. C edge description +#: boxes/edges.py +msgid "Click on (top)" +msgstr "" + +#. C edge description +#: boxes/edges.py +msgid "C Click on (top)" +msgstr "" + +#. d edge description +#: boxes/edges.py +msgid "Dove Tail Joint" +msgstr "" + +#. d edge description +#: boxes/edges.py +msgid "d Dove Tail Joint" +msgstr "" + +#. D edge description +#: boxes/edges.py +msgid "Dove Tail Joint (opposing side)" +msgstr "" + +#. D edge description +#: boxes/edges.py +msgid "D Dove Tail Joint (opposing side)" +msgstr "" + +#. X edge description +#: boxes/edges.py +msgid "Flex cut" +msgstr "" + +#. X edge description +#: boxes/edges.py +msgid "X Flex cut" +msgstr "" + +#. R edge description +#: boxes/edges.py +msgid "Rack (and pinion) Edge" +msgstr "" + +#. R edge description +#: boxes/edges.py +msgid "R Rack (and pinion) Edge" +msgstr "" + +#. t edge description +#: boxes/edges.py +msgid "Triangle for handle" +msgstr "" + +#. t edge description +#: boxes/edges.py +msgid "t Triangle for handle" +msgstr "" + +#. T edge description +#: boxes/edges.py +msgid "T Triangle for handle" +msgstr "" + +#. y edge description +#: boxes/edges.py +msgid "Handle for e.g. a drawer" +msgstr "" + +#. y edge description +#: boxes/edges.py +msgid "y Handle for e.g. a drawer" +msgstr "" + +#. Y edge description +#: boxes/edges.py +msgid "Handle with holes for parallel finger joint" +msgstr "" + +#. Y edge description +#: boxes/edges.py +msgid "Y Handle with holes for parallel finger joint" +msgstr "" + +#. j edge description +#: boxes/edges.py +msgid "Straight edge with hinge eye (other end)" +msgstr "" + +#. j edge description +#: boxes/edges.py +msgid "j Straight edge with hinge eye (other end)" +msgstr "" + +#. J edge description +#: boxes/edges.py +msgid "Edge with hinge pin (other end)" +msgstr "" + +#. J edge description +#: boxes/edges.py +msgid "J Edge with hinge pin (other end)" +msgstr "" + +#. k edge description +#: boxes/edges.py +msgid "Straight edge with hinge eye (both ends)" +msgstr "" + +#. k edge description +#: boxes/edges.py +msgid "k Straight edge with hinge eye (both ends)" +msgstr "" + +#. K edge description +#: boxes/edges.py +msgid "Edge with hinge pin (both ends)" +msgstr "" + +#. K edge description +#: boxes/edges.py +msgid "K Edge with hinge pin (both ends)" +msgstr "" + +#. O edge description +#: boxes/edges.py +msgid "Edge with chest hinge (other end)" +msgstr "" + +#. O edge description +#: boxes/edges.py +msgid "O Edge with chest hinge (other end)" +msgstr "" + +#. P edge description +#: boxes/edges.py +msgid "P Edge with chest hinge (other end)" +msgstr "" + +#. U edge description +#: boxes/edges.py +msgid "Edge with cabinet hinges top side" +msgstr "" + +#. U edge description +#: boxes/edges.py +msgid "U Edge with cabinet hinges top side" +msgstr "" + +#. v edge description +#: boxes/edges.py +msgid "Edge with cabinet hinges for 90° lid" +msgstr "" + +#. v edge description +#: boxes/edges.py +msgid "v Edge with cabinet hinges for 90° lid" +msgstr "" + +#. V edge description +#: boxes/edges.py +msgid "Edge with cabinet hinges 90° lid" +msgstr "" + +#. V edge description +#: boxes/edges.py +msgid "V Edge with cabinet hinges 90° lid" +msgstr "" + +#. name of generator group +#: boxes/generators/__init__.py +msgid "Box" +msgstr "" + +#. title of group Box +#: boxes/generators/__init__.py +msgid "Boxes" +msgstr "" + +#. name of generator group +#: boxes/generators/__init__.py +msgid "FlexBox" +msgstr "" + +#. title of group FlexBox +#: boxes/generators/__init__.py +msgid "Boxes with flex" +msgstr "" + +#. name of generator group +#: boxes/generators/__init__.py +msgid "Tray" +msgstr "" + +#. title of group Tray +#: boxes/generators/__init__.py +msgid "Trays and Drawer Inserts" +msgstr "" + +#. name of generator group +#: boxes/generators/__init__.py +msgid "Shelf" +msgstr "" + +#. title of group Shelf +#: boxes/generators/__init__.py +msgid "Shelves" +msgstr "" + +#. name of generator group +#: boxes/generators/__init__.py +msgid "WallMounted" +msgstr "" + +#. name of generator group +#: boxes/generators/__init__.py +msgid "Holes" +msgstr "" + +#. title of group Holes +#: boxes/generators/__init__.py +msgid "Hole patterns" +msgstr "" + +#. name of generator group +#: boxes/generators/__init__.py +msgid "Part" +msgstr "" + +#. title of group Part +#: boxes/generators/__init__.py +msgid "Parts and Samples" +msgstr "" + +#. name of generator group +#: boxes/generators/__init__.py +msgid "Misc" +msgstr "" + +#. name of generator group +#: boxes/generators/__init__.py +msgid "Unstable" +msgstr "" + +#. description of group Unstable +#: boxes/generators/__init__.py +msgid "Generators are still untested or need manual adjustment to be useful." +msgstr "" + +msgid "DefaultParams Settings" +msgstr "" + +#. parameter name +msgid "x" +msgstr "" + +#. help for parameter x +msgid "inner width in mm (unless outside selected)" +msgstr "" + +#. parameter name +msgid "y" +msgstr "" + +#. help for parameter y +msgid "inner depth in mm (unless outside selected)" +msgstr "" + +#. parameter name +msgid "h" +msgstr "" + +#. help for parameter h +msgid "inner height in mm (unless outside selected)" +msgstr "" + +#. parameter name +msgid "hi" +msgstr "" + +#. help for parameter hi +msgid "" +"inner height of inner walls in mm (unless outside selected)(leave to zero " +"for same as outer walls)" +msgstr "" + +#. parameter name +msgid "sx" +msgstr "" + +#. help for parameter sx +msgid "" +"sections left to right in mm [🛈](https://florianfesti.github.io/boxes/html/" +"usermanual.html#section-parameters)" +msgstr "" + +#. parameter name +msgid "sy" +msgstr "" + +#. help for parameter sy +msgid "" +"sections back to front in mm [🛈](https://florianfesti.github.io/boxes/html/" +"usermanual.html#section-parameters)" +msgstr "" + +#. parameter name +msgid "sh" +msgstr "" + +#. help for parameter sh +msgid "" +"sections bottom to top in mm [🛈](https://florianfesti.github.io/boxes/html/" +"usermanual.html#section-parameters)" +msgstr "" + +#. parameter name +msgid "bottom_edge" +msgstr "" + +#. help for parameter bottom_edge +msgid "edge type for bottom edge" +msgstr "" + +#. possible choice for bottom_edge +msgid "F" +msgstr "" + +#. possible choice for bottom_edge +msgid "s" +msgstr "" + +#. possible choice for bottom_edge +msgid "e" +msgstr "" + +#. parameter name +msgid "top_edge" +msgstr "" + +#. help for parameter top_edge +msgid "edge type for top edge" +msgstr "" + +#. possible choice for top_edge +msgid "f" +msgstr "" + +#. possible choice for top_edge +msgid "c" +msgstr "" + +#. possible choice for top_edge +msgid "E" +msgstr "" + +#. possible choice for top_edge +msgid "S" +msgstr "" + +#. possible choice for top_edge +msgid "Š" +msgstr "" + +#. possible choice for top_edge +msgid "i" +msgstr "" + +#. possible choice for top_edge +msgid "k" +msgstr "" + +#. possible choice for top_edge +msgid "v" +msgstr "" + +#. possible choice for top_edge +msgid "L" +msgstr "" + +#. possible choice for top_edge +msgid "t" +msgstr "" + +#. possible choice for top_edge +msgid "G" +msgstr "" + +#. possible choice for top_edge +msgid "Y" +msgstr "" + +#. parameter name +msgid "outside" +msgstr "" + +#. help for parameter outside +msgid "" +"treat sizes as outside measurements [🛈](https://florianfesti.github.io/boxes/" +"html/usermanual.html#outside)" +msgstr "" + +#. parameter name +msgid "nema_mount" +msgstr "" + +#. help for parameter nema_mount +msgid "NEMA size of motor" +msgstr "" + +msgid "Default Settings" +msgstr "" + +#. parameter name +msgid "thickness" +msgstr "" + +#. help for parameter thickness +msgid "" +"thickness of the material (in mm) [🛈](https://florianfesti.github.io/boxes/" +"html/usermanual.html#thickness)" +msgstr "" + +#. parameter name +msgid "format" +msgstr "" + +#. help for parameter format +msgid "" +"format of resulting file [🛈](https://florianfesti.github.io/boxes/html/" +"usermanual.html#format)" +msgstr "" + +#. possible choice for format +msgid "dxf" +msgstr "" + +#. possible choice for format +msgid "gcode" +msgstr "" + +#. possible choice for format +msgid "lbrn2" +msgstr "" + +#. possible choice for format +msgid "pdf" +msgstr "" + +#. possible choice for format +msgid "plt" +msgstr "" + +#. possible choice for format +msgid "ps" +msgstr "" + +#. possible choice for format +msgid "svg" +msgstr "" + +#. possible choice for format +msgid "svg_Ponoko" +msgstr "" + +#. parameter name +msgid "tabs" +msgstr "" + +#. help for parameter tabs +msgid "" +"width of tabs holding the parts in place (in mm)(not supported everywhere) " +"[🛈](https://florianfesti.github.io/boxes/html/usermanual.html#tabs)" +msgstr "" + +#. parameter name +msgid "qr_code" +msgstr "" + +#. help for parameter qr_code +msgid "Add a QR Code with link or command line to the generated output" +msgstr "" + +#. parameter name +msgid "debug" +msgstr "" + +#. help for parameter debug +msgid "" +"print surrounding boxes for some structures [🛈](https://florianfesti.github." +"io/boxes/html/usermanual.html#debug)" +msgstr "" + +#. parameter name +msgid "labels" +msgstr "" + +#. help for parameter labels +msgid "label the parts (where available)" +msgstr "" + +#. parameter name +msgid "reference" +msgstr "" + +#. help for parameter reference +msgid "" +"print reference rectangle with given length (in mm)(zero to disable) [🛈]" +"(https://florianfesti.github.io/boxes/html/usermanual.html#reference)" +msgstr "" + +#. parameter name +msgid "inner_corners" +msgstr "" + +#. help for parameter inner_corners +msgid "" +"style for inner corners [🛈](https://florianfesti.github.io/boxes/html/" +"usermanual.html#inner-corners)" +msgstr "" + +#. possible choice for inner_corners +msgid "loop" +msgstr "" + +#. possible choice for inner_corners +msgid "corner" +msgstr "" + +#. possible choice for inner_corners +msgid "backarc" +msgstr "" + +#. parameter name +msgid "burn" +msgstr "" + +#. help for parameter burn +msgid "" +"burn correction (in mm)(bigger values for tighter fit) [🛈](https://" +"florianfesti.github.io/boxes/html/usermanual.html#burn)" +msgstr "" + +msgid "Settings for Finger Joints" +msgstr "" + +#. parameter name for FingerJoint +msgid "angle" +msgstr "" + +#. parameter name for FingerJoint +msgid "style" +msgstr "" + +#. help for parameter style +msgid "style of the fingers" +msgstr "" + +#. possible choice for style +msgid "rectangular" +msgstr "" + +#. possible choice for style +msgid "springs" +msgstr "" + +#. possible choice for style +msgid "barbs" +msgstr "" + +#. possible choice for style +msgid "snap" +msgstr "" + +#. parameter name for FingerJoint +msgid "surroundingspaces" +msgstr "" + +#. help for parameter surroundingspaces +msgid "space at the start and end in multiple of normal spaces" +msgstr "" + +#. parameter name for FingerJoint +msgid "bottom_lip" +msgstr "" + +#. help for parameter bottom_lip +msgid "" +"height of the bottom lips sticking out (multiples of thickness) " +"FingerHoleEdge only!" +msgstr "" + +#. parameter name for FingerJoint +msgid "edge_width" +msgstr "" + +#. help for parameter edge_width +msgid "space below holes of FingerHoleEdge (multiples of thickness)" +msgstr "" + +#. parameter name for FingerJoint +msgid "extra_length" +msgstr "" + +#. help for parameter extra_length +msgid "extra material to grind away burn marks (multiples of thickness)" +msgstr "" + +#. parameter name for FingerJoint +msgid "finger" +msgstr "" + +#. help for parameter finger +msgid "width of the fingers (multiples of thickness)" +msgstr "" + +#. parameter name for FingerJoint +msgid "play" +msgstr "" + +#. help for parameter play +msgid "extra space to allow finger move in and out (multiples of thickness)" +msgstr "" + +#. parameter name for FingerJoint +msgid "space" +msgstr "" + +#. help for parameter space +msgid "space between fingers (multiples of thickness)" +msgstr "" + +#. parameter name for FingerJoint +msgid "width" +msgstr "" + +#. help for parameter width +msgid "width of finger holes (multiples of thickness)" +msgstr "" + +msgid "Settings for Stackable Edges" +msgstr "" + +#. help for parameter angle +msgid "inside angle of the feet" +msgstr "" + +#. parameter name for Stackable +msgid "bottom_stabilizers" +msgstr "" + +#. help for parameter bottom_stabilizers +msgid "" +"height of strips to be glued to the inside of bottom edges (multiples of " +"thickness)" +msgstr "" + +#. parameter name for Stackable +msgid "height" +msgstr "" + +#. help for parameter height +msgid "height of the feet (multiples of thickness)" +msgstr "" + +#. parameter name for Stackable +msgid "holedistance" +msgstr "" + +#. help for parameter holedistance +msgid "distance from finger holes to bottom edge (multiples of thickness)" +msgstr "" + +#. help for parameter width +msgid "width of the feet (multiples of thickness)" +msgstr "" + +msgid "Settings for Hinges and HingePins" +msgstr "" + +#. parameter name for Hinge +msgid "grip_percentage" +msgstr "" + +#. parameter name for Hinge +msgid "outset" +msgstr "" + +#. help for parameter outset +msgid "have lid overlap at the sides (similar to OutSetEdge)" +msgstr "" + +#. parameter name for Hinge +msgid "pinwidth" +msgstr "" + +#. help for parameter pinwidth +msgid "set to lower value to get disks surrounding the pins" +msgstr "" + +#. help for parameter style +msgid "\"outset\" or \"flush\"" +msgstr "" + +#. possible choice for style +msgid "flush" +msgstr "" + +#. parameter name for Hinge +msgid "axle" +msgstr "" + +#. help for parameter axle +msgid "diameter of the pin hole (multiples of thickness)" +msgstr "" + +#. parameter name for Hinge +msgid "grip_length" +msgstr "" + +#. help for parameter grip_length +msgid "fixed length of the grips on he lids (multiples of thickness)" +msgstr "" + +#. parameter name for Hinge +msgid "hingestrength" +msgstr "" + +#. help for parameter hingestrength +msgid "thickness of the arc holding the pin in place (multiples of thickness)" +msgstr "" + +msgid "Settings for Slide-on Lids" +msgstr "" + +#. parameter name for SlideOnLid +msgid "hole_width" +msgstr "" + +#. help for parameter hole_width +msgid "width of the \"finger hole\" in mm" +msgstr "" + +#. parameter name for SlideOnLid +msgid "second_pin" +msgstr "" + +#. help for parameter second_pin +msgid "additional pin for better positioning" +msgstr "" + +#. parameter name for SlideOnLid +msgid "spring" +msgstr "" + +#. help for parameter spring +msgid "position(s) of the extra locking springs in the lid" +msgstr "" + +#. possible choice for spring +msgid "both" +msgstr "" + +#. possible choice for spring +msgid "none" +msgstr "" + +#. possible choice for spring +msgid "left" +msgstr "" + +#. possible choice for spring +msgid "right" +msgstr "" + +msgid "Settings for Click-on Lids" +msgstr "" + +#. help for parameter angle +msgid "angle of the hooks bending outward" +msgstr "" + +#. parameter name for Click +msgid "bottom_radius" +msgstr "" + +#. help for parameter bottom_radius +msgid "radius at the bottom (multiples of thickness)" +msgstr "" + +#. parameter name for Click +msgid "depth" +msgstr "" + +#. help for parameter depth +msgid "length of the hooks (multiples of thickness)" +msgstr "" + +msgid "Settings for Flex" +msgstr "" + +#. parameter name for Flex +msgid "stretch" +msgstr "" + +#. help for parameter stretch +msgid "Hint of how much the flex part should be shortened" +msgstr "" + +#. parameter name for Flex +msgid "connection" +msgstr "" + +#. help for parameter connection +msgid "width of the gaps in the cuts (multiples of thickness)" +msgstr "" + +#. parameter name for Flex +msgid "distance" +msgstr "" + +#. help for parameter distance +msgid "width of the pattern perpendicular to the cuts (multiples of thickness)" +msgstr "" + +#. help for parameter width +msgid "width of the pattern in direction of the cuts (multiples of thickness)" +msgstr "" + +msgid "Settings for the Lid" +msgstr "" + +#. parameter name for Lid +msgid "handle" +msgstr "" + +#. help for parameter handle +msgid "type of handle" +msgstr "" + +#. possible choice for handle +msgid "long_rounded" +msgstr "" + +#. possible choice for handle +msgid "long_trapezoid" +msgstr "" + +#. possible choice for handle +msgid "long_doublerounded" +msgstr "" + +#. possible choice for handle +msgid "knob" +msgstr "" + +#. help for parameter style +msgid "type of lid to create" +msgstr "" + +#. possible choice for style +msgid "flat" +msgstr "" + +#. possible choice for style +msgid "chest" +msgstr "" + +#. possible choice for style +msgid "overthetop" +msgstr "" + +#. possible choice for style +msgid "ontop" +msgstr "" + +#. parameter name for Lid +msgid "handle_height" +msgstr "" + +#. help for parameter handle_height +msgid "height of the handle (if applicable)" +msgstr "" + +#. help for parameter height +msgid "height of the brim (if any)" +msgstr "" + +#. help for parameter play +msgid "play when sliding the lid on (if applicable)" +msgstr "" + +#. name of box generator +#: boxes/generators/abox.py +msgid "ABox" +msgstr "" + +#. description of ABox +#: boxes/generators/abox.py +msgid "A simple Box" +msgstr "" + +#. long description of ABox in markdown +#: boxes/generators/abox.py +msgid "" +"This box is kept simple on purpose. If you need more features have a look at " +"the UniversalBox." +msgstr "" + +msgid "ABox Settings" +msgstr "" + +#. name of box generator +#: boxes/generators/agricolainsert.py +msgid "AgricolaInsert" +msgstr "" + +#. description of AgricolaInsert +#: boxes/generators/agricolainsert.py +msgid "" +"\n" +" Agricola Revised Edition game box insert, including some expansions.\n" +" " +msgstr "" + +#. long description of AgricolaInsert in markdown +#: boxes/generators/agricolainsert.py +msgid "" +"\n" +"This insert was designed with 3 mm plywood in mind, and should work fine " +"with\n" +"materials around this thickness.\n" +"\n" +"This is an insert for the [Agricola Revised Edition](https://boardgamegeek." +"com/boardgame/200680/agricola-revised-edition)\n" +"board game. It is specifically designed around the [Farmers Of The Moor " +"expansion](https://boardgamegeek.com/boardgameexpansion/257344/agricola-" +"farmers-moor),\n" +"and should also store the [5-6 players expansion](https://boardgamegeek.com/" +"boardgameexpansion/210625/agricola-expansion-5-and-6-players)\n" +"(not tested, but I tried to take everything into account for it, please " +"inform\n" +"us if you tested it).\n" +"\n" +"It can be stored inside the original game box, including the 2 expansions,\n" +"with the lid slightly raised.\n" +"\n" +"The parts of a given element are mostly generated next to each other " +"vertically.\n" +"It should be straightforward to match them.\n" +"\n" +"Here are the different elements, from left to right in the generated file.\n" +"\n" +"#### Card tray\n" +"\n" +"The cards are all kept in a tray, with paper dividers to sort them easily. " +"When\n" +"the tray is not full of cards, wood dividers slides in slots in order to " +"keep\n" +"the cards from falling into the empty space.\n" +"\n" +"There should be enough space for the main game, Farmers Of The Moor, and the " +"5-6\n" +"player expansion, but not much more than that.\n" +"\n" +"To keep a lower profile, the cards are at a slight angle, and the paper " +"dividers\n" +"tabs are horizontal instead of vertical.\n" +"A small wall keeps the card against one side while the tabs protrude on the\n" +"other side, above the small wall.\n" +"\n" +"The wall with the big hole is the sloped one. It goes between the two\n" +"\"comb-like\" walls first, with its two small holes at the bottom. Then " +"there is a\n" +"low-height long wall with a sloped edge which should go from the sloped wall " +"to\n" +"the other side. You can finish the tray with the last wall at the end.\n" +"\n" +"#### Upper level trays\n" +"\n" +"4 trays with movable walls are used to store resources. They were designed " +"to\n" +"store them in this order:\n" +"\n" +"* Stone / Vegetable / Pig / Cow\n" +"* Reed / Grain / Sheep\n" +"* Wood / Clay\n" +"* Food / Fire\n" +"\n" +"The wall would probably be better if fixed instead of movable, but I would " +"like\n" +"to test with the 5-6 player expansion to be sure their positions are " +"correct\n" +"with it too.\n" +"\n" +"The little feet of the movable wall should be glued. The triangles are put\n" +"horizontally, with their bases towards the sides.\n" +"\n" +"#### Lower level tray\n" +"\n" +"The lower level tray is used to store the horses.\n" +"\n" +"#### Room/Field tiles\n" +"\n" +"Two boxes are generated to store the room/field tiles. One for the wood/" +"field,\n" +"the other for the clay/stone. They are stored with the main opening upside, " +"but\n" +"I prefer to use them during play with this face on the side.\n" +"\n" +"#### Moor/Forest and miscellaneous tiles\n" +"\n" +"A box is generated to store the Moor/Forest tiles, and some other tiles such " +"as\n" +"the \"multiple resources\" cardboard tokens.\n" +"\n" +"The Moor/Forest tiles are at the same height as the Room/Field, and the " +"upper\n" +"level trays are directly on them. The horse box and player box are slightly\n" +"lower. This Moor/Forest box have a lowered corner (the one for the " +"miscellaneous\n" +"tiles). Two cardboard pieces can be stored between the smaller boxes and " +"the\n" +"upper level trays (as seen on the picture).\n" +"\n" +"Be sure to match the pieces so that the walls with smaller heights are next " +"to\n" +"each other.\n" +"\n" +"#### Players bit boxes\n" +"\n" +"Each player has its own box where the bits of his color are stored.\n" +"The cardboard bed from Farmers Of The Moor is central to this box.\n" +"\n" +"* The fences are stored inside the bed\n" +"* The bed is placed in the box, with holes to keep it there (and to take " +"less\n" +" height)\n" +"* The stables are stored in the two corners\n" +"* The five farmers are stored between the bed and the three walls, " +"alternatively\n" +" head up and head down.\n" +"\n" +"During assembly, the small bars are put in the middle holes. The two bigger\n" +"holes at the ends are used for the bed feet. The bar keeps the bed from\n" +"protruding underneath.\n" +"\n" +msgstr "" + +msgid "AgricolaInsert Settings" +msgstr "" + +#. name of box generator +#: boxes/generators/alledges.py +msgid "AllEdges" +msgstr "" + +#. description of AllEdges +#: boxes/generators/alledges.py +msgid "Showing all edge types" +msgstr "" + +msgid "AllEdges Settings" +msgstr "" + +#. help for parameter x +#: boxes/generators/alledges.py +msgid "inner width in mm" +msgstr "" + +msgid "Settings for HandleEdge" +msgstr "" + +#. help for parameter height +#: boxes/generators/alledges.py +msgid "height above the wall in mm" +msgstr "" + +#. parameter name for HandleEdge +#: boxes/generators/alledges.py +msgid "hole_height" +msgstr "" + +#. help for parameter hole_height +#: boxes/generators/alledges.py +msgid "" +"height of hole(s) in percentage of maximum hole height (handle height - 2 * " +"material thickness)" +msgstr "" + +#. help for parameter hole_width +#: boxes/generators/alledges.py +msgid "" +"width of hole(s) in percentage of maximum hole width (width of edge - (n+1) " +"* material thickness)" +msgstr "" + +#. parameter name for HandleEdge +#: boxes/generators/alledges.py +msgid "on_sides" +msgstr "" + +#. help for parameter on_sides +#: boxes/generators/alledges.py +msgid "" +"added to side panels if checked, to front and back otherwise (only used with " +"top_edge parameter)" +msgstr "" + +#. parameter name for HandleEdge +#: boxes/generators/alledges.py +msgid "radius" +msgstr "" + +#. help for parameter radius +#: boxes/generators/alledges.py +msgid "radius of corners in mm" +msgstr "" + +#. help for parameter outset +#: boxes/generators/alledges.py +msgid "extend the handle along the length of the edge (multiples of thickness)" +msgstr "" + +#. name of box generator +#: boxes/generators/angledbox.py +msgid "AngledBox" +msgstr "" + +#. description of AngledBox +#: boxes/generators/angledbox.py +msgid "Box with both ends cornered" +msgstr "" + +msgid "AngledBox Settings" +msgstr "" + +#. parameter name +#: boxes/generators/angledbox.py +msgid "n" +msgstr "" + +#. help for parameter n +#: boxes/generators/angledbox.py +msgid "number of walls at one side (1+)" +msgstr "" + +#. parameter name +#: boxes/generators/angledbox.py +msgid "top" +msgstr "" + +#. help for parameter top +#: boxes/generators/angledbox.py +msgid "style of the top and lid" +msgstr "" + +#. possible choice for top +#: boxes/generators/angledbox.py +msgid "angled hole" +msgstr "" + +#. possible choice for top +#: boxes/generators/angledbox.py +msgid "angled lid" +msgstr "" + +#. possible choice for top +#: boxes/generators/angledbox.py +msgid "angled lid2" +msgstr "" + +#. name of box generator +#: boxes/generators/angledcutjig.py +msgid "AngledCutJig" +msgstr "" + +#. description of AngledCutJig +#: boxes/generators/angledcutjig.py +msgid "Jig for making angled cuts in a laser cutter" +msgstr "" + +msgid "AngledCutJig Settings" +msgstr "" + +#. help for parameter y +#: boxes/generators/angledcutjig.py +msgid "inner depth in mm" +msgstr "" + +#. help for parameter angle +#: boxes/generators/angledcutjig.py +msgid "Angle of the cut" +msgstr "" + +#. name of box generator +#: boxes/generators/arcade.py +msgid "Arcade" +msgstr "" + +#. description of Arcade +#: boxes/generators/arcade.py +msgid "Desktop Arcade Machine" +msgstr "" + +msgid "Arcade Settings" +msgstr "" + +#. help for parameter width +#: boxes/generators/arcade.py +msgid "inner width of the console" +msgstr "" + +#. parameter name +#: boxes/generators/arcade.py +msgid "monitor_height" +msgstr "" + +#. parameter name +#: boxes/generators/arcade.py +msgid "keyboard_depth" +msgstr "" + +#. name of box generator +#: boxes/generators/atreus21.py +msgid "Atreus21" +msgstr "" + +#. description of Atreus21 +#: boxes/generators/atreus21.py +msgid "Generator for a split atreus keyboard." +msgstr "" + +msgid "Atreus21 Settings" +msgstr "" + +#. parameter name +#: boxes/generators/atreus21.py +msgid "hotswap_enable" +msgstr "" + +#. help for parameter hotswap_enable +#: boxes/generators/atreus21.py +msgid "enlarge switches holes for hotswap pcb sockets" +msgstr "" + +#. parameter name +#: boxes/generators/atreus21.py +msgid "pcb_mount_enable" +msgstr "" + +#. help for parameter pcb_mount_enable +#: boxes/generators/atreus21.py +msgid "adds holes for pcb mount switches" +msgstr "" + +#. parameter name +#: boxes/generators/atreus21.py +msgid "led_enable" +msgstr "" + +#. help for parameter led_enable +#: boxes/generators/atreus21.py +msgid "adds pin holes under switches for leds" +msgstr "" + +#. parameter name +#: boxes/generators/atreus21.py +msgid "diode_enable" +msgstr "" + +#. help for parameter diode_enable +#: boxes/generators/atreus21.py +msgid "adds pin holes under switches for diodes" +msgstr "" + +#. parameter name +#: boxes/generators/atreus21.py +msgid "cutout_type" +msgstr "" + +#. help for parameter cutout_type +#: boxes/generators/atreus21.py +msgid "" +"Shape of the plate cutout: 'castle' allows for modding, and 'simple' is a " +"tighter and simpler square" +msgstr "" + +#. parameter name +#: boxes/generators/atreus21.py +msgid "columns_definition" +msgstr "" + +#. help for parameter columns_definition +#: boxes/generators/atreus21.py +msgid "" +"Each column is separated by '/', and is in the form 'nb_rows @ offset x " +"repeat_count'. Nb_rows is the number of rows for this column. The offset is " +"in mm and optional. Repeat_count is optional and repeats this column " +"multiple times. Spaces are not important.For example '3x2 / 4@11' means we " +"want 3 columns, the two first with 3 rows without offset, and the last with " +"4 rows starting at 11mm high." +msgstr "" + +#. name of box generator +#: boxes/generators/basedbox.py +msgid "BasedBox" +msgstr "" + +#. description of BasedBox +#: boxes/generators/basedbox.py +msgid "Fully closed box on a base" +msgstr "" + +#. long description of BasedBox in markdown +#: boxes/generators/basedbox.py +msgid "" +"This box is more of a building block than a finished item.\n" +"Use a vector graphics program (like Inkscape) to add holes or adjust the " +"base\n" +"plate. The width of the \"brim\" can also be adjusted with the " +"**edge_width**\n" +" parameter in the **Finger Joints Settings**.\n" +" \n" +"See ClosedBox for variant without a base.\n" +msgstr "" + +msgid "BasedBox Settings" +msgstr "" + +#. name of box generator +#: boxes/generators/bayonetbox.py +msgid "BayonetBox" +msgstr "" + +#. description of BayonetBox +#: boxes/generators/bayonetbox.py +msgid "Round box made from layers with twist on top" +msgstr "" + +#. long description of BayonetBox in markdown +#: boxes/generators/bayonetbox.py +msgid "" +"Glue together - all outside rings to the bottom, all inside rings to the top." +msgstr "" + +msgid "BayonetBox Settings" +msgstr "" + +#. parameter name +#: boxes/generators/bayonetbox.py +msgid "diameter" +msgstr "" + +#. help for parameter diameter +#: boxes/generators/bayonetbox.py +msgid "Diameter of the box in mm" +msgstr "" + +#. parameter name +#: boxes/generators/bayonetbox.py +msgid "lugs" +msgstr "" + +#. help for parameter lugs +#: boxes/generators/bayonetbox.py +msgid "number of locking lugs" +msgstr "" + +#. parameter name +#: boxes/generators/bayonetbox.py +msgid "alignment_pins" +msgstr "" + +#. help for parameter alignment_pins +#: boxes/generators/bayonetbox.py +msgid "diameter of the alignment pins" +msgstr "" + +#. name of box generator +#: boxes/generators/bintray.py +msgid "BinTray" +msgstr "" + +#. description of BinTray +#: boxes/generators/bintray.py +msgid "A Type tray variant to be used up right with sloped walls in front" +msgstr "" + +msgid "BinTray Settings" +msgstr "" + +#. parameter name +#: boxes/generators/bintray.py +msgid "hole_dD" +msgstr "" + +#. help for parameter hole_dD +#: boxes/generators/bintray.py +msgid "" +"mounting hole diameter (shaft:head) in mm [🛈](https://florianfesti.github.io/" +"boxes/html/usermanual.html#mounting-holes)" +msgstr "" + +#. parameter name +#: boxes/generators/bintray.py +msgid "front" +msgstr "" + +#. help for parameter front +#: boxes/generators/bintray.py +msgid "fraction of bin height covert with slope" +msgstr "" + +#. name of box generator +#: boxes/generators/birdhouse.py +msgid "BirdHouse" +msgstr "" + +#. description of BirdHouse +#: boxes/generators/birdhouse.py +msgid "Simple Bird House" +msgstr "" + +msgid "BirdHouse Settings" +msgstr "" + +#. help for parameter h +#: boxes/generators/birdhouse.py +msgid "inner height in mm" +msgstr "" + +#. parameter name +#: boxes/generators/birdhouse.py +msgid "roof_overhang" +msgstr "" + +#. help for parameter roof_overhang +#: boxes/generators/birdhouse.py +msgid "overhang as fraction of the roof length" +msgstr "" + +#. name of box generator +#: boxes/generators/bottlestack.py +msgid "BottleStack" +msgstr "" + +#. description of BottleStack +#: boxes/generators/bottlestack.py +msgid "Stack bottles in a fridge" +msgstr "" + +#. long description of BottleStack in markdown +#: boxes/generators/bottlestack.py +msgid "" +"When rendered with the \"double\" option the parts with the double slots get " +"connected the shorter beams in the asymmetrical slots.\n" +"\n" +"Without the \"double\" option the stand is a bit more narrow.\n" +msgstr "" + +msgid "BottleStack Settings" +msgstr "" + +#. help for parameter diameter +#: boxes/generators/bottlestack.py +msgid "diameter of the bottles in mm" +msgstr "" + +#. parameter name +#: boxes/generators/bottlestack.py +msgid "number" +msgstr "" + +#. help for parameter number +#: boxes/generators/bottlestack.py +msgid "number of bottles to hold in the bottom row" +msgstr "" + +#. help for parameter depth +#: boxes/generators/bottlestack.py +msgid "depth of the stand along the base of the bottles" +msgstr "" + +#. parameter name +#: boxes/generators/bottlestack.py +msgid "double" +msgstr "" + +#. help for parameter double +#: boxes/generators/bottlestack.py +msgid "two pieces that can be combined to up to double the width" +msgstr "" + +#. name of box generator +#: boxes/generators/bottletag.py +msgid "BottleTag" +msgstr "" + +#. description of BottleTag +#: boxes/generators/bottletag.py +msgid "Paper slip over bottle tag" +msgstr "" + +msgid "BottleTag Settings" +msgstr "" + +#. help for parameter width +#: boxes/generators/bottletag.py +msgid "width of neck tag" +msgstr "" + +#. help for parameter height +#: boxes/generators/bottletag.py +msgid "height of neck tag" +msgstr "" + +#. parameter name +#: boxes/generators/bottletag.py +msgid "min_diameter" +msgstr "" + +#. help for parameter min_diameter +#: boxes/generators/bottletag.py +msgid "inner diameter of bottle neck hole" +msgstr "" + +#. parameter name +#: boxes/generators/bottletag.py +msgid "max_diameter" +msgstr "" + +#. help for parameter max_diameter +#: boxes/generators/bottletag.py +msgid "outer diameter of bottle neck hole" +msgstr "" + +#. help for parameter radius +#: boxes/generators/bottletag.py +msgid "corner radius of bottom tag" +msgstr "" + +#. parameter name +#: boxes/generators/bottletag.py +msgid "segment_width" +msgstr "" + +#. help for parameter segment_width +#: boxes/generators/bottletag.py +msgid "inner segment width" +msgstr "" + +#. name of box generator +#: boxes/generators/breadbox.py +msgid "BreadBox" +msgstr "" + +#. description of BreadBox +#: boxes/generators/breadbox.py +msgid "A BreadBox with a gliding door" +msgstr "" + +#. long description of BreadBox in markdown +#: boxes/generators/breadbox.py +msgid "Beware of the rolling shutter effect! Use wax on sliding surfaces.\n" +msgstr "" + +msgid "BreadBox Settings" +msgstr "" + +#. help for parameter radius +#: boxes/generators/breadbox.py +msgid "radius of the corners" +msgstr "" + +#. name of box generator +#: boxes/generators/burntest.py +msgid "BurnTest" +msgstr "" + +#. description of BurnTest +#: boxes/generators/burntest.py +msgid "Test different burn values" +msgstr "" + +#. long description of BurnTest in markdown +#: boxes/generators/burntest.py +msgid "" +"This generator will make shapes that you can use to select\n" +"optimal value for burn parameter for other generators. After burning try to\n" +"attach sides with the same value and use best fitting one on real projects.\n" +"In this generator set burn in the Default Settings to the lowest value\n" +"to be tested. To get an idea cut a rectangle with known nominal size and\n" +"measure the shrinkage due to the width of the laser cut. Now you can\n" +"measure the burn value that you should use in other generators. It is half\n" +"the difference of the overall size as shrinkage is occurring on both\n" +"sides. You can use the reference rectangle as it is rendered without burn\n" +"correction.\n" +"\n" +"See also LBeam that can serve as compact BurnTest and FlexTest for testing " +"flex settings.\n" +msgstr "" + +msgid "BurnTest Settings" +msgstr "" + +#. parameter name +#: boxes/generators/burntest.py +msgid "step" +msgstr "" + +#. help for parameter step +#: boxes/generators/burntest.py +msgid "increases in burn value between the sides" +msgstr "" + +#. parameter name +#: boxes/generators/burntest.py +msgid "pairs" +msgstr "" + +#. help for parameter pairs +#: boxes/generators/burntest.py +msgid "number of pairs (each testing four burn values)" +msgstr "" + +#. name of box generator +#: boxes/generators/can_storage.py +msgid "CanStorage" +msgstr "" + +#. description of CanStorage +#: boxes/generators/can_storage.py +msgid "Storage box for round containers" +msgstr "" + +#. long description of CanStorage in markdown +#: boxes/generators/can_storage.py +msgid "" +"\n" +"for AA batteries:\n" +"\n" +"![CanStorage for AA batteries](static/samples/CanStorageAA.jpg)\n" +"\n" +"for canned tomatoes:\n" +msgstr "" + +msgid "CanStorage Settings" +msgstr "" + +#. possible choice for bottom_edge +#: boxes/generators/can_storage.py +msgid "š" +msgstr "" + +#. parameter name +#: boxes/generators/can_storage.py +msgid "canDiameter" +msgstr "" + +#. help for parameter canDiameter +#: boxes/generators/can_storage.py +msgid "outer diameter of the cans to be stored (in mm)" +msgstr "" + +#. parameter name +#: boxes/generators/can_storage.py +msgid "canHeight" +msgstr "" + +#. help for parameter canHeight +#: boxes/generators/can_storage.py +msgid "height of the cans to be stored (in mm)" +msgstr "" + +#. parameter name +#: boxes/generators/can_storage.py +msgid "canNum" +msgstr "" + +#. help for parameter canNum +#: boxes/generators/can_storage.py +msgid "number of cans to be stored" +msgstr "" + +#. parameter name +#: boxes/generators/can_storage.py +msgid "chuteAngle" +msgstr "" + +#. help for parameter chuteAngle +#: boxes/generators/can_storage.py +msgid "slope angle of the chutes" +msgstr "" + +msgid "Settings for Hole filling" +msgstr "" + +#. parameter name for fillHoles +#: boxes/generators/can_storage.py +msgid "bar_length" +msgstr "" + +#. help for parameter bar_length +#: boxes/generators/can_storage.py +msgid "maximum length of bars" +msgstr "" + +#. parameter name for fillHoles +#: boxes/generators/can_storage.py +msgid "fill_pattern" +msgstr "" + +#. help for parameter fill_pattern +#: boxes/generators/can_storage.py +msgid "style of hole pattern" +msgstr "" + +#. possible choice for fill_pattern +#: boxes/generators/can_storage.py +msgid "no fill" +msgstr "" + +#. possible choice for fill_pattern +#: boxes/generators/can_storage.py +msgid "hex" +msgstr "" + +#. possible choice for fill_pattern +#: boxes/generators/can_storage.py +msgid "square" +msgstr "" + +#. possible choice for fill_pattern +#: boxes/generators/can_storage.py +msgid "random" +msgstr "" + +#. possible choice for fill_pattern +#: boxes/generators/can_storage.py +msgid "hbar" +msgstr "" + +#. possible choice for fill_pattern +#: boxes/generators/can_storage.py +msgid "vbar" +msgstr "" + +#. parameter name for fillHoles +#: boxes/generators/can_storage.py +msgid "hole_max_radius" +msgstr "" + +#. help for parameter hole_max_radius +#: boxes/generators/can_storage.py +msgid "maximum radius of generated holes (in mm)" +msgstr "" + +#. parameter name for fillHoles +#: boxes/generators/can_storage.py +msgid "hole_min_radius" +msgstr "" + +#. help for parameter hole_min_radius +#: boxes/generators/can_storage.py +msgid "minimum radius of generated holes (in mm)" +msgstr "" + +#. parameter name for fillHoles +#: boxes/generators/can_storage.py +msgid "hole_style" +msgstr "" + +#. help for parameter hole_style +#: boxes/generators/can_storage.py +msgid "style of holes (does not apply to fill patterns 'vbar' and 'hbar')" +msgstr "" + +#. possible choice for hole_style +#: boxes/generators/can_storage.py +msgid "round" +msgstr "" + +#. possible choice for hole_style +#: boxes/generators/can_storage.py +msgid "triangle" +msgstr "" + +#. possible choice for hole_style +#: boxes/generators/can_storage.py +msgid "hexagon" +msgstr "" + +#. possible choice for hole_style +#: boxes/generators/can_storage.py +msgid "octagon" +msgstr "" + +#. parameter name for fillHoles +#: boxes/generators/can_storage.py +msgid "max_random" +msgstr "" + +#. help for parameter max_random +#: boxes/generators/can_storage.py +msgid "maximum number of random holes" +msgstr "" + +#. parameter name for fillHoles +#: boxes/generators/can_storage.py +msgid "space_between_holes" +msgstr "" + +#. help for parameter space_between_holes +#: boxes/generators/can_storage.py +msgid "hole to hole spacing (in mm)" +msgstr "" + +#. parameter name for fillHoles +#: boxes/generators/can_storage.py +msgid "space_to_border" +msgstr "" + +#. help for parameter space_to_border +#: boxes/generators/can_storage.py +msgid "hole to border spacing (in mm)" +msgstr "" + +#. name of box generator +#: boxes/generators/cardbox.py +msgid "CardBox" +msgstr "" + +#. description of CardBox +#: boxes/generators/cardbox.py +msgid "Box for storage of playing cards, with versatile options" +msgstr "" + +#. long description of CardBox in markdown +#: boxes/generators/cardbox.py +msgid "" +"\n" +"### Description\n" +"Versatile Box for Storage of playing cards. Multiple different styles of " +"storage are supportet, e.g. a flat storage or a trading card deck box style " +"storage. See images for ideas.\n" +"\n" +"#### Building instructions\n" +"Place inner walls on floor first (if any). Then add the outer walls. Glue " +"the two walls without finger joins to the inside of the side walls. Make " +"sure there is no squeeze out on top, as this is going to form the rail for " +"the lid.\n" +"\n" +"Add the top of the rails to the sides (front open) or to the back and front " +"(right side open) and the grip rail to the lid.\n" +"Details of the lid and rails\n" +"![Details](static/samples/CardBox-detail.jpg)\n" +"Whole box (early version still missing grip rail on the lid):\n" +msgstr "" + +msgid "CardBox Settings" +msgstr "" + +#. parameter name +#: boxes/generators/cardbox.py +msgid "openingdirection" +msgstr "" + +#. help for parameter openingdirection +#: boxes/generators/cardbox.py +msgid "" +"Direction in which the lid slides open. Lid length > Lid width recommended." +msgstr "" + +#. parameter name +#: boxes/generators/cardbox.py +msgid "fingerhole" +msgstr "" + +#. help for parameter fingerhole +#: boxes/generators/cardbox.py +msgid "Depth of cutout to grab the cards" +msgstr "" + +#. possible choice for fingerhole +#: boxes/generators/cardbox.py +msgid "regular" +msgstr "" + +#. possible choice for fingerhole +#: boxes/generators/cardbox.py +msgid "deep" +msgstr "" + +#. possible choice for fingerhole +#: boxes/generators/cardbox.py +msgid "custom" +msgstr "" + +#. parameter name +#: boxes/generators/cardbox.py +msgid "fingerhole_depth" +msgstr "" + +#. help for parameter fingerhole_depth +#: boxes/generators/cardbox.py +msgid "Depth of cutout if fingerhole is set to 'custom'. Disabled otherwise." +msgstr "" + +#. parameter name +#: boxes/generators/cardbox.py +msgid "add_lidtopper" +msgstr "" + +#. help for parameter add_lidtopper +#: boxes/generators/cardbox.py +msgid "Add an additional lid topper for optical reasons and customisation" +msgstr "" + +#. name of box generator +#: boxes/generators/cardholder.py +msgid "CardHolder" +msgstr "" + +#. description of CardHolder +#: boxes/generators/cardholder.py +msgid "Shelf for holding (multiple) piles of playing cards / notes" +msgstr "" + +msgid "CardHolder Settings" +msgstr "" + +#. help for parameter angle +#: boxes/generators/cardholder.py +msgid "backward angle of floor" +msgstr "" + +#. parameter name +#: boxes/generators/cardholder.py +msgid "stackable" +msgstr "" + +#. help for parameter stackable +#: boxes/generators/cardholder.py +msgid "make holders stackable" +msgstr "" + +msgid "Settings for Grooved Edge" +msgstr "" + +#. parameter name for Grooved +#: boxes/generators/cardholder.py +msgid "arc_angle" +msgstr "" + +#. help for parameter arc_angle +#: boxes/generators/cardholder.py +msgid "the angle of arc cuts" +msgstr "" + +#. parameter name for Grooved +#: boxes/generators/cardholder.py +msgid "gap" +msgstr "" + +#. help for parameter gap +#: boxes/generators/cardholder.py +msgid "the gap between grooves (fraction of the edge length)" +msgstr "" + +#. parameter name for Grooved +#: boxes/generators/cardholder.py +msgid "interleave" +msgstr "" + +#. help for parameter interleave +#: boxes/generators/cardholder.py +msgid "alternate the direction of grooves" +msgstr "" + +#. parameter name for Grooved +#: boxes/generators/cardholder.py +msgid "inverse" +msgstr "" + +#. help for parameter inverse +#: boxes/generators/cardholder.py +msgid "invert the groove directions" +msgstr "" + +#. parameter name for Grooved +#: boxes/generators/cardholder.py +msgid "margin" +msgstr "" + +#. help for parameter margin +#: boxes/generators/cardholder.py +msgid "" +"minimum space left and right without grooves (fraction of the edge length)" +msgstr "" + +#. help for parameter style +#: boxes/generators/cardholder.py +msgid "the style of grooves" +msgstr "" + +#. possible choice for style +#: boxes/generators/cardholder.py +msgid "arc" +msgstr "" + +#. possible choice for style +#: boxes/generators/cardholder.py +msgid "softarc" +msgstr "" + +#. parameter name for Grooved +#: boxes/generators/cardholder.py +msgid "tri_angle" +msgstr "" + +#. help for parameter tri_angle +#: boxes/generators/cardholder.py +msgid "the angle of triangular cuts" +msgstr "" + +#. help for parameter width +#: boxes/generators/cardholder.py +msgid "the width of each groove (fraction of the edge length)" +msgstr "" + +#. name of box generator +#: boxes/generators/castle.py +msgid "Castle" +msgstr "" + +#. description of Castle +#: boxes/generators/castle.py +msgid "Castle tower with two walls" +msgstr "" + +#. long description of Castle in markdown +#: boxes/generators/castle.py +msgid "" +"This was done as a table decoration. May be at some point in the future " +"someone will create a proper castle\n" +"with towers and gates and walls that can be attached in multiple " +"configurations." +msgstr "" + +msgid "Castle Settings" +msgstr "" + +#. name of box generator +#: boxes/generators/closedbox.py +msgid "ClosedBox" +msgstr "" + +#. description of ClosedBox +#: boxes/generators/closedbox.py +msgid "Fully closed box" +msgstr "" + +#. long description of ClosedBox in markdown +#: boxes/generators/closedbox.py +msgid "" +"This box is more of a building block than a finished item.\n" +"Use a vector graphics program (like Inkscape) to add holes or adjust the " +"base\n" +"plate.\n" +"\n" +"See BasedBox for variant with a base." +msgstr "" + +msgid "ClosedBox Settings" +msgstr "" + +#. name of box generator +#: boxes/generators/coffeecapsulesholder.py +msgid "CoffeeCapsuleHolder" +msgstr "" + +#. description of CoffeeCapsuleHolder +#: boxes/generators/coffeecapsulesholder.py +msgid "" +"\n" +" Coffee capsule holder\n" +" " +msgstr "" + +#. long description of CoffeeCapsuleHolder in markdown +#: boxes/generators/coffeecapsulesholder.py +msgid "" +"\n" +" You can store your coffee capsule near your espresso machine with this. " +"It works both vertically, or upside down under a shelf.\n" +msgstr "" + +msgid "CoffeeCapsuleHolder Settings" +msgstr "" + +#. parameter name +#: boxes/generators/coffeecapsulesholder.py +msgid "columns" +msgstr "" + +#. help for parameter columns +#: boxes/generators/coffeecapsulesholder.py +msgid "Number of columns of capsules." +msgstr "" + +#. parameter name +#: boxes/generators/coffeecapsulesholder.py +msgid "rows" +msgstr "" + +#. help for parameter rows +#: boxes/generators/coffeecapsulesholder.py +msgid "Number of capsules by columns." +msgstr "" + +#. parameter name +#: boxes/generators/coffeecapsulesholder.py +msgid "backplate" +msgstr "" + +#. help for parameter backplate +#: boxes/generators/coffeecapsulesholder.py +msgid "True if a backplate should be generated." +msgstr "" + +#. name of box generator +#: boxes/generators/coindisplay.py +msgid "CoinDisplay" +msgstr "" + +#. description of CoinDisplay +#: boxes/generators/coindisplay.py +msgid "A showcase for a single coin" +msgstr "" + +msgid "CoinDisplay Settings" +msgstr "" + +#. parameter name +#: boxes/generators/coindisplay.py +msgid "coin_d" +msgstr "" + +#. help for parameter coin_d +#: boxes/generators/coindisplay.py +msgid "The diameter of the coin in mm" +msgstr "" + +#. parameter name +#: boxes/generators/coindisplay.py +msgid "coin_plate" +msgstr "" + +#. help for parameter coin_plate +#: boxes/generators/coindisplay.py +msgid "The size of the coin plate" +msgstr "" + +#. parameter name +#: boxes/generators/coindisplay.py +msgid "coin_showcase_h" +msgstr "" + +#. help for parameter coin_showcase_h +#: boxes/generators/coindisplay.py +msgid "The height of the coin showcase piece" +msgstr "" + +#. help for parameter angle +#: boxes/generators/coindisplay.py +msgid "The angle that the coin will tilt as" +msgstr "" + +#. name of box generator +#: boxes/generators/concaveknob.py +msgid "ConcaveKnob" +msgstr "" + +#. description of ConcaveKnob +#: boxes/generators/concaveknob.py +msgid "Round knob serrated outside for better gripping" +msgstr "" + +msgid "ConcaveKnob Settings" +msgstr "" + +#. help for parameter diameter +#: boxes/generators/concaveknob.py +msgid "Diameter of the knob (mm)" +msgstr "" + +#. parameter name +#: boxes/generators/concaveknob.py +msgid "serrations" +msgstr "" + +#. help for parameter serrations +#: boxes/generators/concaveknob.py +msgid "Number of serrations" +msgstr "" + +#. parameter name +#: boxes/generators/concaveknob.py +msgid "rounded" +msgstr "" + +#. help for parameter rounded +#: boxes/generators/concaveknob.py +msgid "Amount of circumference used for non convex parts" +msgstr "" + +#. help for parameter angle +#: boxes/generators/concaveknob.py +msgid "Angle between convex and concave parts" +msgstr "" + +#. parameter name +#: boxes/generators/concaveknob.py +msgid "bolthole" +msgstr "" + +#. help for parameter bolthole +#: boxes/generators/concaveknob.py +msgid "Diameter of the bolt hole (mm)" +msgstr "" + +#. parameter name +#: boxes/generators/concaveknob.py +msgid "dhole" +msgstr "" + +#. help for parameter dhole +#: boxes/generators/concaveknob.py +msgid "D-Flat in fraction of the diameter" +msgstr "" + +#. parameter name +#: boxes/generators/concaveknob.py +msgid "hexhead" +msgstr "" + +#. help for parameter hexhead +#: boxes/generators/concaveknob.py +msgid "Width of the hex bolt head (mm)" +msgstr "" + +#. name of box generator +#: boxes/generators/console.py +msgid "Console" +msgstr "" + +#. description of Console +#: boxes/generators/console.py +msgid "Console with slanted panel" +msgstr "" + +#. long description of Console in markdown +#: boxes/generators/console.py +msgid "" +"\n" +"\n" +"Console Arcade Stick\n" +"\n" +"![Front](static/samples/ConsoleArcadeStickFront.jpg)\n" +"![Back](static/samples/ConsoleArcadeStickBack.jpg)\n" +"![Inside](static/samples/ConsoleArcadeStickInside.jpg)\n" +"\n" +"Keyboard enclosure:\n" +msgstr "" + +msgid "Console Settings" +msgstr "" + +#. parameter name +#: boxes/generators/console.py +msgid "front_height" +msgstr "" + +#. help for parameter front_height +#: boxes/generators/console.py +msgid "height of the front below the panel (in mm)" +msgstr "" + +#. help for parameter angle +#: boxes/generators/console.py +msgid "angle of the front panel (90°=upright)" +msgstr "" + +#. name of box generator +#: boxes/generators/console2.py +msgid "Console2" +msgstr "" + +#. description of Console2 +#: boxes/generators/console2.py +msgid "Console with slanted panel and service hatches" +msgstr "" + +#. long description of Console2 in markdown +#: boxes/generators/console2.py +msgid "" +"\n" +"This box is designed as a housing for electronic projects. It has hatches " +"that can be re-opened with simple tools. It intentionally cannot be opened " +"with bare hands - if build with thin enough material.\n" +"\n" +"#### Caution\n" +"There is a chance that the latches of the back wall or the back wall itself " +"interfere with the front panel or it's mounting frame/lips. The generator " +"does not check for this. So depending on the variant chosen you might need " +"to make the box deeper (increase y parameter) or the panel angle steeper " +"(increase angle parameter) until there is enough room.\n" +"\n" +"It's also possible that the frame of the panel interferes with the floor if " +"the hi parameter is too small.\n" +"\n" +"#### Assembly instructions\n" +"The main body is easy to assemble by starting with the floor and then adding " +"the four walls and (if present) the top piece.\n" +"\n" +"If the back wall is removable you need to add the lips and latches. The U-" +"shaped clamps holding the latches in place need to be clued in place without " +"also gluing the latches themselves. Make sure the springs on the latches " +"point inwards and the angled ends point to the side walls as shown here:\n" +"\n" +"![Back wall details](static/samples/Console2-backwall-detail.jpg)\n" +"\n" +"If the panel is removable you need to add the springs with the tabs to the " +"side lips. This photo shows the variant which has the panel glued to the " +"frame:\n" +"\n" +"![Back wall details](static/samples/Console2-panel-detail.jpg)\n" +"\n" +"If space is tight you may consider not gluing the cross pieces in place and " +"remove them after the glue-up. This may prevent the latches of the back wall " +"and the panel from interfering with each other.\n" +"\n" +"The variant using finger joints only has the two side lips without the cross " +"bars.\n" +"\n" +"#### Re-Opening\n" +"\n" +"The latches at the back wall lock in place when closed. To open them they " +"need to be pressed in and can then be moved aside.\n" +"\n" +"To remove the panel you have to press in the four tabs at the side. It is " +"easiest to push them in and then pull the panel up a little bit so the tabs " +"stay in.\n" +msgstr "" + +msgid "Console2 Settings" +msgstr "" + +#. parameter name +#: boxes/generators/console2.py +msgid "removable_backwall" +msgstr "" + +#. help for parameter removable_backwall +#: boxes/generators/console2.py +msgid "have latches at the backwall" +msgstr "" + +#. parameter name +#: boxes/generators/console2.py +msgid "removable_panel" +msgstr "" + +#. help for parameter removable_panel +#: boxes/generators/console2.py +msgid "The panel is held by tabs and can be removed" +msgstr "" + +#. parameter name +#: boxes/generators/console2.py +msgid "glued_panel" +msgstr "" + +#. help for parameter glued_panel +#: boxes/generators/console2.py +msgid "the panel is glued and not held by finger joints" +msgstr "" + +#. name of box generator +#: boxes/generators/crypticfont.py +msgid "CrypticFont" +msgstr "" + +#. description of CrypticFont +#: boxes/generators/crypticfont.py +msgid "DESCRIPTION" +msgstr "" + +msgid "CrypticFont Settings" +msgstr "" + +#. parameter name +#: boxes/generators/crypticfont.py +msgid "ctext" +msgstr "" + +#. help for parameter ctext +#: boxes/generators/crypticfont.py +msgid "text to render" +msgstr "" + +msgid "Settings for the Cryptic Font" +msgstr "" + +#. parameter name for CrypticFont +#: boxes/generators/crypticfont.py +msgid "ratio" +msgstr "" + +#. help for parameter ratio +#: boxes/generators/crypticfont.py +msgid "ratio of height to width" +msgstr "" + +#. parameter name for CrypticFont +#: boxes/generators/crypticfont.py +msgid "spacing" +msgstr "" + +#. help for parameter width +#: boxes/generators/crypticfont.py +msgid "width of the glphys in mm" +msgstr "" + +#. name of box generator +#: boxes/generators/desksign.py +msgid "Desksign" +msgstr "" + +#. description of Desksign +#: boxes/generators/desksign.py +msgid "Simple diagonal plate with stands to show name or mesage" +msgstr "" + +#. long description of Desksign in markdown +#: boxes/generators/desksign.py +msgid "" +"Text to be engraved can be genarated by inputing the label and fontsize " +"fields.\n" +" height represents the area that can be used for writing " +"text, does not match the actual\n" +" height when standing. Generated text is put in the center. " +"Currently only a single\n" +" line of text is supported." +msgstr "" + +msgid "Desksign Settings" +msgstr "" + +#. help for parameter width +#: boxes/generators/desksign.py +msgid "plate width in mm (excluding holes)" +msgstr "" + +#. help for parameter height +#: boxes/generators/desksign.py +msgid "plate height in mm" +msgstr "" + +#. help for parameter angle +#: boxes/generators/desksign.py +msgid "plate angle in degrees (90 is vertical)" +msgstr "" + +#. parameter name +#: boxes/generators/desksign.py +msgid "label" +msgstr "" + +#. help for parameter label +#: boxes/generators/desksign.py +msgid "optional text to engrave (leave blank to omit)" +msgstr "" + +#. parameter name +#: boxes/generators/desksign.py +msgid "fontsize" +msgstr "" + +#. help for parameter fontsize +#: boxes/generators/desksign.py +msgid "height of text" +msgstr "" + +#. parameter name +#: boxes/generators/desksign.py +msgid "feet" +msgstr "" + +#. help for parameter feet +#: boxes/generators/desksign.py +msgid "add raised feet" +msgstr "" + +#. parameter name +#: boxes/generators/desksign.py +msgid "mirror" +msgstr "" + +#. help for parameter mirror +#: boxes/generators/desksign.py +msgid "" +"mirrors one of the stand so the same side of the material can be placed on " +"the outside" +msgstr "" + +#. name of box generator +#: boxes/generators/dicebox.py +msgid "DiceBox" +msgstr "" + +#. description of DiceBox +#: boxes/generators/dicebox.py +msgid "Box with lid and integraded hinge for storing dice" +msgstr "" + +msgid "DiceBox Settings" +msgstr "" + +#. parameter name +#: boxes/generators/dicebox.py +msgid "lidheight" +msgstr "" + +#. help for parameter lidheight +#: boxes/generators/dicebox.py +msgid "height of lid in mm" +msgstr "" + +#. parameter name +#: boxes/generators/dicebox.py +msgid "hex_hole_corner_radius" +msgstr "" + +#. help for parameter hex_hole_corner_radius +#: boxes/generators/dicebox.py +msgid "The corner radius of the hexagonal dice holes, in mm" +msgstr "" + +#. parameter name +#: boxes/generators/dicebox.py +msgid "magnet_diameter" +msgstr "" + +#. help for parameter magnet_diameter +#: boxes/generators/dicebox.py +msgid "The diameter of magnets for holding the box closed, in mm" +msgstr "" + +msgid "Settings for Chest Hinges" +msgstr "" + +#. parameter name for ChestHinge +#: boxes/generators/dicebox.py +msgid "finger_joints_on_box" +msgstr "" + +#. help for parameter finger_joints_on_box +#: boxes/generators/dicebox.py +msgid "whether to include finger joints on the edge with the box" +msgstr "" + +#. parameter name for ChestHinge +#: boxes/generators/dicebox.py +msgid "finger_joints_on_lid" +msgstr "" + +#. help for parameter finger_joints_on_lid +#: boxes/generators/dicebox.py +msgid "whether to include finger joints on the edge with the lid" +msgstr "" + +#. parameter name for ChestHinge +#: boxes/generators/dicebox.py +msgid "hinge_strength" +msgstr "" + +#. parameter name for ChestHinge +#: boxes/generators/dicebox.py +msgid "pin_height" +msgstr "" + +#. help for parameter pin_height +#: boxes/generators/dicebox.py +msgid "radius of the disc rotating in the hinge (multiples of thickness)" +msgstr "" + +#. name of box generator +#: boxes/generators/dinrailbox.py +msgid "DinRailBox" +msgstr "" + +#. description of DinRailBox +#: boxes/generators/dinrailbox.py +msgid "Box for DIN rail used in electrical junction boxes" +msgstr "" + +msgid "DinRailBox Settings" +msgstr "" + +#. parameter name +#: boxes/generators/dinrailbox.py +msgid "rail_width" +msgstr "" + +#. help for parameter rail_width +#: boxes/generators/dinrailbox.py +msgid "width of the rail (typically 35 or 15mm)" +msgstr "" + +#. parameter name +#: boxes/generators/dinrailbox.py +msgid "rail_offset" +msgstr "" + +#. help for parameter rail_offset +#: boxes/generators/dinrailbox.py +msgid "offset of the rail from the middle of the box (in mm)" +msgstr "" + +#. name of box generator +#: boxes/generators/discrack.py +msgid "DiscRack" +msgstr "" + +#. description of DiscRack +#: boxes/generators/discrack.py +msgid "A rack for storing disk-shaped objects vertically next to each other" +msgstr "" + +msgid "DiscRack Settings" +msgstr "" + +#. parameter name +#: boxes/generators/discrack.py +msgid "disc_diameter" +msgstr "" + +#. help for parameter disc_diameter +#: boxes/generators/discrack.py +msgid "Disc diameter in mm" +msgstr "" + +#. parameter name +#: boxes/generators/discrack.py +msgid "disc_thickness" +msgstr "" + +#. help for parameter disc_thickness +#: boxes/generators/discrack.py +msgid "Thickness of the discs in mm" +msgstr "" + +#. parameter name +#: boxes/generators/discrack.py +msgid "lower_factor" +msgstr "" + +#. help for parameter lower_factor +#: boxes/generators/discrack.py +msgid "Position of the lower rack grids along the radius" +msgstr "" + +#. parameter name +#: boxes/generators/discrack.py +msgid "rear_factor" +msgstr "" + +#. help for parameter rear_factor +#: boxes/generators/discrack.py +msgid "Position of the rear rack grids along the radius" +msgstr "" + +#. parameter name +#: boxes/generators/discrack.py +msgid "disc_outset" +msgstr "" + +#. help for parameter disc_outset +#: boxes/generators/discrack.py +msgid "Additional space kept between the disks and the outbox of the rack" +msgstr "" + +#. help for parameter angle +#: boxes/generators/discrack.py +msgid "Backwards slant of the rack" +msgstr "" + +#. name of box generator +#: boxes/generators/dispenser.py +msgid "Dispenser" +msgstr "" + +#. description of Dispenser +#: boxes/generators/dispenser.py +msgid "Dispenser for stackable (flat) items of same size" +msgstr "" + +#. long description of Dispenser in markdown +#: boxes/generators/dispenser.py +msgid "" +"Set *bottomheight* to 0 for a wall mounting variant.\n" +"Please add mounting holes yourself." +msgstr "" + +msgid "Dispenser Settings" +msgstr "" + +#. parameter name +#: boxes/generators/dispenser.py +msgid "slotheight" +msgstr "" + +#. help for parameter slotheight +#: boxes/generators/dispenser.py +msgid "height of the dispenser slot / items (in mm)" +msgstr "" + +#. parameter name +#: boxes/generators/dispenser.py +msgid "bottomheight" +msgstr "" + +#. help for parameter bottomheight +#: boxes/generators/dispenser.py +msgid "height underneath the dispenser (in mm)" +msgstr "" + +#. parameter name +#: boxes/generators/dispenser.py +msgid "sideedges" +msgstr "" + +#. help for parameter sideedges +#: boxes/generators/dispenser.py +msgid "edges used for holding the front panels and back" +msgstr "" + +#. name of box generator +#: boxes/generators/display.py +msgid "Display" +msgstr "" + +#. description of Display +#: boxes/generators/display.py +msgid "Display for flyers or leaflets" +msgstr "" + +msgid "Display Settings" +msgstr "" + +#. help for parameter radius +#: boxes/generators/display.py +msgid "radius of the corners in mm" +msgstr "" + +#. help for parameter angle +#: boxes/generators/display.py +msgid "greater zero for top wider as bottom" +msgstr "" + +#. name of box generator +#: boxes/generators/displaycase.py +msgid "DisplayCase" +msgstr "" + +#. description of DisplayCase +#: boxes/generators/displaycase.py +msgid "" +"Fully closed box intended to be cut from transparent acrylics and to serve " +"as a display case." +msgstr "" + +msgid "DisplayCase Settings" +msgstr "" + +#. parameter name +#: boxes/generators/displaycase.py +msgid "overhang" +msgstr "" + +#. help for parameter overhang +#: boxes/generators/displaycase.py +msgid "overhang for joints in mm" +msgstr "" + +#. name of box generator +#: boxes/generators/displayshelf.py +msgid "DisplayShelf" +msgstr "" + +#. description of DisplayShelf +#: boxes/generators/displayshelf.py +msgid "Shelf with slanted floors" +msgstr "" + +msgid "DisplayShelf Settings" +msgstr "" + +#. parameter name +#: boxes/generators/displayshelf.py +msgid "num" +msgstr "" + +#. help for parameter num +#: boxes/generators/displayshelf.py +msgid "number of shelves" +msgstr "" + +#. parameter name +#: boxes/generators/displayshelf.py +msgid "front_wall_height" +msgstr "" + +#. help for parameter front_wall_height +#: boxes/generators/displayshelf.py +msgid "height of front walls" +msgstr "" + +#. help for parameter angle +#: boxes/generators/displayshelf.py +msgid "angle of floors (negative values for slanting backwards)" +msgstr "" + +#. parameter name +#: boxes/generators/displayshelf.py +msgid "include_back" +msgstr "" + +#. help for parameter include_back +#: boxes/generators/displayshelf.py +msgid "Include panel on the back of the shelf" +msgstr "" + +#. parameter name +#: boxes/generators/displayshelf.py +msgid "slope_top" +msgstr "" + +#. help for parameter slope_top +#: boxes/generators/displayshelf.py +msgid "Slope the sides and the top by front wall height" +msgstr "" + +#. parameter name +#: boxes/generators/displayshelf.py +msgid "divider_wall_height" +msgstr "" + +#. help for parameter divider_wall_height +#: boxes/generators/displayshelf.py +msgid "height of divider walls" +msgstr "" + +#. name of box generator +#: boxes/generators/dividertray.py +msgid "DividerTray" +msgstr "" + +#. description of DividerTray +#: boxes/generators/dividertray.py +msgid "Divider tray - rows and dividers" +msgstr "" + +#. long description of DividerTray in markdown +#: boxes/generators/dividertray.py +msgid "" +"\n" +"Adding '0:' at the start of the sy parameter adds a slot at the very back. " +"Adding ':0' at the end of sy adds a slot meeting the bottom at the very " +"front. This is especially useful if slot angle is set above zero.\n" +"\n" +"There are 4 different sets of dividers rendered:\n" +"\n" +"* With asymmetric tabs so the tabs fit on top of each other\n" +"* With tabs of half wall thickness that can go side by side\n" +"* With tabs of a full wall thickness\n" +"* One single divider spanning across all columns\n" +"\n" +"You will likely need to cut each of the dividers you want multiple times.\n" +msgstr "" + +msgid "DividerTray Settings" +msgstr "" + +#. parameter name +#: boxes/generators/dividertray.py +msgid "notches_in_wall" +msgstr "" + +#. help for parameter notches_in_wall +#: boxes/generators/dividertray.py +msgid "generate the same notches on the walls that are on the dividers" +msgstr "" + +#. parameter name +#: boxes/generators/dividertray.py +msgid "left_wall" +msgstr "" + +#. help for parameter left_wall +#: boxes/generators/dividertray.py +msgid "generate wall on the left side" +msgstr "" + +#. parameter name +#: boxes/generators/dividertray.py +msgid "right_wall" +msgstr "" + +#. help for parameter right_wall +#: boxes/generators/dividertray.py +msgid "generate wall on the right side" +msgstr "" + +#. parameter name +#: boxes/generators/dividertray.py +msgid "bottom" +msgstr "" + +#. help for parameter bottom +#: boxes/generators/dividertray.py +msgid "generate wall on the bottom" +msgstr "" + +#. help for parameter handle +#: boxes/generators/dividertray.py +msgid "add handle to the bottom" +msgstr "" + +msgid "Settings for Divider Slots" +msgstr "" + +#. help for parameter angle +#: boxes/generators/dividertray.py +msgid "angle at which slots are generated, in degrees. 0° is vertical." +msgstr "" + +#. help for parameter depth +#: boxes/generators/dividertray.py +msgid "depth of the slot in mm" +msgstr "" + +#. parameter name for Slot +#: boxes/generators/dividertray.py +msgid "extra_slack" +msgstr "" + +#. help for parameter extra_slack +#: boxes/generators/dividertray.py +msgid "" +"extra slack (in addition to thickness and kerf) to help insert dividers in mm" +msgstr "" + +#. help for parameter radius +#: boxes/generators/dividertray.py +msgid "radius of the slot entrance in mm" +msgstr "" + +msgid "Settings for Notches on the Dividers" +msgstr "" + +#. parameter name for Notch +#: boxes/generators/dividertray.py +msgid "lower_radius" +msgstr "" + +#. parameter name for Notch +#: boxes/generators/dividertray.py +msgid "upper_radius" +msgstr "" + +msgid "Settings for Dividers" +msgstr "" + +#. parameter name for Divider +#: boxes/generators/dividertray.py +msgid "bottom_margin" +msgstr "" + +#. help for parameter bottom_margin +#: boxes/generators/dividertray.py +msgid "margin between box's bottom and divider's in mm" +msgstr "" + +#. help for parameter play +#: boxes/generators/dividertray.py +msgid "play to avoid them clamping onto the walls (in multiples of thickness)" +msgstr "" + +#. name of box generator +#: boxes/generators/doubleflexdoorbox.py +msgid "DoubleFlexDoorBox" +msgstr "" + +#. description of DoubleFlexDoorBox +#: boxes/generators/doubleflexdoorbox.py +msgid "Box with two part lid with living hinges and round corners" +msgstr "" + +msgid "DoubleFlexDoorBox Settings" +msgstr "" + +#. help for parameter radius +#: boxes/generators/doubleflexdoorbox.py +msgid "Radius of the latch in mm" +msgstr "" + +#. parameter name +#: boxes/generators/doubleflexdoorbox.py +msgid "latchsize" +msgstr "" + +#. help for parameter latchsize +#: boxes/generators/doubleflexdoorbox.py +msgid "size of latch in multiples of thickness" +msgstr "" + +#. name of box generator +#: boxes/generators/drillbox.py +msgid "DrillBox" +msgstr "" + +#. description of DrillBox +#: boxes/generators/drillbox.py +msgid "A parametrized box for drills" +msgstr "" + +#. long description of DrillBox in markdown +#: boxes/generators/drillbox.py +msgid "![Multiple DrillBoxes](static/samples/DrillBoxes.jpg) " +msgstr "" + +msgid "DrillBox Settings" +msgstr "" + +#. parameter name +#: boxes/generators/drillbox.py +msgid "holes" +msgstr "" + +#. help for parameter holes +#: boxes/generators/drillbox.py +msgid "Number of holes for each size" +msgstr "" + +#. parameter name +#: boxes/generators/drillbox.py +msgid "firsthole" +msgstr "" + +#. help for parameter firsthole +#: boxes/generators/drillbox.py +msgid "Smallest hole" +msgstr "" + +#. parameter name +#: boxes/generators/drillbox.py +msgid "holeincrement" +msgstr "" + +#. help for parameter holeincrement +#: boxes/generators/drillbox.py +msgid "increment between holes" +msgstr "" + +msgid "Settings for RoundedTriangleEdge" +msgstr "" + +#. help for parameter height +#: boxes/generators/drillbox.py +msgid "height above the wall" +msgstr "" + +#. parameter name for RoundedTriangleEdge +#: boxes/generators/drillbox.py +msgid "r_hole" +msgstr "" + +#. help for parameter r_hole +#: boxes/generators/drillbox.py +msgid "radius of hole" +msgstr "" + +#. help for parameter radius +#: boxes/generators/drillbox.py +msgid "radius of top corner" +msgstr "" + +#. help for parameter outset +#: boxes/generators/drillbox.py +msgid "" +"extend the triangle along the length of the edge (multiples of thickness)" +msgstr "" + +msgid "Settings for Mounting Edge" +msgstr "" + +#. parameter name for Mounting +#: boxes/generators/drillbox.py +msgid "d_head" +msgstr "" + +#. help for parameter d_head +#: boxes/generators/drillbox.py +msgid "head diameter of mounting screw (in mm)" +msgstr "" + +#. parameter name for Mounting +#: boxes/generators/drillbox.py +msgid "d_shaft" +msgstr "" + +#. help for parameter d_shaft +#: boxes/generators/drillbox.py +msgid "shaft diameter of mounting screw (in mm)" +msgstr "" + +#. help for parameter margin +#: boxes/generators/drillbox.py +msgid "" +"minimum space left and right without holes (fraction of the edge length)" +msgstr "" + +#. help for parameter num +#: boxes/generators/drillbox.py +msgid "number of mounting holes (integer)" +msgstr "" + +#. parameter name for Mounting +#: boxes/generators/drillbox.py +msgid "side" +msgstr "" + +#. help for parameter side +#: boxes/generators/drillbox.py +msgid "side of box (not all valid configurations make sense...)" +msgstr "" + +#. possible choice for side +#: boxes/generators/drillbox.py +msgid "back" +msgstr "" + +#. help for parameter style +#: boxes/generators/drillbox.py +msgid "edge style" +msgstr "" + +#. possible choice for style +#: boxes/generators/drillbox.py +msgid "straight edge, within" +msgstr "" + +#. possible choice for style +#: boxes/generators/drillbox.py +msgid "straight edge, extended" +msgstr "" + +#. possible choice for style +#: boxes/generators/drillbox.py +msgid "mounting tab" +msgstr "" + +#. name of box generator +#: boxes/generators/drillstand.py +msgid "DrillStand" +msgstr "" + +#. description of DrillStand +#: boxes/generators/drillstand.py +msgid "Box for drills with each compartment of a different height" +msgstr "" + +#. long description of DrillStand in markdown +#: boxes/generators/drillstand.py +msgid "" +"Note: `sh` gives the hight of the rows front to back. It though should have " +"the same number of entries as `sy`. These heights are the one on the left " +"side and increase throughout the row. To have each compartment a bit higher " +"than the previous one the steps in `sh` should be a bit bigger than " +"`extra_height`.\n" +"\n" +"Assembly:\n" +"\n" +"![Parts](static/samples/DrillStand-drawing.png)\n" +"\n" +"Start with putting the slots of the inner walls together. Be especially " +"careful with adding the bottom. It is always asymmetrical and flush with the " +"right/lower side while being a little short on the left/higher side to not " +"protrude into the side wall.\n" +"\n" +"| | |\n" +"| ---- | ---- |\n" +"| ![Assembly inner walls](static/samples/DrillStand-assembly-1.jpg) | !" +"[Assembly bottom](static/samples/DrillStand-assembly-2.jpg) |\n" +"| Then add the front and the back wall. | Add the very left and right walls " +"last. |\n" +"| ![Assembly front and back](static/samples/DrillStand-assembly-3.jpg) | !" +"[Assembly side walls](static/samples/DrillStand-assembly-4.jpg) |\n" +msgstr "" + +msgid "DrillStand Settings" +msgstr "" + +#. parameter name +#: boxes/generators/drillstand.py +msgid "extra_height" +msgstr "" + +#. help for parameter extra_height +#: boxes/generators/drillstand.py +msgid "height difference left to right" +msgstr "" + +#. name of box generator +#: boxes/generators/electronicsbox.py +msgid "ElectronicsBox" +msgstr "" + +#. description of ElectronicsBox +#: boxes/generators/electronicsbox.py +msgid "Closed box with screw on top and mounting holes" +msgstr "" + +msgid "ElectronicsBox Settings" +msgstr "" + +#. help for parameter triangle +#: boxes/generators/electronicsbox.py +msgid "Sides of the triangles holding the lid in mm" +msgstr "" + +#. parameter name +#: boxes/generators/electronicsbox.py +msgid "d1" +msgstr "" + +#. help for parameter d1 +#: boxes/generators/electronicsbox.py +msgid "Diameter of the inner lid screw holes in mm" +msgstr "" + +#. parameter name +#: boxes/generators/electronicsbox.py +msgid "d2" +msgstr "" + +#. help for parameter d2 +#: boxes/generators/electronicsbox.py +msgid "Diameter of the lid screw holes in mm" +msgstr "" + +#. parameter name +#: boxes/generators/electronicsbox.py +msgid "d3" +msgstr "" + +#. help for parameter d3 +#: boxes/generators/electronicsbox.py +msgid "Diameter of the mounting screw holes in mm" +msgstr "" + +#. parameter name +#: boxes/generators/electronicsbox.py +msgid "outsidemounts" +msgstr "" + +#. help for parameter outsidemounts +#: boxes/generators/electronicsbox.py +msgid "Add external mounting points" +msgstr "" + +#. parameter name +#: boxes/generators/electronicsbox.py +msgid "holedist" +msgstr "" + +#. help for parameter holedist +#: boxes/generators/electronicsbox.py +msgid "Distance of the screw holes from the wall in mm" +msgstr "" + +#. name of box generator +#: boxes/generators/eurorackskiff.py +msgid "EuroRackSkiff" +msgstr "" + +#. description of EuroRackSkiff +#: boxes/generators/eurorackskiff.py +msgid "3U Height case with adjustable width and height and included rails" +msgstr "" + +msgid "EuroRackSkiff Settings" +msgstr "" + +#. parameter name +#: boxes/generators/eurorackskiff.py +msgid "hp" +msgstr "" + +#. help for parameter hp +#: boxes/generators/eurorackskiff.py +msgid "Width of the case in HP" +msgstr "" + +#. name of box generator +#: boxes/generators/fanhole.py +msgid "FanHole" +msgstr "" + +#. description of FanHole +#: boxes/generators/fanhole.py +msgid "Hole pattern for mounting a fan" +msgstr "" + +msgid "FanHole Settings" +msgstr "" + +#. help for parameter diameter +#: boxes/generators/fanhole.py +msgid "diameter of the fan hole" +msgstr "" + +#. parameter name +#: boxes/generators/fanhole.py +msgid "mounting_holes" +msgstr "" + +#. help for parameter mounting_holes +#: boxes/generators/fanhole.py +msgid "diameter of the fan mounting holes" +msgstr "" + +#. parameter name +#: boxes/generators/fanhole.py +msgid "mounting_holes_inset" +msgstr "" + +#. help for parameter mounting_holes_inset +#: boxes/generators/fanhole.py +msgid "distance of the fan mounting holes from the outside" +msgstr "" + +#. parameter name +#: boxes/generators/fanhole.py +msgid "arms" +msgstr "" + +#. help for parameter arms +#: boxes/generators/fanhole.py +msgid "number of arms" +msgstr "" + +#. parameter name +#: boxes/generators/fanhole.py +msgid "inner_disc" +msgstr "" + +#. help for parameter inner_disc +#: boxes/generators/fanhole.py +msgid "relative size of the inner disc" +msgstr "" + +#. help for parameter style +#: boxes/generators/fanhole.py +msgid "Style of the fan hole" +msgstr "" + +#. possible choice for style +#: boxes/generators/fanhole.py +msgid "CW Swirl" +msgstr "" + +#. possible choice for style +#: boxes/generators/fanhole.py +msgid "CCW Swirl" +msgstr "" + +#. possible choice for style +#: boxes/generators/fanhole.py +msgid "Hole" +msgstr "" + +#. name of box generator +#: boxes/generators/filamentspool.py +msgid "FilamentSpool" +msgstr "" + +#. description of FilamentSpool +#: boxes/generators/filamentspool.py +msgid "A two part spool for 3D printing filament" +msgstr "" + +#. long description of FilamentSpool in markdown +#: boxes/generators/filamentspool.py +msgid "" +"\n" +"Use small nails to properly align the pieces of the bayonet latch. Glue the " +"parts of the bayonet latch before assembling the \"axle\". The inner parts " +"go at the side and the outer parts at the inside of the axle.\n" +"![opened spool](static/samples/FilamentSpool-2.jpg)" +msgstr "" + +msgid "FilamentSpool Settings" +msgstr "" + +#. parameter name +#: boxes/generators/filamentspool.py +msgid "outer_diameter" +msgstr "" + +#. help for parameter outer_diameter +#: boxes/generators/filamentspool.py +msgid "diameter of the flanges" +msgstr "" + +#. parameter name +#: boxes/generators/filamentspool.py +msgid "inner_diameter" +msgstr "" + +#. help for parameter inner_diameter +#: boxes/generators/filamentspool.py +msgid "diameter of the center part" +msgstr "" + +#. parameter name +#: boxes/generators/filamentspool.py +msgid "axle_diameter" +msgstr "" + +#. help for parameter axle_diameter +#: boxes/generators/filamentspool.py +msgid "diameter of the axle hole" +msgstr "" + +#. parameter name +#: boxes/generators/filamentspool.py +msgid "sides" +msgstr "" + +#. help for parameter sides +#: boxes/generators/filamentspool.py +msgid "number of pieces for the center part" +msgstr "" + +#. name of box generator +#: boxes/generators/filltest.py +msgid "FillTest" +msgstr "" + +#. description of FillTest +#: boxes/generators/filltest.py +msgid "Piece for testing different settings for hole filling" +msgstr "" + +msgid "FillTest Settings" +msgstr "" + +#. description of FlexBox +#: boxes/generators/flexbox.py +msgid "Box with living hinge and round corners" +msgstr "" + +msgid "FlexBox Settings" +msgstr "" + +#. name of box generator +#: boxes/generators/flexbox2.py +msgid "FlexBox2" +msgstr "" + +#. description of FlexBox2 +#: boxes/generators/flexbox2.py +msgid "Box with living hinge and top corners rounded" +msgstr "" + +msgid "FlexBox2 Settings" +msgstr "" + +#. help for parameter radius +#: boxes/generators/flexbox2.py +msgid "Radius of the corners in mm" +msgstr "" + +#. name of box generator +#: boxes/generators/flexbox3.py +msgid "FlexBox3" +msgstr "" + +#. description of FlexBox3 +#: boxes/generators/flexbox3.py +msgid "Box with living hinge" +msgstr "" + +msgid "FlexBox3 Settings" +msgstr "" + +#. parameter name +#: boxes/generators/flexbox3.py +msgid "z" +msgstr "" + +#. help for parameter z +#: boxes/generators/flexbox3.py +msgid "height of the box" +msgstr "" + +#. help for parameter h +#: boxes/generators/flexbox3.py +msgid "height of the lid" +msgstr "" + +#. help for parameter radius +#: boxes/generators/flexbox3.py +msgid "radius of the lids living hinge" +msgstr "" + +#. help for parameter c +#: boxes/generators/flexbox3.py +msgid "clearance of the lid" +msgstr "" + +#. name of box generator +#: boxes/generators/flexbox4.py +msgid "FlexBox4" +msgstr "" + +#. description of FlexBox4 +#: boxes/generators/flexbox4.py +msgid "Box with living hinge and left corners rounded" +msgstr "" + +msgid "FlexBox4 Settings" +msgstr "" + +#. name of box generator +#: boxes/generators/flexbox5.py +msgid "FlexBox5" +msgstr "" + +msgid "FlexBox5 Settings" +msgstr "" + +#. parameter name +#: boxes/generators/flexbox5.py +msgid "top_diameter" +msgstr "" + +#. help for parameter top_diameter +#: boxes/generators/flexbox5.py +msgid "diameter at the top" +msgstr "" + +#. parameter name +#: boxes/generators/flexbox5.py +msgid "bottom_diameter" +msgstr "" + +#. help for parameter bottom_diameter +#: boxes/generators/flexbox5.py +msgid "diameter at the bottom" +msgstr "" + +#. name of box generator +#: boxes/generators/flextest.py +msgid "FlexTest" +msgstr "" + +#. description of FlexTest +#: boxes/generators/flextest.py +msgid "Piece for testing different flex settings" +msgstr "" + +msgid "FlexTest Settings" +msgstr "" + +#. name of box generator +#: boxes/generators/flextest2.py +msgid "FlexTest2" +msgstr "" + +#. description of FlexTest2 +#: boxes/generators/flextest2.py +msgid "Piece for testing 2D flex settings" +msgstr "" + +msgid "FlexTest2 Settings" +msgstr "" + +#. parameter name +#: boxes/generators/flextest2.py +msgid "fw" +msgstr "" + +#. help for parameter fw +#: boxes/generators/flextest2.py +msgid "distance of flex cuts in multiples of thickness" +msgstr "" + +#. name of box generator +#: boxes/generators/folder.py +msgid "Folder" +msgstr "" + +#. description of Folder +#: boxes/generators/folder.py +msgid "Book cover with flex for the spine" +msgstr "" + +msgid "Folder Settings" +msgstr "" + +#. parameter name +#: boxes/generators/folder.py +msgid "r" +msgstr "" + +#. name of box generator +#: boxes/generators/frontpanel.py +msgid "FrontPanel" +msgstr "" + +#. description of FrontPanel +#: boxes/generators/frontpanel.py +msgid "Mounting Holes and cutouts for all your holy needs." +msgstr "" + +#. long description of FrontPanel in markdown +#: boxes/generators/frontpanel.py +msgid "" +"\n" +" -""" % self.groups_by_name[box.ui_group].title) +""") return b''.join(s.encode("utf-8") for s in result) def writeINX(self, name, box, path): @@ -146,8 +139,14 @@ class Boxes2INX: continue self.writeINX(name, box, path) -if __name__=="__main__": + +def main() -> None: if len(sys.argv) != 2: - print("Usage: boxes2inksacpe TARGETPATH") + print("Usage: boxes2inkscape TARGETPATH") + return b = Boxes2INX() b.writeAllINX(sys.argv[1]) + + +if __name__ == "__main__": + main() diff --git a/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxes2pot b/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxes2pot index 438601f..9159969 100755 --- a/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxes2pot +++ b/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxes2pot @@ -13,36 +13,41 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from __future__ import annotations -import sys -import os.path import argparse +import os.path +import sys +from typing import Any try: import boxes.generators except ImportError: sys.path.append(os.path.dirname(__file__) + "/..") import boxes.generators -from boxes import edges +from boxes import edges, lids + class DefaultParams(boxes.Boxes): - def __init__(self): + def __init__(self) -> None: boxes.Boxes.__init__(self) self.buildArgParser("x", "y", "h", "hi", "sx", "sy", "sh", "bottom_edge", "top_edge", "outside", "nema_mount") - self.addSettingsArgs(edges.FingerJointSettings, finger=1.0,space=1.0) + self.addSettingsArgs(edges.FingerJointSettings, finger=1.0, space=1.0) self.addSettingsArgs(edges.StackableSettings) self.addSettingsArgs(edges.HingeSettings) - self.addSettingsArgs(edges.LidSettings) + self.addSettingsArgs(edges.SlideOnLidSettings) self.addSettingsArgs(edges.ClickSettings) self.addSettingsArgs(edges.FlexSettings) + self.addSettingsArgs(lids.LidSettings) + class Boxes2pot: - def __init__(self): - self.messages = [] - self.message_set = set() - self.boxes = {b.__name__ : b() for b in boxes.generators.getAllBoxGenerators().values() if b.webinterface} + def __init__(self) -> None: + self.messages: list[Any] = [] + self.message_set: set[Any] = set() + self.boxes = {b.__name__: b() for b in boxes.generators.getAllBoxGenerators().values() if b.webinterface} self.groups = boxes.generators.ui_groups self.groups_by_name = boxes.generators.ui_groups_by_name self._readEdgeDescriptions() @@ -60,8 +65,7 @@ class Boxes2pot: def _readEdgeDescriptions(self): for char, descr in edges.getDescriptions().items(): self.add(descr, f"{char} edge description", "boxes/edges.py") - self.add(f"{char} {descr}", f"{char} edge description", - "boxes/edges.py") + self.add(f"{char} {descr}", f"{char} edge description", "boxes/edges.py") def addBoxParams(self, name, box, location=None): for group in box.argparser._action_groups: @@ -76,7 +80,7 @@ class Boxes2pot: prefix = getattr(group, "prefix", "") name = a.option_strings[0].replace("-", "") if prefix and name.startswith(prefix + '_'): - name = name[len(prefix)+1:] + name = name[len(prefix) + 1:] self.add(name, "parameter name for " + prefix, location) else: self.add(name, "parameter name", location) @@ -100,7 +104,7 @@ class Boxes2pot: if box.__doc__: self.add(box.__doc__, "description of " + name, location) if box.description: - self.add(box.description, "long description of "+ name + " in markdown", location) + self.add(box.description, "long description of " + name + " in markdown", location) self.addBoxParams(name, box, location) def writePOT(self, fn): @@ -117,7 +121,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" """) - + for msg, comment, reference in self.messages: f.write("\n") if comment: @@ -125,16 +129,22 @@ msgstr "" if reference: f.write("#: %s\n" % reference) msg = msg.split("\n") - for i in range(len(msg)-1): + for i in range(len(msg) - 1): msg[i] += "\\n" f.write('msgid ') for m in msg: f.write(' "%s"\n' % m.replace('"', '\\"')) f.write('msgstr ""\n') -if __name__=="__main__": + +def main() -> None: if len(sys.argv) != 2: - print("Usage: boxes2inksacpe TARGETPATH") + print("Usage: boxes2pot TARGETPATH") + return b = Boxes2pot() b.readBoxes() b.writePOT(sys.argv[1]) + + +if __name__ == "__main__": + main() diff --git a/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxes_example.ipynb b/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxes_example.ipynb index 339fcbf..e451b80 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxes_example.ipynb +++ b/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxes_example.ipynb @@ -18,10 +18,9 @@ "outputs": [], "source": [ "from IPython.display import SVG, display\n", - "import tempfile, os\n", "\n", "import sys\n", - "#sys.path.append('..') # uncomments and adjust if your Boxes.py copy in not in the Python path\n", + "# sys.path.append('..') # uncomments and adjust if your Boxes.py copy in not in the Python path\n", "from boxes import *" ] }, @@ -35,35 +34,55 @@ { "data": { "image/svg+xml": [ - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "Example\n", + "\n", + "\n", + "Shelves - Example\n", + "2024-02-11 17:45:59\n", + "boxes Example --reference=0 --debug=0\n", + "Example: Single Shelve to screw to the wall\n", + "\n", + "Created with Boxes.py (https://festi.info/boxes.py)\n", + "Command line: boxes Example --reference=0 --debug=0\n", + "Command line short: boxes Example --reference=0\n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "\n", + " \n", + " \n", "\n", "" ], @@ -110,17 +129,21 @@ " \n", "\n", "b = Example()\n", - "fd, fn = tempfile.mkstemp()\n", - "b.parseArgs(['--reference=0', '--debug=0', '--output=' + fn])\n", + "b.parseArgs(['--reference=0', '--debug=0'])\n", "b.open()\n", "b.render()\n", - "b.close()\n", + "data = b.close()\n", "\n", - "display(SVG(fn))\n", - "os.close(fd)\n", - "os.remove(fn)" + "display(SVG(data=data.getvalue()))" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, @@ -131,7 +154,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -145,9 +168,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.8" + "version": "3.12.1" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxes_proxy.py b/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxes_proxy.py new file mode 100755 index 0000000..0800ab3 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxes_proxy.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 + +""" +Extension for InkScape 1.0+ + +boxes.py wrapper script to make it work on Windows and Linux systems without duplicating .inx files + +Author: Mario Voigt / FabLab Chemnitz +Mail: mario.voigt@stadtfabrikanten.org +Date: 27.04.2021 +Last patch: 27.04.2021 +License: GNU GPL v3 + +""" +pass +import subprocess +import sys + +pass +from shlex import quote + +from inkex.extensions import GenerateExtension +from lxml import etree + +import inkex + + +class boxesPyWrapper(GenerateExtension): + def add_arguments(self, pars): + args = sys.argv[1:] + for arg in args: + key = arg.split("=")[0] + if key == "--id": + continue + if len(arg.split("=")) == 2: + value = arg.split("=")[1] + pars.add_argument(key, default=key) + + def generate(self): + cmd = "boxes" # boxes.exe in this local dir (or if present in %PATH%), or boxes from $PATH in linux + for arg in vars(self.options): + if arg in ( + "output", "id", "ids", "selected_nodes", + "input_file", "tab"): + continue + # fix behaviour of "original" arg which does not correctly gets + # interpreted if set to false + if arg == "original" and str(getattr(self.options, arg)) == "false": + continue + cmd += f" --{arg} {quote(str(getattr(self.options, arg)))}" + cmd += f" --output -" + cmd = cmd.replace("boxes --generator", "boxes") + + # run boxes with the parameters provided + result = subprocess.run(cmd.split(), capture_output=True) + + if result.returncode: + inkex.utils.debug("Generating box svg failed. Cannot continue. Command was:") + inkex.utils.debug(str(cmd)) + inkex.utils.debug(str(result.stderr)) + exit(1) + + # write the generated SVG into Inkscape's canvas + p = etree.XMLParser(huge_tree=True) + doc = etree.fromstring(result.stdout, parser=etree.XMLParser(huge_tree=True)) + group = inkex.Group(id="boxes.py") + for element in doc: + group.append(element) + return group + + +def main() -> None: + boxesPyWrapper().run() + + +if __name__ == '__main__': + main() diff --git a/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxesserver b/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxesserver index 16123b7..ef4e862 100755 --- a/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxesserver +++ b/extensions/fablabchemnitz/boxes.py/boxes/scripts/boxesserver @@ -13,26 +13,26 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from __future__ import annotations -import sys import argparse -import html -import tempfile -import os.path -import threading -import time -import codecs -import mimetypes -import re -import markdown import gettext import glob +import html +import io +import mimetypes +import os.path +import re +import sys +import threading +import time import traceback -from urllib.parse import unquote_plus, quote -from urllib.parse import parse_qs -from wsgiref.util import setup_testing_defaults +from typing import Any, NoReturn +from urllib.parse import quote, unquote_plus from wsgiref.simple_server import make_server -import wsgiref.util + +import markdown +import qrcode try: import boxes.generators @@ -40,9 +40,10 @@ except ImportError: sys.path.append(os.path.join(os.path.dirname(__file__), "..")) import boxes.generators + class FileChecker(threading.Thread): - def __init__(self, files=[], checkmodules=True): - super(FileChecker, self).__init__() + def __init__(self, files=[], checkmodules: bool = True) -> None: + super().__init__() self.checkmodules = checkmodules self.timestamps = {} self._stopped = False @@ -51,15 +52,15 @@ class FileChecker(threading.Thread): if checkmodules: self._addModules() - def _addModules(self): + def _addModules(self) -> None: for name, module in sys.modules.items(): path = getattr(module, "__file__", None) if not path: continue if path not in self.timestamps: - self.timestamps[path] = os.stat(path).st_mtime + self.timestamps[path] = os.stat(path).st_mtime - def filesOK(self): + def filesOK(self) -> bool: if self.checkmodules: self._addModules() for path, timestamp in self.timestamps.items(): @@ -70,38 +71,68 @@ class FileChecker(threading.Thread): return False return True - def run(self): + def run(self) -> None: while not self._stopped: if not self.filesOK(): os.execv(__file__, sys.argv) time.sleep(1) - def stop(self): + def stop(self) -> None: self._stopped = True + +def filter_url(url, non_default_args): + if len(url) == 0: + return '' + try: + base, args = url.split('?') + except ValueError: + return '' + args = args.split('&') + new_args = [] + args_to_ignore = ["qr_code", "format"] + for arg in args: + a, b = arg.split('=') + if a.strip() in args_to_ignore: + continue + if a in non_default_args: + new_args.append(arg) + if len(new_args): + return f"{base}?{'&'.join(new_args)}" + else: + return f"{base}" + + class ArgumentParserError(Exception): pass + class ThrowingArgumentParser(argparse.ArgumentParser): - def error(self, message): + def error(self, message) -> NoReturn: raise ArgumentParserError(message) -boxes.ArgumentParser = ThrowingArgumentParser # Evil hack + + +# Evil hack +boxes.ArgumentParser = ThrowingArgumentParser # type: ignore + class BServer: - lang_re = re.compile(r"([a-z]{2,3}(-[-a-zA-Z0-9]*)?)\s*(;\s*q=(\d\.?\d*))?") - def __init__(self): - self.boxes = {b.__name__ : b for b in boxes.generators.getAllBoxGenerators().values() if b.webinterface} - self.boxes['TrayLayout2'] = boxes.generators.traylayout.TrayLayout2 + def __init__(self, url_prefix="", static_url="static") -> None: + self.boxes = {b.__name__: b for b in boxes.generators.getAllBoxGenerators().values() if b.webinterface} self.groups = boxes.generators.ui_groups self.groups_by_name = boxes.generators.ui_groups_by_name for name, box in self.boxes.items(): + box.UI = "web" self.groups_by_name.get(box.ui_group, self.groups_by_name["Misc"]).add(box) self.staticdir = os.path.join(os.path.dirname(__file__), '../static/') self._languages = None + self._cache: dict[Any, Any] = {} + self.url_prefix = url_prefix + self.static_url = static_url def getLanguages(self, domain=None, localedir=None): if self._languages is not None: @@ -115,7 +146,6 @@ class BServer: return self._languages def getLanguage(self, args, accept_language): - lang = None langs = [] @@ -126,8 +156,7 @@ class BServer: break if lang: try: - return gettext.translation('boxes.py', localedir='locale', - languages=[lang]) + return gettext.translation('boxes.py', localedir='locale', languages=[lang]) except OSError: pass try: @@ -146,100 +175,81 @@ class BServer: langs = [l[1].replace("-", "_") for l in langs] try: - return gettext.translation('boxes.py', localedir='locale', - languages=langs) + return gettext.translation('boxes.py', localedir='locale', languages=langs) except OSError: return gettext.translation('boxes.py', languages=langs, fallback=True) - def arg2html(self, a, prefix, defaults={}, _=lambda s:s): + def arg2html(self, a, prefix, defaults={}, _=lambda s: s): name = a.option_strings[0].replace("-", "") if isinstance(a, argparse._HelpAction): return "" viewname = name if prefix and name.startswith(prefix + '_'): - viewname = name[len(prefix)+1:] + viewname = name[len(prefix) + 1:] default = defaults.get(name, None) row = """
%%s%s
\n""" % \ - (name+"_id", name, _(viewname), name+"_description","" if not a.help else markdown.markdown(_(a.help))) + (name + "_id", name, _(viewname), name + "_description", "" if not a.help else markdown.markdown(_(a.help))) if (isinstance(a, argparse._StoreAction) and - hasattr(a.type, "html")): + hasattr(a.type, "html")): input = a.type.html(name, default or a.default, _) - elif a.dest == "layout": + elif a.type == str and "\n" in a.default: val = (default or a.default).split("\n") input = """""" % \ - (name, name, name+"_id", name+"_description", max((len(l) for l in val))+10, len(val)+1, default or a.default) + (name, name, name + "_id", name + "_description", max(len(l) for l in val) + 10, len(val) + 1, default or a.default) elif a.choices: options = "\n".join( - ("""""" % - (e, ' selected="selected"' if (e == (default or a.default)) or (str(e) == str(default or a.default)) else "", - _(e)) for e in a.choices)) - input = """\n""" % (name, name, name+"_id", name+"_description", options) + """""" % + (e, ' selected="selected"' if (e == (default or a.default)) or (str(e) == str(default or a.default)) else "", + _(e)) for e in a.choices) + input = """\n""".format(name, name, name + "_id", name + "_description", options) else: - input = """
- """ + _("Boxes - %s") % _(name), """ - - - - -""", self.scripts % (len(box.argparser._action_groups)-3), """ - + {_("%s - Boxes") % _(name)} + {self.genHTMLMeta()} + {self.genHTMLMetaLanguageLink()} + {self.genHTMLCSS()} + {self.genHTMLJS()} - + - -
+
-self-Logo +self-Logo

-

""", _(name), """

-

""", _(box.__doc__) if box.__doc__ else "", """

-
- """ % (action)] +

{_(name)}

+

{_(box.__doc__) if box.__doc__ else ""}

+ + """] groupid = 0 for group in box.argparser._action_groups[3:] + box.argparser._action_groups[:3]: if not group._group_actions: @@ -247,183 +257,235 @@ class BServer: if len(group._group_actions) == 1 and isinstance(group._group_actions[0], argparse._HelpAction): continue prefix = getattr(group, "prefix", None) - result.append('''

%s

\n\n''' % (groupid, groupid, groupid, _(group.title), groupid)) + result.append(f'''

{_(group.title)}

\n\n''') for a in group._group_actions: if a.dest in ("input", "output"): - continue result.append(self.arg2html(a, prefix, defaults, _)) result.append("") groupid += 1 - result.append(""" + + result.append(f""" + +

- - + + + +

+
-
-

""") - no_img_msg = _('There is no image yet. Please donate an image of your project on GitHub!') + no_img_msg = _('There is no image yet. Please donate an image of your project on GitHub!') if box.description: - result.append(markdown.markdown(_(box.description), - extensions=["extra"])) + result.append( + markdown.markdown(_(box.description), extensions=["extra"]) + .replace('src="static/', f'src="{self.static_url}/')) result.append(f'''
- -
-''') - - result.append(""" +Picture of box.
-""" + self.footer(lang) + """ + +{self.genPagePartFooter(lang)} + - """ ) + ''') return (s.encode("utf-8") for s in result) - def menu(self, lang): + def genPageMenu(self, lang): _ = lang.gettext lang_name = lang.info().get('language', None) + + langparam = "" if lang_name: langparam = "?language=" + lang_name - else: - langparam = "" - result = [""" - + result = [f"""{self.genHTMLStart(lang)} - """ + _("Boxes.py") + """ - - - - - """, self.scripts % len(self.groups), """ - + {_("Boxes.py")} + {self.genHTMLMeta()} + {self.genHTMLMetaLanguageLink()} + {self.genHTMLCSS()} + {self.genHTMLJS()} - -
+ +
-

""" + _("Boxes.py") + """

-

-""" + _("Create boxes and more with a laser cutter!") + """ -

-

-""" + _(""" - Boxes.py is an Open Source box generator written in Python. It features both finished parametrized generators as well as a Python API for writing your own. It features finger and (flat) dovetail joints, flex cuts, holes and slots for screws, hinges, gears, pulleys and much more.""") + """ -

- +{self.genPagePartHeader(lang)} +
+{_("Gallery")} +{_("Menu")}
-
-self-Logo -
-
-
-
+

-
""" + self.footer(lang) + """ +
+{self.genPagePartFooter(lang)} """) return (s.encode("utf-8") for s in result) + def genHTMLStart(self, lang) -> str: + lang_attr = lang.info().get("language", "") + + if lang_attr != "": + return f"""""" + + return "" + + def genHTMLMeta(self) -> str: + return f''' + + + + + + ''' + + def genHTMLMetaLanguageLink(self) -> str: + """Generates meta language list for search engines.""" + languages = self.getLanguages() + + s = "" + for language in languages: + s += f'\n' + return s + + def genHTMLCSS(self) -> str: + return f'' + + def genHTMLJS(self) -> str: + return f'' + + def genHTMLLanguageSelection(self, lang) -> str: + """Generates a dropdown selection for the language change.""" + current_language = lang.info().get('language', '') + languages = self.getLanguages() + + if len(languages) < 2: + return "" + + html_option = "" + for language in languages: + html_option += f"\n" - def footer(self, lang): - _ = lang.gettext - language = lang.info().get('language', '') return """ - + +
+self-Logo +
+ +
+ +
+ +""" + + def genPagePartFooter(self, lang) -> str: + _ = lang.gettext + + return """ + """ - def errorMessage(self, name, e, _): - return [ - (""" + def genPageError(self, name, e, lang) -> list[bytes]: + """Generates a error page.""" + _ = lang.gettext + + h = f"""{self.genHTMLStart(lang)} - """ + _("Error generating %s") % _(name) + - """ - + {_("Error generating %s") % _(name)} + {self.genHTMLMeta()} + -

""" + _("An error occurred!") + "

" + -"".join(u"

%s

" % html.escape(s) for s in type(u"")(e).split(u"\n")) + +

{_("An error occurred!")}

""" - - -""").encode("utf-8") ] + for s in str(e).split("\n"): + h += f"

{html.escape(s)}

\n" + h += "" + return [h.encode("utf-8")] def serveStatic(self, environ, start_response): filename = environ["PATH_INFO"][len("/static/"):] path = os.path.join(self.staticdir, filename) if (not re.match(r"[a-zA-Z0-9_/-]+\.[a-zA-Z0-9]+", filename) or - not os.path.exists(path)): + not os.path.exists(path)): if re.match(r"samples/.*-thumb.jpg", filename): path = os.path.join(self.staticdir, "nothing.png") else: - start_response("404 Not Found", - [('Content-type', 'text/plain')]) + start_response("404 Not Found", [('Content-type', 'text/plain')]) return [b"Not found"] type_, encoding = mimetypes.guess_type(filename) @@ -435,13 +497,13 @@ class BServer: if type_ is not None and "image" in type_ and type_ != "image/svg+xml": start_response("200 OK", [('Content-type', "%s" % type_)]) else: - start_response("200 OK", [('Content-type', "%s; charset=%s" % (type_, encoding))]) + start_response("200 OK", [('Content-type', f"{type_}; charset={encoding}")]) f = open(path, 'rb') - return environ['wsgi.file_wrapper'](f, 512*1024) + return environ['wsgi.file_wrapper'](f, 512 * 1024) - def getURL(self, environ): - url = environ['wsgi.url_scheme']+'://' + def getURL(self, environ) -> str: + url = environ['wsgi.url_scheme'] + '://' if environ.get('HTTP_HOST'): url += environ['HTTP_HOST'] @@ -454,6 +516,7 @@ class BServer: else: if environ['SERVER_PORT'] != '80': url += ':' + environ['SERVER_PORT'] + url += quote(self.url_prefix) url += quote(environ.get('SCRIPT_NAME', '')) url += quote(environ.get('PATH_INFO', '')) if environ.get('QUERY_STRING'): @@ -461,113 +524,188 @@ class BServer: return url + def serveGallery(self, environ, start_response, lang): + _ = lang.gettext + lang_name = lang.info().get('language', None) + + start_response("200 OK", [('Content-type', "text/html; charset=utf-8")]) + + if ("Gallery", lang_name) in self._cache: + return self._cache[("Gallery", lang_name)] + + langparam = "" + if lang_name: + langparam = "?language=" + lang_name + + result = [f""" +{self.genHTMLStart(lang)} + + {_("Gallery")} - {_("Boxes.py")} + {self.genHTMLMeta()} + {self.genHTMLMetaLanguageLink()} + {self.genHTMLCSS()} + {self.genHTMLJS()} + + +
+
+{self.genPagePartHeader(lang)} +
+{_("Gallery")} +{_("Menu")} +
+"""] + for nr, group in enumerate(self.groups): + result.append(f"

{_(group.title)}

\n") + for box in group.generators: + name = box.__name__ + fn = f"samples/{name}-thumb.jpg" + thumbnail = f"{self.static_url}/{fn}" + static_filename = os.path.join(self.staticdir, fn) + alt = f"{_(name)}" + href = f"{name}{langparam}" + if not os.path.exists(static_filename): + result.append(f""" {_(name)}

{_(box.__doc__)}
\n""") + else: + result.append(f""" {alt}\n""") + + result.append(f""" +
+

+{self.genPagePartFooter(lang)} + + +""" + ) + self._cache[("Gallery", lang_name)] = [s.encode("utf-8") for s in result] + return self._cache[("Gallery", lang_name)] + def serve(self, environ, start_response): + # serve favicon from static for generated SVGs + if environ["PATH_INFO"] == "favicon.ico": + environ["PATH_INFO"] = "/static/favicon.ico" if environ["PATH_INFO"].startswith("/static/"): return self.serveStatic(environ, start_response) status = '200 OK' headers = [('Content-type', 'text/html; charset=utf-8'), ('X-XSS-Protection', '1; mode=block'), ('X-Content-Type-Options', 'nosniff'), ('x-frame-options', 'SAMEORIGIN'), ('Referrer-Policy', 'no-referrer')] - d = parse_qs(environ.get('QUERY_STRING', '')) name = environ["PATH_INFO"][1:] - args = [unquote_plus(arg) for arg in - environ.get('QUERY_STRING', '').split("&")] + args = [unquote_plus(arg) for arg in environ.get('QUERY_STRING', '').split("&")] + render = "0" + for arg in args: + if arg.startswith("render="): + render = arg[len("render="):] lang = self.getLanguage(args, environ.get("HTTP_ACCEPT_LANGUAGE", "")) _ = lang.gettext + if not name or name == "Gallery": + return self.serveGallery(environ, start_response, lang) + box_cls = self.boxes.get(name, None) if not box_cls: start_response(status, headers) - return self.menu(lang) - if name == "TrayLayout2": - box = box_cls(self, webargs=True) - else: - box = box_cls() + lang_name = lang.info().get('language', None) + if lang_name not in self._cache: + self._cache[lang_name] = list(self.genPageMenu(lang)) + return self._cache[lang_name] - if "render=1" not in args: - defaults = { } + box = box_cls() + + box.translations = lang + + if render == "0": + defaults = {} for a in args: kv = a.split('=') if len(kv) == 2: k, v = kv defaults[k] = html.escape(v, True) start_response(status, headers) - return self.args2html(name, box, lang, "./" + name, defaults=defaults) - else: - args = ["--"+ arg for arg in args if arg != "render=1"] - try: - box.parseArgs(args) - except (ArgumentParserError) as e: - start_response(status, headers) - return self.errorMessage(name, e, _) - if name == "TrayLayout": - start_response(status, headers) - box.fillDefault(box.x, box.y) - layout2 = boxes.generators.traylayout.TrayLayout2(self, webargs=True) - layout2.argparser.set_defaults(layout=str(box)) - return self.args2html( - name, layout2, lang, action="TrayLayout2") - if name == "TrayLayout2": - try: - box.parse(box.layout.split("\n")) - except Exception as e: - start_response(status, headers) - return self.errorMessage(name, e, _) + return self.args2html_cached(name, box, lang, "./" + name, defaults=defaults) - try: - fd, box.output = tempfile.mkstemp() - box.metadata["url"] = self.getURL(environ) - box.open() - box.render() - box.close() - except Exception as e: - if not isinstance(e, ValueError): - print("Exception during rendering:") - traceback.print_exc() - start_response("500 Internal Server Error", - headers) - return self.errorMessage(name, e, _) + args = ["--" + arg for arg in args if not arg.startswith("render=")] + try: + box.parseArgs(args) + except ArgumentParserError as e: + start_response(status, headers) + return self.genPageError(name, e, lang) - http_headers = box.formats.http_headers.get( - box.format, - [('Content-type', 'application/unknown; charset=utf-8')])[:] + try: + box.metadata["url"] = self.getURL(environ) + box.metadata["url_short"] = filter_url(box.metadata["url"], + box.non_default_args) + box.open() + box.render() + data = box.close() + except Exception as e: + if not isinstance(e, ValueError): + print("Exception during rendering:") + traceback.print_exc() + start_response("500 Internal Server Error", headers) + return self.genPageError(name, e, lang) - if box.format != "svg": - extension = box.format - if extension == "svg_Ponoko": - extension = "svg" - http_headers.append(('Content-Disposition', 'attachment; filename="%s.%s"' % (box.__class__.__name__, extension))) + http_headers = box.formats.http_headers.get(box.format, [('Content-type', 'application/unknown; charset=utf-8')])[:] + # Prevent crawlers. + http_headers.append(('X-Robots-Tag', 'noindex,nofollow')) + + if render == "3": + http_headers = [('Content-type', 'image/png')] + http_headers.append(('X-Robots-Tag', 'noindex,nofollow')) + qr_format = "png" + fn = box.__class__.__name__ start_response(status, http_headers) - result = open(box.output, 'rb').readlines() - os.close(fd) - os.remove(box.output) - return (l for l in result) + qrcode = get_qrcode(box.metadata["url_short"], qr_format) + return (qrcode,) + + if box.format != "svg" or render == "2": + extension = box.format + if extension == "svg_Ponoko": + extension = "svg" + http_headers.append(('Content-Disposition', f'attachment; filename="{box.__class__.__name__}.{extension}"')) + start_response(status, http_headers) + return environ['wsgi.file_wrapper'](data, 512 * 1024) -if __name__=="__main__": - host = '' - port = 8000 - if len(sys.argv) > 1: - tmp = sys.argv[1].split(':') - if len(tmp) == 2: - host = tmp[0] - port = int(tmp[1]) - else: - port = int(tmp[0]) +def get_qrcode(url, format): + if url is None: + url = "no url" + img = qrcode.make(url) + image_bytes = io.BytesIO() + img.save(image_bytes, format=format) + return image_bytes.getvalue() + + +def main() -> None: + parser = argparse.ArgumentParser() + + parser.add_argument("--host", default="") + parser.add_argument("--port", type=int, default=8000) + parser.add_argument("--url_prefix", default="", + help="URL path to Boxes.py instance") + parser.add_argument("--static_url", default="static", + help="URL of static content") + args = parser.parse_args() + + boxserver = BServer(url_prefix=args.url_prefix, static_url=args.static_url) + fc = FileChecker() fc.start() - boxserver = BServer() - httpd = make_server(host, port, boxserver.serve) - print("BoxesServer serving on host:port %s:%s..." % (host, port) ) + + httpd = make_server(args.host, args.port, boxserver.serve) + print(f"BoxesServer serving on {args.host}:{args.port}...") try: httpd.serve_forever() except KeyboardInterrupt: fc.stop() httpd.server_close() print("BoxesServer stops.") + + +if __name__ == "__main__": + main() else: - application = BServer().serve - - + boxserver = BServer(url_prefix='/boxes.py', static_url="https://florianfesti.github.io/boxes/static") + application = boxserver.serve diff --git a/extensions/fablabchemnitz/boxes.py/boxes/scripts/gen_thumbnails.sh b/extensions/fablabchemnitz/boxes.py/boxes/scripts/gen_thumbnails.sh index 54a314d..15d12a4 100755 --- a/extensions/fablabchemnitz/boxes.py/boxes/scripts/gen_thumbnails.sh +++ b/extensions/fablabchemnitz/boxes.py/boxes/scripts/gen_thumbnails.sh @@ -15,7 +15,7 @@ thumbnail() { find "$STATIC_DIR" -name '*.jpg' ! -name '*-thumb.jpg' -type f | while read -r f do - f_=$(echo "$f" | sed -E -e 's@([/.])@\\\1@g') + f_=$(echo "$f" | sed -E -e 's@([.])@\\\1@g') checksum=$(grep "$f_" "$STATIC_DIR"samples.sha256 || /bin/true) if [ -n "$checksum" ] then diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/fonts.css b/extensions/fablabchemnitz/boxes.py/boxes/static/fonts.css index 4228f45..7747f4c 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/static/fonts.css +++ b/extensions/fablabchemnitz/boxes.py/boxes/static/fonts.css @@ -1,36 +1,41 @@ -/* vietnamese */ +/*https://github.com/fontsource/fontsource/tree/main/fonts/google/bungee-shade#readme*/ +/*Font version (provided by source): v11.*/ +/* bungee-shade-vietnamese-400-normal*/ @font-face { font-family: 'Bungee Shade'; font-style: normal; - font-weight: 400; font-display: swap; - src: local('Bungee Shade'), local('BungeeShade-Regular'), url(fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc5ijTnwE.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB; + font-weight: 400; + src: url('./fonts/bungeeshade/bungee-shade-vietnamese-400-normal.woff2') format('woff2'); + unicode-range: U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB; } -/* latin-ext */ +/* bungee-shade-latin-ext-400-normal*/ @font-face { font-family: 'Bungee Shade'; font-style: normal; - font-weight: 400; font-display: swap; - src: local('Bungee Shade'), local('BungeeShade-Regular'), url(fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc5yjTnwE.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; + font-weight: 400; + src: url('./fonts/bungeeshade/bungee-shade-latin-ext-400-normal.woff2') format('woff2'); + unicode-range: U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF; } -/* latin */ +/* bungee-shade-latin-400-normal*/ @font-face { font-family: 'Bungee Shade'; font-style: normal; - font-weight: 400; font-display: swap; - src: local('Bungee Shade'), local('BungeeShade-Regular'), url(fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc6SjT.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-weight: 400; + src: url('./fonts/bungeeshade/bungee-shade-latin-400-normal.woff2') format('woff2'); + unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; } -/* latin */ + +/*https://github.com/fontsource/fontsource/tree/main/fonts/google/luckiest-guy*/ +/*Font version (provided by source): v18*/ +/* luckiest-guy-latin-400-normal*/ @font-face { font-family: 'Luckiest Guy'; font-style: normal; - font-weight: 400; font-display: swap; - src: local('Luckiest Guy Regular'), local('LuckiestGuy-Regular'), url(fonts/luckiestguy/v8/_gP_1RrxsjcxVyin9l9n_j2hTd52.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-weight: 400; + src: url('./fonts/luckiestguy/luckiest-guy-latin-400-normal.woff2') format('woff2'); + unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; } diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/bungee-shade-latin-400-normal.woff2 b/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/bungee-shade-latin-400-normal.woff2 new file mode 100644 index 0000000..72bd6c6 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/bungee-shade-latin-400-normal.woff2 differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/bungee-shade-latin-ext-400-normal.woff2 b/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/bungee-shade-latin-ext-400-normal.woff2 new file mode 100644 index 0000000..2583d29 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/bungee-shade-latin-ext-400-normal.woff2 differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/bungee-shade-vietnamese-400-normal.woff2 b/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/bungee-shade-vietnamese-400-normal.woff2 new file mode 100644 index 0000000..5819b92 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/bungee-shade-vietnamese-400-normal.woff2 differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc5ijTnwE.woff2 b/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc5ijTnwE.woff2 deleted file mode 100644 index 75a661e..0000000 Binary files a/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc5ijTnwE.woff2 and /dev/null differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc5yjTnwE.woff2 b/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc5yjTnwE.woff2 deleted file mode 100644 index 0fb5357..0000000 Binary files a/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc5yjTnwE.woff2 and /dev/null differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc6SjT.woff2 b/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc6SjT.woff2 deleted file mode 100644 index 6df716c..0000000 Binary files a/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/bungeeshade/v3/DtVkJxarWL0t2KdzK3oI_jkc6SjT.woff2 and /dev/null differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/luckiestguy/luckiest-guy-latin-400-normal.woff2 b/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/luckiestguy/luckiest-guy-latin-400-normal.woff2 new file mode 100644 index 0000000..a1c865f Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/luckiestguy/luckiest-guy-latin-400-normal.woff2 differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/luckiestguy/v8/_gP_1RrxsjcxVyin9l9n_j2hTd52.woff2 b/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/luckiestguy/v8/_gP_1RrxsjcxVyin9l9n_j2hTd52.woff2 deleted file mode 100644 index ca65028..0000000 Binary files a/extensions/fablabchemnitz/boxes.py/boxes/static/fonts/luckiestguy/v8/_gP_1RrxsjcxVyin9l9n_j2hTd52.woff2 and /dev/null differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/needs-image.png b/extensions/fablabchemnitz/boxes.py/boxes/static/needs-image.png new file mode 100644 index 0000000..f1fdd27 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/needs-image.png differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ABox-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ABox-thumb.jpg new file mode 100644 index 0000000..0005508 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ABox-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ABox.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ABox.jpg new file mode 100644 index 0000000..e530599 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ABox.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/AirPurifier-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/AirPurifier-thumb.jpg new file mode 100644 index 0000000..db7ee71 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/AirPurifier-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/AirPurifier.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/AirPurifier.jpg new file mode 100644 index 0000000..43558bb Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/AirPurifier.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Arcade-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Arcade-thumb.jpg new file mode 100644 index 0000000..f7deb9b Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Arcade-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Arcade.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Arcade.jpg new file mode 100644 index 0000000..95373f7 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Arcade.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/BirdHouse-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/BirdHouse-thumb.jpg new file mode 100644 index 0000000..3552aba Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/BirdHouse-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/BirdHouse.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/BirdHouse.jpg new file mode 100644 index 0000000..280ad95 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/BirdHouse.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/BreadBox-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/BreadBox-thumb.jpg new file mode 100644 index 0000000..09cd8f7 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/BreadBox-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/BreadBox.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/BreadBox.jpg new file mode 100644 index 0000000..3411507 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/BreadBox.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-closed-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-closed-thumb.jpg new file mode 100644 index 0000000..ff28c8f Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-closed-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-closed.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-closed.jpg new file mode 100644 index 0000000..5239e1b Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-closed.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-open-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-open-thumb.jpg new file mode 100644 index 0000000..83767fb Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-open-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-open.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-open.jpg new file mode 100644 index 0000000..e3b3357 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-open.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-pins-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-pins-thumb.jpg new file mode 100644 index 0000000..7d35814 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-pins-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-pins.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-pins.jpg new file mode 100644 index 0000000..b336e55 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-pins.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-thumb.jpg new file mode 100644 index 0000000..4330eb7 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe.jpg new file mode 100644 index 0000000..1ec97d1 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/CoinBankSafe.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConcaveKnob-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConcaveKnob-thumb.jpg new file mode 100644 index 0000000..f72ca0c Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConcaveKnob-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConcaveKnob.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConcaveKnob.jpg new file mode 100644 index 0000000..e0c0e34 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConcaveKnob.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickBack-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickBack-thumb.jpg new file mode 100644 index 0000000..d925638 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickBack-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickBack.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickBack.jpg new file mode 100644 index 0000000..2694235 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickBack.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickFront-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickFront-thumb.jpg new file mode 100644 index 0000000..732354d Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickFront-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickFront.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickFront.jpg new file mode 100644 index 0000000..a97823b Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickFront.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickInside-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickInside-thumb.jpg new file mode 100644 index 0000000..52082f7 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickInside-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickInside.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickInside.jpg new file mode 100644 index 0000000..bf3544d Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/ConsoleArcadeStickInside.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Desksign-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Desksign-thumb.jpg new file mode 100644 index 0000000..fa569eb Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Desksign-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Desksign.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Desksign.jpg new file mode 100644 index 0000000..b2f1ab8 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Desksign.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Dispenser-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Dispenser-thumb.jpg new file mode 100644 index 0000000..eadae58 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Dispenser-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Dispenser.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Dispenser.jpg new file mode 100644 index 0000000..7a9c395 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Dispenser.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/DrillBox-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/DrillBox-thumb.jpg new file mode 100644 index 0000000..646e2c2 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/DrillBox-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/DrillBox.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/DrillBox.jpg new file mode 100644 index 0000000..84274f4 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/DrillBox.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/DrillBoxes-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/DrillBoxes-thumb.jpg new file mode 100644 index 0000000..372084d Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/DrillBoxes-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/DrillBoxes.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/DrillBoxes.jpg new file mode 100644 index 0000000..480ac5c Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/DrillBoxes.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FanHole-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FanHole-thumb.jpg new file mode 100644 index 0000000..b25bb2a Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FanHole-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FanHole.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FanHole.jpg new file mode 100644 index 0000000..e039bab Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FanHole.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FilamentSpool-2-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FilamentSpool-2-thumb.jpg new file mode 100644 index 0000000..61b5efa Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FilamentSpool-2-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FilamentSpool-2.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FilamentSpool-2.jpg new file mode 100644 index 0000000..8f81611 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FilamentSpool-2.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FilamentSpool-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FilamentSpool-thumb.jpg new file mode 100644 index 0000000..2c3333d Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FilamentSpool-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FilamentSpool.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FilamentSpool.jpg new file mode 100644 index 0000000..73933de Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FilamentSpool.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FillTest-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FillTest-thumb.jpg new file mode 100644 index 0000000..3bb0401 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FillTest-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FillTest.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FillTest.jpg new file mode 100644 index 0000000..b9ab612 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FillTest.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBook-2-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBook-2-thumb.jpg new file mode 100644 index 0000000..d8e2a2f Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBook-2-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBook-2.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBook-2.jpg new file mode 100644 index 0000000..16d9724 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBook-2.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBook-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBook-thumb.jpg new file mode 100644 index 0000000..a29939b Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBook-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBook.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBook.jpg new file mode 100644 index 0000000..4bdf260 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBook.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBox5-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBox5-thumb.jpg new file mode 100644 index 0000000..50af933 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBox5-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBox5.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBox5.jpg new file mode 100644 index 0000000..4187829 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexBox5.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexTest2-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexTest2-thumb.jpg new file mode 100644 index 0000000..475e8cd Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexTest2-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexTest2.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexTest2.jpg new file mode 100644 index 0000000..b9039ce Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FlexTest2.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FrontPanel-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FrontPanel-thumb.jpg new file mode 100644 index 0000000..54b362b Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FrontPanel-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FrontPanel.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FrontPanel.jpg new file mode 100644 index 0000000..322b33c Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/FrontPanel.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GearBox-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GearBox-thumb.jpg new file mode 100644 index 0000000..095b2cf Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GearBox-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GearBox.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GearBox.jpg new file mode 100644 index 0000000..e96e469 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GearBox.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GridfinityBase-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GridfinityBase-thumb.jpg new file mode 100644 index 0000000..0b3eea3 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GridfinityBase-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GridfinityBase.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GridfinityBase.jpg new file mode 100644 index 0000000..032624e Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GridfinityBase.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GridfinityTrayLayout-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GridfinityTrayLayout-thumb.jpg new file mode 100644 index 0000000..d1cc411 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GridfinityTrayLayout-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GridfinityTrayLayout.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GridfinityTrayLayout.jpg new file mode 100644 index 0000000..132be5a Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/GridfinityTrayLayout.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/HolePattern-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/HolePattern-thumb.jpg new file mode 100644 index 0000000..3227ac3 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/HolePattern-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/HolePattern.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/HolePattern.jpg new file mode 100644 index 0000000..17076fc Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/HolePattern.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Hook-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Hook-thumb.jpg new file mode 100644 index 0000000..397aa51 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Hook-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Hook.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Hook.jpg new file mode 100644 index 0000000..b224a26 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Hook.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/KeyHolder-2-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/KeyHolder-2-thumb.jpg new file mode 100644 index 0000000..40ddf17 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/KeyHolder-2-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/KeyHolder-2.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/KeyHolder-2.jpg new file mode 100644 index 0000000..2544441 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/KeyHolder-2.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/KeyHolder-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/KeyHolder-thumb.jpg new file mode 100644 index 0000000..9935708 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/KeyHolder-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/KeyHolder.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/KeyHolder.jpg new file mode 100644 index 0000000..007660e Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/KeyHolder.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/LaptopStand-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/LaptopStand-thumb.jpg new file mode 100644 index 0000000..df82dc1 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/LaptopStand-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/LaptopStand.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/LaptopStand.jpg new file mode 100644 index 0000000..38544b8 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/LaptopStand.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/MagazinFile-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/MagazineFile-thumb.jpg similarity index 100% rename from extensions/fablabchemnitz/boxes.py/boxes/static/samples/MagazinFile-thumb.jpg rename to extensions/fablabchemnitz/boxes.py/boxes/static/samples/MagazineFile-thumb.jpg diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/MagazinFile.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/MagazineFile.jpg similarity index 100% rename from extensions/fablabchemnitz/boxes.py/boxes/static/samples/MagazinFile.jpg rename to extensions/fablabchemnitz/boxes.py/boxes/static/samples/MagazineFile.jpg diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/NemaPattern-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/NemaPattern-thumb.jpg new file mode 100644 index 0000000..7203d0a Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/NemaPattern-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/NemaPattern.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/NemaPattern.jpg new file mode 100644 index 0000000..e9faa5d Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/NemaPattern.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoBody-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoBody-thumb.jpg index 481d13c..f04d1f1 100644 Binary files a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoBody-thumb.jpg and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoBody-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoBody.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoBody.jpg index 17c783d..78f3adb 100644 Binary files a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoBody.jpg and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoBody.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoLegs-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoLegs-thumb.jpg new file mode 100644 index 0000000..227cdcc Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoLegs-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoLegs.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoLegs.jpg new file mode 100644 index 0000000..615a24f Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoLegs.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoSoles-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoSoles-thumb.jpg new file mode 100644 index 0000000..ac6b621 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoSoles-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoSoles.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoSoles.jpg new file mode 100644 index 0000000..25d56b0 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/OttoSoles.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/PirateChest-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/PirateChest-thumb.jpg new file mode 100644 index 0000000..40248cb Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/PirateChest-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/PirateChest.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/PirateChest.jpg new file mode 100644 index 0000000..fb0879b Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/PirateChest.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/PizzaShovel-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/PizzaShovel-thumb.jpg new file mode 100644 index 0000000..210f0a7 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/PizzaShovel-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/PizzaShovel.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/PizzaShovel.jpg new file mode 100644 index 0000000..053de42 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/PizzaShovel.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Rack19Box-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Rack19Box-thumb.jpg new file mode 100644 index 0000000..91e5d5f Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Rack19Box-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Rack19Box.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Rack19Box.jpg new file mode 100644 index 0000000..0f4c04e Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Rack19Box.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RackBox-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RackBox-thumb.jpg new file mode 100644 index 0000000..24d02f3 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RackBox-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RackBox.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RackBox.jpg new file mode 100644 index 0000000..eed5505 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RackBox.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RectangularWall-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RectangularWall-thumb.jpg new file mode 100644 index 0000000..4d586cd Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RectangularWall-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RectangularWall.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RectangularWall.jpg new file mode 100644 index 0000000..8d023cb Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RectangularWall.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RollHolder-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RollHolder-thumb.jpg new file mode 100644 index 0000000..ea134d6 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RollHolder-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RollHolder.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RollHolder.jpg new file mode 100644 index 0000000..e51e6bc Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/RollHolder.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SevenSegmentPattern-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SevenSegmentPattern-thumb.jpg new file mode 100644 index 0000000..c51ff35 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SevenSegmentPattern-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SevenSegmentPattern.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SevenSegmentPattern.jpg new file mode 100644 index 0000000..2115ace Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SevenSegmentPattern.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-backlit-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-backlit-thumb.jpg new file mode 100644 index 0000000..3ac0558 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-backlit-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-backlit.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-backlit.jpg new file mode 100644 index 0000000..d8805d8 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-backlit.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-diagram-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-diagram-thumb.jpg new file mode 100644 index 0000000..a7f5d55 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-diagram-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-diagram.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-diagram.jpg new file mode 100644 index 0000000..f7d22d0 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-diagram.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-thumb.jpg new file mode 100644 index 0000000..ffd2794 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox.jpg new file mode 100644 index 0000000..1a7e705 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shadowbox.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shoe-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shoe-thumb.jpg new file mode 100644 index 0000000..06d2aca Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shoe-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shoe.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shoe.jpg new file mode 100644 index 0000000..9bb6cbf Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Shoe.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SideDoorHousing-2-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SideDoorHousing-2-thumb.jpg new file mode 100644 index 0000000..ef4d2a9 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SideDoorHousing-2-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SideDoorHousing-2.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SideDoorHousing-2.jpg new file mode 100644 index 0000000..f1d6efa Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SideDoorHousing-2.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SideDoorHousing-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SideDoorHousing-thumb.jpg new file mode 100644 index 0000000..f9998ec Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SideDoorHousing-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SideDoorHousing.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SideDoorHousing.jpg new file mode 100644 index 0000000..c97e2fb Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SideDoorHousing.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SkadisBoard-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SkadisBoard-thumb.jpg new file mode 100644 index 0000000..a6ebc1d Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SkadisBoard-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SkadisBoard.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SkadisBoard.jpg new file mode 100644 index 0000000..9d16302 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SkadisBoard.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SlantedTray-2-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SlantedTray-2-thumb.jpg new file mode 100644 index 0000000..0e56869 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SlantedTray-2-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SlantedTray-2.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SlantedTray-2.jpg new file mode 100644 index 0000000..176c042 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SlantedTray-2.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SlantedTray-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SlantedTray-thumb.jpg new file mode 100644 index 0000000..c81d501 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SlantedTray-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SlantedTray.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SlantedTray.jpg new file mode 100644 index 0000000..e4c6d03 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SlantedTray.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SmallPartsTray-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SmallPartsTray-thumb.jpg new file mode 100644 index 0000000..fd7fbd7 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SmallPartsTray-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SmallPartsTray.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SmallPartsTray.jpg new file mode 100644 index 0000000..c5d3263 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SmallPartsTray.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SmallPartsTray2-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SmallPartsTray2-thumb.jpg new file mode 100644 index 0000000..15ded3a Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SmallPartsTray2-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SmallPartsTray2.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SmallPartsTray2.jpg new file mode 100644 index 0000000..f7bd952 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/SmallPartsTray2.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Spool-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Spool-thumb.jpg new file mode 100644 index 0000000..3af542e Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Spool-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Spool.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Spool.jpg new file mode 100644 index 0000000..6dd249e Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Spool.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Tetris-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Tetris-thumb.jpg new file mode 100644 index 0000000..2adfe7a Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Tetris-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Tetris.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Tetris.jpg new file mode 100644 index 0000000..60f03b7 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/Tetris.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallChiselHolder-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallChiselHolder-thumb.jpg new file mode 100644 index 0000000..5806953 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallChiselHolder-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallChiselHolder.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallChiselHolder.jpg new file mode 100644 index 0000000..ffdda10 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallChiselHolder.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallConsole-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallConsole-thumb.jpg new file mode 100644 index 0000000..d7d819b Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallConsole-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallConsole.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallConsole.jpg new file mode 100644 index 0000000..a7f5e46 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallConsole.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallPinRow-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallPinRow-thumb.jpg new file mode 100644 index 0000000..e3c239a Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallPinRow-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallPinRow.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallPinRow.jpg new file mode 100644 index 0000000..d4dc114 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallPinRow.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallPlaneHolder-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallPlaneHolder-thumb.jpg new file mode 100644 index 0000000..ff7e455 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallPlaneHolder-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallPlaneHolder.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallPlaneHolder.jpg new file mode 100644 index 0000000..a43d61f Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallPlaneHolder.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallRack-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallRack-thumb.jpg new file mode 100644 index 0000000..3be0760 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallRack-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallRack.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallRack.jpg new file mode 100644 index 0000000..2d97e83 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallRack.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallRollHolder-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallRollHolder-thumb.jpg new file mode 100644 index 0000000..b80397d Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallRollHolder-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallRollHolder.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallRollHolder.jpg new file mode 100644 index 0000000..72ae60c Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallRollHolder.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallStairs-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallStairs-thumb.jpg new file mode 100644 index 0000000..62a1144 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallStairs-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallStairs.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallStairs.jpg new file mode 100644 index 0000000..58b5b27 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallStairs.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallWrenchHolder-thumb.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallWrenchHolder-thumb.jpg new file mode 100644 index 0000000..3768f86 Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallWrenchHolder-thumb.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallWrenchHolder.jpg b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallWrenchHolder.jpg new file mode 100644 index 0000000..c93fe5b Binary files /dev/null and b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/WallWrenchHolder.jpg differ diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/samples.sha256 b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/samples.sha256 index 2a5e3c4..4e5c4f4 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/static/samples/samples.sha256 +++ b/extensions/fablabchemnitz/boxes.py/boxes/static/samples/samples.sha256 @@ -11,7 +11,7 @@ e21f917775a9a1d08964c46c67eac7ac994122d674d1be437ee42aecfb608efa ../static/samp f60561e58fbe66509c0f839dc02043192862b2f886a23178bdef9102050ee6fa ../static/samples/Folder.jpg b93f528b3c1252c996858c1b0ec6482e177ed767c8d47646b17629fb8b115ce7 ../static/samples/UBox.jpg c07ecba64de889a551e1634bd9d30494d225c1167ac877672282326e68543d8d ../static/samples/UniversalBox.jpg -6a845731b5fa4cc69a5a77660dbe0e8fec2210f3a5572759506f8e1ad8ba40ec ../static/samples/OttoBody.jpg +293085a6678d31431ecd8331fd3fbe1897611fd94065c20ac6dfd635d4b70522 ../static/samples/OttoBody.jpg aec34803225353bf9503e7dec3e0459de4191e337d5272db0042c6f636b30c9f ../static/samples/RobotArm.jpg 18826f349517bdea85c8ce2bd8a8df83ee41ebee84eef6b331daf8f31736e25a ../static/samples/UnevenHeightBox.jpg 55bdf7b75fa5f710c0c09763bd5ea5792eae57033ca5bc59d70d74576b40b6ba ../static/samples/CardBox.jpg @@ -97,9 +97,71 @@ a95305025a8136290e95f8232463bb1c6023a1d98d502e7a249d52c60aafdd94 ../static/samp 34058c3a37bc70dc72e467d2da1ece7cde2aacedf7b751243a7c264c9998afb2 ../static/samples/DiceBox.jpg e90b4a90c1dbd2a4a03e4cf50ba82bc7119b721bd5c90c5fdbd0a356c9eabfd3 ../static/samples/CanStorageAA.jpg 0580043276c800c68dfba840d47c27f974ef2d0629751ecd250df2400f03cca8 ../static/samples/CanStorage.jpg -bb250512410c007a74f91caee4e25eac817021f5a535987a7f5ff46148a074ca ../static/samples/MagazinFile.jpg +bb250512410c007a74f91caee4e25eac817021f5a535987a7f5ff46148a074ca ../static/samples/MagazineFile.jpg 59c29899a7f6cfa1727830b35ba621747bf1fe16b8559884054bf50235559a11 ../static/samples/CoinDisplay.jpg c21b47963a9de686eec4bcdb04dc84a64fef1e943d9c276f6ecb7d619af22791 ../static/samples/HalfBox_Shelf_usage.jpg 3bdbb6dab2beea4dcdcf84941bf1f763bc9eb60ca9369965c4b003d5bb57f83b ../static/samples/HalfBox_AngleJig_usage.jpg 9a0c0bf1d3b74a1e09f278ad9f89d8b7e59261a870bb60ed547d45117594cf93 ../static/samples/HalfBox.jpg e1e5001ad1c59e0815a684f2eb2d25bb88113423ec6b582fb81f835c25947127 ../static/samples/HalfBox_Bookend_usage.jpg +2300383efee9aebb910fb8967edcee9689e1143031c5bca4d7d9a2c4109bd7f9 ../static/samples/GridfinityBase.jpg +6a8a5789897d4836ca1d0eab54b66e77efde181cdf375374253be8a3eb85a183 ../static/samples/BreadBox.jpg +2b53f202726de27f65cb36bd3b8e7fc0f54af4e8021257a2084771613c604401 ../static/samples/SlantedTray-2.jpg +5a7ff061d7e3d6b4ec587926f81b594bd12c0a2516ac939260564164d7d01db2 ../static/samples/SlantedTray.jpg +b4b0aeecfd34eedb1a1e0b9763e219b77f4d0cea0ae39c5197e22dbae5d4f0d4 ../static/samples/SmallPartsTray.jpg +f342316e00a07329aec8a9377274db4709870511126a3cada10ae9057f3f5367 ../static/samples/BirdHouse.jpg +7046e8171d15e3a01685ab0763fa8b4cbdecf98956c02203343579cab39b9c6f ../static/samples/SmallPartsTray2.jpg +bb294909eb5e7a1a49cc7d5ba72b08ca0baf42dd2302494aa1932432817ce973 ../static/samples/RollHolder.jpg +a7ee83b42685295fd02db666841984c55af2f9632540c651f5dea42088a3c170 ../static/samples/FilamentSpool.jpg +0524ca976f76c181135044b185eb4e6cf2ce92ab3316f48a6bc61b9366daefd1 ../static/samples/FilamentSpool-2.jpg +ab47e02fb9736d20b457964aa640f50852ac97bb2857cea753fa4c3067a60d41 ../static/samples/Spool.jpg +cfc0782f3a952dd0c6ceb0fb7998050f3bdea135d791fa32b731808371514e63 ../static/samples/GearBox.jpg +75733dfdfd601ace1521bddfea28546ca34d8281acbeb6ec44f15b2b942cb944 ../static/samples/Arcade.jpg +a21471512fd73c15e1d8a11aa3bd4ef807791895855c2f1d30a00bd207d79919 ../static/samples/FrontPanel.jpg +5640a59072afac58b4bb8410967cb8434a455ac3570118d2163887c17099e064 ../static/samples/WallChiselHolder.jpg +cfec3b4f915befc29efc3913c131204948e63a444fd41370989173b0243617d7 ../static/samples/WallPlaneHolder.jpg +6a5222b9d886454f6c4fda8ead777f2136f8c1d0231e145a30ea853430ee7baa ../static/samples/WallRollHolder.jpg +cedf3c9ce40a379c4ca52089d0cda51cdf4c3899df1368c6374ed45afcb8cef8 ../static/samples/WallWrenchHolder.jpg +5b123ea245e7670cd0a55cea8348aa1f2628080f5ec005902cdd86bab231aad1 ../static/samples/WallConsole.jpg +d7b9c4a9ea70c2cc7ede5e992862fa4c001cff0f592a689ed4d5fab5ac339249 ../static/samples/GridfinityTrayLayout.jpg +885a89ccdf04ddcbd31e8ccd8c7157129443b9b6a3e2b720085c0f11c406a748 ../static/samples/ABox.jpg +f340b90d92d445a626d5e162bc1081fdeda9ba11d563c7f7c4632996379f34da ../static/samples/ConsoleArcadeStickBack.jpg +005751de891a4faa50cf65bcad6781cc5499329a5596a7fa93cee901c7e89537 ../static/samples/ConsoleArcadeStickFront.jpg +4f0b46c88c1c425dcec6b9a0bff754f6b5580f82aa7004233dec03f8f4c5ce41 ../static/samples/ConsoleArcadeStickInside.jpg +2432b6095e513cecaf5e96552dd4136615a0470e36d8804668cbb5b73684249e ../static/samples/DrillBox.jpg +4e055c330eb95303fcce029f6efb1089e9a5b61cf0eef25c95267a8c90d1c2c4 ../static/samples/RackBox.jpg +71f7fb03035c12037beb4bc6c9acdd5fb75ee044d20b4e4be910aae8085ca592 ../static/samples/ConcaveKnob.jpg +4508f68ec6c233b116b6268322996ed50146df34318ffffe4062041fd289fbf6 ../static/samples/DrillBoxes.jpg +70644df6aa7191ae32a66039d3231fc9b84f4f49210571632d2a2f010e728a9b ../static/samples/LaptopStand.jpg +72ea1d0144f27354febffcef5d55ee0897ea6082d05cef78f3dd786b9e943243 ../static/samples/Dispenser.jpg +622576918df92b533f6a689525dbbce3822ad59f58378a2fd1a117a650d30c0e ../static/samples/OttoLegs.jpg +eedccf62c193224e2b86e52be8cd02faddf97d565330e2d5770390141da35069 ../static/samples/OttoSoles.jpg +e9ee43e336401d3a0cb8e141c3a0b87a84e90f99eed30bdd420dcaead92940e1 ../static/samples/NemaPattern.jpg +3070eb2ed89461497d4dcc67021a0619338ab6d4d4d89d3cd1f83160da3c7920 ../static/samples/FanHole.jpg +c8adb0661956430ccec7fc00aa4b8e3c42d02083e17a9307541bc0160251cb96 ../static/samples/HolePattern.jpg +7c399c1cd235c3c044f676a31aa5215109e815055f2f69aef5d0cb1a24dfae41 ../static/samples/Desksign.jpg +fda14ba0920ef7ea0579117a30b5bb4a05ab3be4da3d87a4fc03fc40b085992b ../static/samples/PirateChest.jpg +125033a02263e74d5688287936f54cd83e917276b7c35f3fd37e6cb86efc0dcc ../static/samples/Shoe.jpg +7bf29dc9e2f0b5a0bbedc8b7b6814fe4cec58e9dcebfcd1167757a2bbcc91c17 ../static/samples/Hook.jpg +16013717335b8561b5d78bd586dceee5bac6a35e9e6f0bb9c3b36dc6ea5218bd ../static/samples/PizzaShovel.jpg +b860c4e6a0016c1fb4742d4a70a8201af88a18e36ae0c4d601549a640471fe11 ../static/samples/KeyHolder.jpg +d63379683d34c42994610bf1d0c3fb5daa2c790431a7c2dd94c76302163979b6 ../static/samples/KeyHolder-2.jpg +60f7f3ac2b046107cb573607706f688380c4f9322fe612e1972627f3b01f507a ../static/samples/AirPurifier.jpg +cdd8658c4bd66de5e00c5a961583986a47c29532e006cb4fedb69faab60c9bac ../static/samples/SevenSegmentPattern.jpg +1335103ec002a662753fbf1850c9bdb7fa8bac7925dab1d5878b90aa54e9e963 ../static/samples/WallStairs.jpg +80e1e5eb264277ab69fcdf689e8e91cf6735e1f9d0e595d845fbe7685f9b8725 ../static/samples/Rack19Box.jpg +70f5fd1ef2f65c54867ef74c20ae30465d52226b69a1c82b9213676ff634aa0d ../static/samples/WallPinRow.jpg +56456fde428172ead54947709758b5d79995fa7c5e9bc89b5da84742c7960749 ../static/samples/SkadisBoard.jpg +4f8047786f1eeb256de2edf070efa758648a83dad2ad1c5d7c45388804117d3a *../static/samples/FlexBox5.jpg +22525c5c4fc9e25a68d79a4f6db6a169b2a9038ee9c2ba4a1dd69fcbe2944fb1 *../static/samples/SideDoorHousing-2.jpg +37eefc7aca018887debb9b9671ba8485289a4d5375fabef50d59efe656edbcf8 *../static/samples/SideDoorHousing.jpg +27c3d97e7d141dd72d065160d7fe48d7be700fa8175a034e40599b60d8c64d8c *../static/samples/Tetris.jpg +0195265c174c157690112bc01563439e536952c7015a41fbc562020fcdb270c3 *../static/samples/FillTest.jpg +27699d62eab473d903f2bcf0f334c2c36ff3601772c4c0ddeae89606a7bb10b5 *../static/samples/FlexTest2.jpg +853ace5a13ac571696a1965c72ae456c769ed4bc01ea29bb60bbe40518dcecf9 *../static/samples/RectangularWall.jpg +ed533e9827b58591fccbed9e6d31ff8c10bb7f9981501771f6577616a9fbe71b *../static/samples/WallRack.jpg +f625a31c8f1f08341f8e4c0ba5d34524f92e258ca2ae3027774c399a200ddfc9 ../static/samples/CoinBankSafe.jpg +49d330c7159dd77fc2e7a4d82ca46f2834c7e35a8c59b44739c0defbd045bedf ../static/samples/CoinBankSafe-open.jpg +17e9c4f4da3a74b94168913b78695918096269dfd2c59feb70bd4a833e155d04 ../static/samples/CoinBankSafe-pins.jpg +94e37a41d8b873f39bd3e8c74465b012c8f861031c93f64fd6eda89af02015c0 ../static/samples/FlexBook-2.jpg +4d8b4d5467a88431ba24e893157a6e09997d74654cd7e27878d2a54b1ee751c6 ../static/samples/CoinBankSafe-closed.jpg +bace3582c13ee543f09fd45752d4403e237d01541aaa4ea266e61e64fd12156a ../static/samples/FlexBook.jpg diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/self.css b/extensions/fablabchemnitz/boxes.py/boxes/static/self.css index 0de7ba6..3327ac1 100644 --- a/extensions/fablabchemnitz/boxes.py/boxes/static/self.css +++ b/extensions/fablabchemnitz/boxes.py/boxes/static/self.css @@ -49,6 +49,7 @@ h3 { padding: 5px; padding-left: 15px; margin: 0px; + width: 75% } h3::before { @@ -78,6 +79,7 @@ table tr p { /* Container */ .container { + background-color: #FFF8EA; margin-top: 20px; margin-left: auto; margin-right: auto; @@ -123,7 +125,12 @@ video { } .footer { - box-shadow: none; + margin-left: auto; + margin-right: auto; + padding: 10px; + padding-bottom: 15px; + width: 996px; + border-radius: 30px; text-align: center; margin-top: 0px; } @@ -138,6 +145,11 @@ video { font-size: 0.9em; } +.search { + float : right; + padding-top: 10px; +} + .footer ul li a { color: #555; margin-right: 10px; @@ -186,3 +198,40 @@ img[id|=sample] { textarea { font-family: monospace; } + +.gallery_missing { + display : inline-block; + text-align : center; + vertical-align: middle; + width : 200px; + margin : 5px; + font-weight: bold; +} + +.gallery img { + max-height : 200px; + max-width : 200px; + margin : 5px; +} + +.modenav { + overflow: hidden; +} + +.modebutton { + float: left; + border: none; + outline: none; + padding: 10px 16px; + margin-right: 10px; + text-align : center; + border-radius: 10px; + font-size: 17px; + width: 25%; + background-color: #EFE8DA; +} + +.modeactive { + background-color: #DFD8BA; + +} diff --git a/extensions/fablabchemnitz/boxes.py/boxes/static/self.js b/extensions/fablabchemnitz/boxes.py/boxes/static/self.js new file mode 100644 index 0000000..df063e4 --- /dev/null +++ b/extensions/fablabchemnitz/boxes.py/boxes/static/self.js @@ -0,0 +1,303 @@ +function showThumbnail(img_link) { + const img = document.getElementById("sample-preview"); + img.src = img_link; + img.style.height = "auto"; + img.style.display = "block"; +} + +function showThumbnailEvt(evt) { + const url = evt.target.getAttribute("data-thumbnail"); + showThumbnail(url); +} + +function hideThumbnail() { + const img = document.getElementById("sample-preview"); + img.style.display = "none"; +} + +function expandId(id) { + const e = document.getElementById(id); + const h = document.getElementById("h-" + id); + e.style.display = "block"; + h.classList.add("open"); + h.setAttribute("aria-expanded", "true"); +} +function collapseId(id) { + const e = document.getElementById(id); + const h = document.getElementById("h-" + id); + e.style.display = "none"; + h.classList.remove("open"); + h.setAttribute("aria-expanded", "false"); +} + +function toggleId(id) { + const e = document.getElementById(id); + const h = document.getElementById("h-" + id); + if (e.style.display == null || e.style.display === "none") { + expandId(id); + } else { + collapseId(id); + } +} + +function toggleEl(el) { + const id = el.getAttribute("data-id"); + toggleId(id); +} + +function toggleEvt(evt) { + const id = evt.target.getAttribute("data-id"); + // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role#examples + if (evt instanceof MouseEvent) { + toggleId(id); + } + if (evt instanceof KeyboardEvent && (evt.key === "Enter" || evt.key === " ")) { + evt.preventDefault(); + toggleId(id); + } +} + +function initToggle(el, hide = false) { + // Add event handler. + el.addEventListener("click", toggleEvt); + el.addEventListener("keydown", toggleEvt); + // Hide. + if (hide) { + toggleEl(el); + } +} + +function initThumbnail(el) { + // Add event handler. + el.addEventListener("mouseenter", showThumbnailEvt); + el.addEventListener("mouseleave", hideThumbnail); +} + +function initPage(num_hide = null) { + const h = document.getElementsByClassName("toggle"); + let i = 0; + for (let el of h) { + if (num_hide === null || i < num_hide) { + initToggle(el, true); + } else { + initToggle(el, false); + } + i++; + } + const t = document.getElementsByClassName("thumbnail"); + for (let el of t) initThumbnail(el); +} + +function GridfinityTrayLayout_GenerateLayout(x, y, nx, ny, countx, county) { + // x = width in mm + // y = height in mm + // nx # of gridfinity grids in X + // ny # of gridfinity grids in Y + // countx split x into this many + // county split y into this many + layout = ''; + if (countx == 0) + countx = nx; + if (county == 0) + county = ny + stepx = x / countx; + stepy = y / county; + for (i = 0; i < countx; i++) { + line = ' |'.repeat(i) + ` ,> ${stepx}mm\n`; + layout += line; + } + for (i = 0; i < county; i++) { + layout += "+-".repeat(countx) + "+\n"; + layout += "| ".repeat(countx) + `| ${stepy}mm\n`; + } + layout += "+-".repeat(countx) + "+\n"; + return layout +} + +function GridfinityTrayUpdateLayout(event) { + console.log("update"); + if (window.layoutUpdated == true) { + // Don't do the update if the layout has been manually touched. + if (confirm("You have manually updated the Layout. Do you wish to regenerate it?")) { + window.layoutUpdated = false; + } else { + return; + } + } + console.log("updating"); + nx = document.getElementById('nx').value; + ny = document.getElementById('ny').value; + countx = document.getElementById('countx').value; + county = document.getElementById('county').value; + margin = document.getElementById('margin').value; + x = nx*42 - margin + y = ny*42 - margin + layout_id = document.getElementById('layout'); + layout_id.value = GridfinityTrayLayout_GenerateLayout(x, y, nx, ny, countx, county); +} + +function setUpdated() { + console.log("this was updated"); + window.layoutUpdated=true; +} +function GridfinityTrayLayoutInit() { + console.log("update init"); + ids = ['nx', 'ny', 'countx', 'county', 'margin']; + window.layoutUpdated=false; + for (id_string of ids) { + id = document.getElementById(id_string); + id.addEventListener('input', GridfinityTrayUpdateLayout); + } + layout_id = document.getElementById('layout'); + layout_id.addEventListener('change', setUpdated); + layout_id.addEventListener('input', setUpdated); + + GridfinityTrayUpdateLayout(); + layout_id = document.getElementById('layout'); + layout_id.rows = 20; + layout_id.cols = 24; +} + +function ParseSections(s) { + var sections = []; + for (var section of s.split(":")) { + var operands = section.split("/"); + if (operands.length > 2) return sections; + if (operands.length == 2) { + for (var i=0; i 2) return sections; + if (operands.length == 2) { + for (var i=0; i ${sx[i].toFixed(2)}mm\n`; + layout += line; + } + for (i = 0; i < ny; i++) { + layout += "+-".repeat(nx) + "+\n"; + layout += "| ".repeat(nx) + `| ${sy[i].toFixed(2)}mm\n`; + } + layout += "+-".repeat(nx) + "+\n"; + return layout +} + +function TrayUpdateLayout(event) { + if (window.layoutUpdated == true) { + // Don't do the update if the layout has been manually touched. + if (confirm("You have manually updated the Layout. Do you wish to regenerate it?")) { + window.layoutUpdated = false; + } else { + return; + } + } + sx = document.getElementById('sx').value; + sy = document.getElementById('sy').value; + layout_id = document.getElementById('layout'); + layout_id.value = TrayLayout_GenerateLayout(sx, sy); +} + + +function TrayLayoutInit() { + ids = ['sx', 'sy']; + window.layoutUpdated=false; + for (id_string of ids) { + id = document.getElementById(id_string); + id.addEventListener('input', TrayUpdateLayout); + } + TrayUpdateLayout(); + layout_id = document.getElementById('layout'); + layout_id.addEventListener('change', setUpdated); + layout_id.addEventListener('input', setUpdated); + layout_id.rows = 20; + layout_id.cols = 24; +} + +function addCallbacks() { + page_callbacks = { + "TrayLayout": TrayLayoutInit, + "GridfinityTrayLayout": GridfinityTrayLayoutInit, + }; + loc = new URL(window.location.href); + pathname = loc.pathname; + page = pathname.substr(pathname.lastIndexOf('/')+1); + if (page in page_callbacks) { + callback = page_callbacks[page]; + callback(); + } +} + +document.addEventListener('DOMContentLoaded', function() { + addCallbacks(); +}, false); + +function collapseAll() { + const h = document.getElementsByClassName("toggle"); + for (let el of h) { + id = el.getAttribute("data-id") + collapseId(id); + } +} + +function expandAll() { + const h = document.getElementsByClassName("toggle"); + for (let el of h) { + id = el.getAttribute("data-id") + expandId(id); + } +} + +function showAll(str) { + let matching_ids = document.querySelectorAll('[id^="search_id_"]') + for (let id of matching_ids) { + id.style.display = "inline-block"; + } +} + +function showOnly(str) { + str = str.toLowerCase(); + let matching_ids = document.querySelectorAll('[id^="search_id_"]') + for (let id of matching_ids) { + name = id.id.replace("search_id_", "").toLowerCase(); + if (name.includes(str) || id.textContent.toLowerCase().includes(str)) { + id.style.display = "inline-block"; + } else { + id.style.display = "none"; + } + } +} + +function filterSearchItems() { + const search = document.getElementById("search") + if (search.value.length == 0) { + collapseAll(); + showAll() + } else { + expandAll(); + showOnly(search.value) + } +}