diff --git a/boxes.py.AllEdges.inx b/boxes.py.AllEdges.inx
new file mode 100644
index 0000000..9662368
--- /dev/null
+++ b/boxes.py.AllEdges.inx
@@ -0,0 +1,102 @@
+
+
+ <_name>AllEdges
+ boxes.exe
+ info.festi.boxes.py.AllEdges
+
+ alledges
+
+
+
+ 100
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 60
+ 2.0
+ 1.0
+ 4.0
+
+
+
+ 0
+ False
+ 0.5
+
+ - outset
+ - flush
+
+ 2
+ 0
+ 1
+
+
+
+ 90.0
+ True
+
+ - both
+ - none
+ - left
+ - right
+
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 3.0
+ 0.05
+ 2.0
+ 1.0
+
+
+
+ 5
+ 0.1
+ 3.0
+
+
+
+ 1.05
+ 1.0
+ 0.5
+ 5.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.AngledBox.inx b/boxes.py.AngledBox.inx
new file mode 100644
index 0000000..80b2bf2
--- /dev/null
+++ b/boxes.py.AngledBox.inx
@@ -0,0 +1,58 @@
+
+
+ <_name>AngledBox
+ boxes.exe
+ info.festi.boxes.py.AngledBox
+
+ angledbox
+
+
+
+ 100.0
+ 100.0
+ 100.0
+ True
+ 5
+
+ - none
+ - angled hole
+ - angled lid
+ - angled lid2
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.AngledCutJig.inx b/boxes.py.AngledCutJig.inx
new file mode 100644
index 0000000..bf3bd1a
--- /dev/null
+++ b/boxes.py.AngledCutJig.inx
@@ -0,0 +1,50 @@
+
+
+ <_name>AngledCutJig
+ boxes.exe
+ info.festi.boxes.py.AngledCutJig
+
+ angledcutjig
+
+
+
+ 50
+ 100
+ 45.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 1.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Arcade.inx b/boxes.py.Arcade.inx
new file mode 100644
index 0000000..0927204
--- /dev/null
+++ b/boxes.py.Arcade.inx
@@ -0,0 +1,50 @@
+
+
+ <_name>Arcade
+ boxes.exe
+ info.festi.boxes.py.Arcade
+
+ arcade
+
+
+
+ 450.0
+ 350.0
+ 150.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.BayonetBox.inx b/boxes.py.BayonetBox.inx
new file mode 100644
index 0000000..dd3f2f2
--- /dev/null
+++ b/boxes.py.BayonetBox.inx
@@ -0,0 +1,36 @@
+
+
+ <_name>BayonetBox
+ boxes.exe
+ info.festi.boxes.py.BayonetBox
+
+ bayonetbox
+
+
+
+ 50.0
+ 10
+ True
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.BinTray.inx b/boxes.py.BinTray.inx
new file mode 100644
index 0000000..aaee41b
--- /dev/null
+++ b/boxes.py.BinTray.inx
@@ -0,0 +1,52 @@
+
+
+ <_name>BinTray
+ boxes.exe
+ info.festi.boxes.py.BinTray
+
+ bintray
+
+
+
+ 50*3
+ 50*3
+ 100.0
+ True
+ 0.4
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 0.5
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.BurnTest.inx b/boxes.py.BurnTest.inx
new file mode 100644
index 0000000..5ba6d7f
--- /dev/null
+++ b/boxes.py.BurnTest.inx
@@ -0,0 +1,50 @@
+
+
+ <_name>BurnTest
+ boxes.exe
+ info.festi.boxes.py.BurnTest
+
+ burntest
+
+
+
+ 100
+ 0.01
+ 2
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.CardBox.inx b/boxes.py.CardBox.inx
new file mode 100644
index 0000000..cd86d90
--- /dev/null
+++ b/boxes.py.CardBox.inx
@@ -0,0 +1,51 @@
+
+
+ <_name>CardBox
+ boxes.exe
+ info.festi.boxes.py.CardBox
+
+ cardbox
+
+
+
+ 30
+ 65
+ 90
+ 2
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Castle.inx b/boxes.py.Castle.inx
new file mode 100644
index 0000000..e571f2f
--- /dev/null
+++ b/boxes.py.Castle.inx
@@ -0,0 +1,47 @@
+
+
+ <_name>Castle
+ boxes.exe
+ info.festi.boxes.py.Castle
+
+ castle
+
+
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.ClosedBox.inx b/boxes.py.ClosedBox.inx
new file mode 100644
index 0000000..b3740f0
--- /dev/null
+++ b/boxes.py.ClosedBox.inx
@@ -0,0 +1,51 @@
+
+
+ <_name>ClosedBox
+ boxes.exe
+ info.festi.boxes.py.ClosedBox
+
+ closedbox
+
+
+
+ 100.0
+ 100.0
+ 100.0
+ True
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.ConcaveKnob.inx b/boxes.py.ConcaveKnob.inx
new file mode 100644
index 0000000..823172c
--- /dev/null
+++ b/boxes.py.ConcaveKnob.inx
@@ -0,0 +1,40 @@
+
+
+ <_name>ConcaveKnob
+ boxes.exe
+ info.festi.boxes.py.ConcaveKnob
+
+ concaveknob
+
+
+
+ 50.0
+ 3
+ 0.2
+ 70.0
+ 6.0
+ 1.0
+ 10.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.DiscRack.inx b/boxes.py.DiscRack.inx
new file mode 100644
index 0000000..72fca50
--- /dev/null
+++ b/boxes.py.DiscRack.inx
@@ -0,0 +1,54 @@
+
+
+ <_name>DiscRack
+ boxes.exe
+ info.festi.boxes.py.DiscRack
+
+ discrack
+
+
+
+ 20*10
+ 150.0
+ 5.0
+ 0.75
+ 0.75
+ 3.0
+ 18
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Display.inx b/boxes.py.Display.inx
new file mode 100644
index 0000000..96b6f8f
--- /dev/null
+++ b/boxes.py.Display.inx
@@ -0,0 +1,37 @@
+
+
+ <_name>Display
+ boxes.exe
+ info.festi.boxes.py.Display
+
+ display
+
+
+
+ 150.0
+ 200.0
+ 5.0
+ 0.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.DisplayCase.inx b/boxes.py.DisplayCase.inx
new file mode 100644
index 0000000..1dd16f5
--- /dev/null
+++ b/boxes.py.DisplayCase.inx
@@ -0,0 +1,52 @@
+
+
+ <_name>DisplayCase
+ boxes.exe
+ info.festi.boxes.py.DisplayCase
+
+ displaycase
+
+
+
+ 100.0
+ 100.0
+ 100.0
+ True
+ 2
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.DisplayShelf.inx b/boxes.py.DisplayShelf.inx
new file mode 100644
index 0000000..b66c35a
--- /dev/null
+++ b/boxes.py.DisplayShelf.inx
@@ -0,0 +1,54 @@
+
+
+ <_name>DisplayShelf
+ boxes.exe
+ info.festi.boxes.py.DisplayShelf
+
+ displayshelf
+
+
+
+ 400
+ 100
+ 300
+ True
+ 3
+ 20.0
+ 30.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.DividerTray.inx b/boxes.py.DividerTray.inx
new file mode 100644
index 0000000..1f7d3ac
--- /dev/null
+++ b/boxes.py.DividerTray.inx
@@ -0,0 +1,47 @@
+
+
+ <_name>DividerTray
+ boxes.exe
+ info.festi.boxes.py.DividerTray
+
+ dividertray
+
+
+
+ 50*3
+ 50*3
+ 100.0
+ True
+ 20
+ 0
+ 2
+ 0.2
+ 0
+ 1
+ 8
+ 15
+ True
+ True
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.DrillBox.inx b/boxes.py.DrillBox.inx
new file mode 100644
index 0000000..ab73f14
--- /dev/null
+++ b/boxes.py.DrillBox.inx
@@ -0,0 +1,47 @@
+
+
+ <_name>DrillBox
+ boxes.exe
+ info.festi.boxes.py.DrillBox
+
+ drillbox
+
+
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.ElectronicsBox.inx b/boxes.py.ElectronicsBox.inx
new file mode 100644
index 0000000..ab6c0c6
--- /dev/null
+++ b/boxes.py.ElectronicsBox.inx
@@ -0,0 +1,57 @@
+
+
+ <_name>ElectronicsBox
+ boxes.exe
+ 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
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.FlexBox.inx b/boxes.py.FlexBox.inx
new file mode 100644
index 0000000..0cc2ebe
--- /dev/null
+++ b/boxes.py.FlexBox.inx
@@ -0,0 +1,60 @@
+
+
+ <_name>FlexBox
+ boxes.exe
+ info.festi.boxes.py.FlexBox
+
+ flexbox
+
+
+
+ 100.0
+ 100.0
+ 100.0
+ True
+ 15
+ 8
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 1.05
+ 1.0
+ 0.5
+ 5.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.FlexBox2.inx b/boxes.py.FlexBox2.inx
new file mode 100644
index 0000000..873123b
--- /dev/null
+++ b/boxes.py.FlexBox2.inx
@@ -0,0 +1,60 @@
+
+
+ <_name>FlexBox2
+ boxes.exe
+ info.festi.boxes.py.FlexBox2
+
+ flexbox2
+
+
+
+ 100.0
+ 100.0
+ 100.0
+ True
+ 15
+ 8
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 1.05
+ 1.0
+ 0.5
+ 5.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.FlexBox3.inx b/boxes.py.FlexBox3.inx
new file mode 100644
index 0000000..1dceb5e
--- /dev/null
+++ b/boxes.py.FlexBox3.inx
@@ -0,0 +1,61 @@
+
+
+ <_name>FlexBox3
+ boxes.exe
+ info.festi.boxes.py.FlexBox3
+
+ flexbox3
+
+
+
+ 100.0
+ 100.0
+ True
+ 100.0
+ 10.0
+ 10.0
+ 1.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 1
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 1.05
+ 1.0
+ 0.5
+ 5.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.FlexBox4.inx b/boxes.py.FlexBox4.inx
new file mode 100644
index 0000000..ffaa477
--- /dev/null
+++ b/boxes.py.FlexBox4.inx
@@ -0,0 +1,60 @@
+
+
+ <_name>FlexBox4
+ boxes.exe
+ info.festi.boxes.py.FlexBox4
+
+ flexbox4
+
+
+
+ 100.0
+ 100.0
+ 100.0
+ True
+ 15
+ 8
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 1.05
+ 1.0
+ 0.5
+ 5.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.FlexBox5.inx b/boxes.py.FlexBox5.inx
new file mode 100644
index 0000000..611a36d
--- /dev/null
+++ b/boxes.py.FlexBox5.inx
@@ -0,0 +1,60 @@
+
+
+ <_name>FlexBox5
+ boxes.exe
+ info.festi.boxes.py.FlexBox5
+
+ flexbox5
+
+
+
+ 100.0
+ 100.0
+ True
+ 60
+ 60
+ 8
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 1.05
+ 1.0
+ 0.5
+ 5.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.FlexTest.inx b/boxes.py.FlexTest.inx
new file mode 100644
index 0000000..88453f3
--- /dev/null
+++ b/boxes.py.FlexTest.inx
@@ -0,0 +1,42 @@
+
+
+ <_name>FlexTest
+ boxes.exe
+ info.festi.boxes.py.FlexTest
+
+ flextest
+
+
+
+ 100.0
+ 100.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 1.05
+ 1.0
+ 0.5
+ 5.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.FlexTest2.inx b/boxes.py.FlexTest2.inx
new file mode 100644
index 0000000..bc28e5a
--- /dev/null
+++ b/boxes.py.FlexTest2.inx
@@ -0,0 +1,36 @@
+
+
+ <_name>FlexTest2
+ boxes.exe
+ info.festi.boxes.py.FlexTest2
+
+ flextest2
+
+
+
+ 100.0
+ 100.0
+ 1
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Folder.inx b/boxes.py.Folder.inx
new file mode 100644
index 0000000..c506f28
--- /dev/null
+++ b/boxes.py.Folder.inx
@@ -0,0 +1,51 @@
+
+
+ <_name>Folder
+ boxes.exe
+ info.festi.boxes.py.Folder
+
+ folder
+
+
+
+ 100.0
+ 100.0
+ 20
+ 10.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.GearBox.inx b/boxes.py.GearBox.inx
new file mode 100644
index 0000000..2cbf8c9
--- /dev/null
+++ b/boxes.py.GearBox.inx
@@ -0,0 +1,52 @@
+
+
+ <_name>GearBox
+ boxes.exe
+ info.festi.boxes.py.GearBox
+
+ gearbox
+
+
+
+ 8
+ 20
+ 3
+ 6.0
+ 4
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Gears.inx b/boxes.py.Gears.inx
new file mode 100644
index 0000000..32a435a
--- /dev/null
+++ b/boxes.py.Gears.inx
@@ -0,0 +1,42 @@
+
+
+ <_name>Gears
+ boxes.exe
+ info.festi.boxes.py.Gears
+
+ gears
+
+
+
+ 12
+ 6.0
+ 75
+ 32
+ 0.0
+ 0
+ 5
+ 20
+ 20
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.HeartBox.inx b/boxes.py.HeartBox.inx
new file mode 100644
index 0000000..74c6a15
--- /dev/null
+++ b/boxes.py.HeartBox.inx
@@ -0,0 +1,61 @@
+
+
+ <_name>HeartBox
+ boxes.exe
+ info.festi.boxes.py.HeartBox
+
+ heartbox
+
+
+
+ 150
+ 50
+
+ - closed
+ - hole
+ - lid
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 1.0
+ 0.0
+ 1.0
+ 1.0
+
+
+
+ 1.05
+ 1.0
+ 0.5
+ 5.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.HingeBox.inx b/boxes.py.HingeBox.inx
new file mode 100644
index 0000000..ccc0942
--- /dev/null
+++ b/boxes.py.HingeBox.inx
@@ -0,0 +1,66 @@
+
+
+ <_name>HingeBox
+ boxes.exe
+ info.festi.boxes.py.HingeBox
+
+ hingebox
+
+
+
+ 100.0
+ 100.0
+ 100.0
+ True
+ 20.0
+ 0.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 3.2
+ 5
+ 2
+
+ - inside
+ - outside
+
+ 1.5
+ 0.05
+ 2.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Hook.inx b/boxes.py.Hook.inx
new file mode 100644
index 0000000..af5312e
--- /dev/null
+++ b/boxes.py.Hook.inx
@@ -0,0 +1,52 @@
+
+
+ <_name>Hook
+ boxes.exe
+ info.festi.boxes.py.Hook
+
+ hook
+
+
+
+ 40.0
+ 40.0
+ 40.0
+ 20.0
+ 45.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 0.5
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.IntegratedHingeBox.inx b/boxes.py.IntegratedHingeBox.inx
new file mode 100644
index 0000000..c44f066
--- /dev/null
+++ b/boxes.py.IntegratedHingeBox.inx
@@ -0,0 +1,58 @@
+
+
+ <_name>IntegratedHingeBox
+ boxes.exe
+ info.festi.boxes.py.IntegratedHingeBox
+
+ integratedhingebox
+
+
+
+ 100.0
+ 100.0
+ 100.0
+ True
+ 20.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 1.0
+ 2.0
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.LBeam.inx b/boxes.py.LBeam.inx
new file mode 100644
index 0000000..c79a622
--- /dev/null
+++ b/boxes.py.LBeam.inx
@@ -0,0 +1,51 @@
+
+
+ <_name>LBeam
+ boxes.exe
+ info.festi.boxes.py.LBeam
+
+ lbeam
+
+
+
+ 100.0
+ 100.0
+ 100.0
+ True
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.LaserClamp.inx b/boxes.py.LaserClamp.inx
new file mode 100644
index 0000000..358d433
--- /dev/null
+++ b/boxes.py.LaserClamp.inx
@@ -0,0 +1,50 @@
+
+
+ <_name>LaserClamp
+ boxes.exe
+ info.festi.boxes.py.LaserClamp
+
+ laserclamp
+
+
+
+ 25.0
+ 50.0
+ 0.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.LaserHoldfast.inx b/boxes.py.LaserHoldfast.inx
new file mode 100644
index 0000000..bce46b2
--- /dev/null
+++ b/boxes.py.LaserHoldfast.inx
@@ -0,0 +1,37 @@
+
+
+ <_name>LaserHoldfast
+ boxes.exe
+ info.festi.boxes.py.LaserHoldfast
+
+ laserholdfast
+
+
+
+ 25
+ 40
+ 5.0
+ 5.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.MagazinFile.inx b/boxes.py.MagazinFile.inx
new file mode 100644
index 0000000..10d9512
--- /dev/null
+++ b/boxes.py.MagazinFile.inx
@@ -0,0 +1,52 @@
+
+
+ <_name>MagazinFile
+ boxes.exe
+ info.festi.boxes.py.MagazinFile
+
+ magazinfile
+
+
+
+ 100.0
+ 100.0
+ 100.0
+ 0.0
+ True
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.MakitaPowerSupply.inx b/boxes.py.MakitaPowerSupply.inx
new file mode 100644
index 0000000..f31e3a3
--- /dev/null
+++ b/boxes.py.MakitaPowerSupply.inx
@@ -0,0 +1,47 @@
+
+
+ <_name>MakitaPowerSupply
+ boxes.exe
+ info.festi.boxes.py.MakitaPowerSupply
+
+ makitapowersupply
+
+
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.NemaMount.inx b/boxes.py.NemaMount.inx
new file mode 100644
index 0000000..a03e716
--- /dev/null
+++ b/boxes.py.NemaMount.inx
@@ -0,0 +1,58 @@
+
+
+ <_name>NemaMount
+ boxes.exe
+ info.festi.boxes.py.NemaMount
+
+ nemamount
+
+
+
+
+ - 8
+ - 11
+ - 14
+ - 16
+ - 17
+ - 23
+ - 24
+ - 34
+ - 42
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.NotesHolder.inx b/boxes.py.NotesHolder.inx
new file mode 100644
index 0000000..eae6061
--- /dev/null
+++ b/boxes.py.NotesHolder.inx
@@ -0,0 +1,64 @@
+
+
+ <_name>NotesHolder
+ boxes.exe
+ info.festi.boxes.py.NotesHolder
+
+ notesholder
+
+
+
+ 78
+ 78
+ 35
+
+ - F Finger Joint (opposing side)
+ - h Edge (parallel Finger Joint Holes)
+ - s Stackable (bottom, finger joint holes)
+ - e Straight Edge
+
+ 40
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 1
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 60
+ 2.0
+ 1.0
+ 4.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.OpenBox.inx b/boxes.py.OpenBox.inx
new file mode 100644
index 0000000..346bfad
--- /dev/null
+++ b/boxes.py.OpenBox.inx
@@ -0,0 +1,51 @@
+
+
+ <_name>OpenBox
+ boxes.exe
+ info.festi.boxes.py.OpenBox
+
+ openbox
+
+
+
+ 100.0
+ 100.0
+ 100.0
+ True
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.OrganPipe.inx b/boxes.py.OrganPipe.inx
new file mode 100644
index 0000000..e40b4fc
--- /dev/null
+++ b/boxes.py.OrganPipe.inx
@@ -0,0 +1,74 @@
+
+
+ <_name>OrganPipe
+ boxes.exe
+ info.festi.boxes.py.OrganPipe
+
+ organpipe
+
+
+
+
+ - c
+ - c#
+ - d
+ - d#
+ - e
+ - f
+ - f#
+ - g
+ - g#
+ - a
+ - a#
+ - b
+
+ 2
+ 2.0
+ 0.25
+ 0.3
+ 0
+ 588.4
+
+ - Pa
+ - mBar
+ - mmHg
+ - mmH2O
+
+ False
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 1.0
+ 1.0
+ 3.0
+ 0.0
+ 3.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.OttoBody.inx b/boxes.py.OttoBody.inx
new file mode 100644
index 0000000..4a81483
--- /dev/null
+++ b/boxes.py.OttoBody.inx
@@ -0,0 +1,53 @@
+
+
+ <_name>OttoBody
+ boxes.exe
+ info.festi.boxes.py.OttoBody
+
+ ottobody
+
+
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 1.0
+ 2.0
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.OttoLegs.inx b/boxes.py.OttoLegs.inx
new file mode 100644
index 0000000..786cf13
--- /dev/null
+++ b/boxes.py.OttoLegs.inx
@@ -0,0 +1,50 @@
+
+
+ <_name>OttoLegs
+ boxes.exe
+ info.festi.boxes.py.OttoLegs
+
+ ottolegs
+
+
+
+ 3.0
+ 2.6
+ 34.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 1.0
+ 1.0
+ 1.0
+ 0.0
+ 1.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.OttoSoles.inx b/boxes.py.OttoSoles.inx
new file mode 100644
index 0000000..8e620cc
--- /dev/null
+++ b/boxes.py.OttoSoles.inx
@@ -0,0 +1,38 @@
+
+
+ <_name>OttoSoles
+ boxes.exe
+ info.festi.boxes.py.OttoSoles
+
+ ottosoles
+
+
+
+ 56.0
+ 36.0
+ 4.0
+ 5.0
+ 2
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.PaintStorage.inx b/boxes.py.PaintStorage.inx
new file mode 100644
index 0000000..dc6fff9
--- /dev/null
+++ b/boxes.py.PaintStorage.inx
@@ -0,0 +1,60 @@
+
+
+ <_name>PaintStorage
+ boxes.exe
+ info.festi.boxes.py.PaintStorage
+
+ paintstorage
+
+
+
+ 100
+ 300
+ 50
+ 30
+ 10
+ False
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 60
+ 2.0
+ 1.0
+ 4.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Planetary.inx b/boxes.py.Planetary.inx
new file mode 100644
index 0000000..af07815
--- /dev/null
+++ b/boxes.py.Planetary.inx
@@ -0,0 +1,39 @@
+
+
+ <_name>Planetary
+ boxes.exe
+ info.festi.boxes.py.Planetary
+
+ planetary
+
+
+
+ 8
+ 20
+ 0
+ 0
+ 3
+ 6.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Planetary2.inx b/boxes.py.Planetary2.inx
new file mode 100644
index 0000000..6ba7f75
--- /dev/null
+++ b/boxes.py.Planetary2.inx
@@ -0,0 +1,69 @@
+
+
+ <_name>Planetary2
+ boxes.exe
+ info.festi.boxes.py.Planetary2
+
+ planetary2
+
+
+
+
+ - 8
+ - 11
+ - 14
+ - 16
+ - 17
+ - 23
+ - 24
+ - 34
+ - 42
+
+
+ - 40DP
+ - AT5
+ - GT2_2mm
+ - GT2_3mm
+ - GT2_5mm
+ - H
+ - HTD_3mm
+ - HTD_5mm
+ - HTD_8mm
+ - MXL
+ - T10
+ - T2_5
+ - T5
+ - XL
+
+ 20
+ 20
+ 0
+ 1
+ 3
+ 6.0
+ 2.4
+ 4.0
+ 3.1
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.PoleHook.inx b/boxes.py.PoleHook.inx
new file mode 100644
index 0000000..73075d3
--- /dev/null
+++ b/boxes.py.PoleHook.inx
@@ -0,0 +1,52 @@
+
+
+ <_name>PoleHook
+ boxes.exe
+ info.festi.boxes.py.PoleHook
+
+ polehook
+
+
+
+ 50.0
+ 7.8
+ 13.0
+ 5.5
+ 4.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Pulley.inx b/boxes.py.Pulley.inx
new file mode 100644
index 0000000..686648f
--- /dev/null
+++ b/boxes.py.Pulley.inx
@@ -0,0 +1,54 @@
+
+
+ <_name>Pulley
+ boxes.exe
+ info.festi.boxes.py.Pulley
+
+ pulley
+
+
+
+ 6.0
+
+ - 40DP
+ - AT5
+ - GT2_2mm
+ - GT2_3mm
+ - GT2_5mm
+ - H
+ - HTD_3mm
+ - HTD_5mm
+ - HTD_8mm
+ - MXL
+ - T10
+ - T2_5
+ - T5
+ - XL
+
+ 20
+ 5
+ False
+ 0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Rack10Box.inx b/boxes.py.Rack10Box.inx
new file mode 100644
index 0000000..05e21a6
--- /dev/null
+++ b/boxes.py.Rack10Box.inx
@@ -0,0 +1,69 @@
+
+
+ <_name>Rack10Box
+ boxes.exe
+ info.festi.boxes.py.Rack10Box
+
+ rack10box
+
+
+
+ 100.0
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ - 10
+ - 11
+ - 12
+ - 13
+ - 14
+ - 15
+ - 16
+
+ 25.0
+ 2.0
+ 3.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 0.5
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Rack19Box.inx b/boxes.py.Rack19Box.inx
new file mode 100644
index 0000000..9b9ebe3
--- /dev/null
+++ b/boxes.py.Rack19Box.inx
@@ -0,0 +1,69 @@
+
+
+ <_name>Rack19Box
+ boxes.exe
+ info.festi.boxes.py.Rack19Box
+
+ rack19box
+
+
+
+ 100.0
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ - 10
+ - 11
+ - 12
+ - 13
+ - 14
+ - 15
+ - 16
+
+ 25.0
+ 2.0
+ 3.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 0.5
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Rack19HalfWidth.inx b/boxes.py.Rack19HalfWidth.inx
new file mode 100644
index 0000000..908605f
--- /dev/null
+++ b/boxes.py.Rack19HalfWidth.inx
@@ -0,0 +1,37 @@
+
+
+ <_name>Rack19HalfWidth
+ boxes.exe
+ info.festi.boxes.py.Rack19HalfWidth
+
+ rack19halfwidth
+
+
+
+ 1
+ xxmpwx
+ 20
+ 124
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.RackBox.inx b/boxes.py.RackBox.inx
new file mode 100644
index 0000000..40b14ce
--- /dev/null
+++ b/boxes.py.RackBox.inx
@@ -0,0 +1,56 @@
+
+
+ <_name>RackBox
+ boxes.exe
+ 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
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 1.2
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.RectangularWall.inx b/boxes.py.RectangularWall.inx
new file mode 100644
index 0000000..9f6bea8
--- /dev/null
+++ b/boxes.py.RectangularWall.inx
@@ -0,0 +1,286 @@
+
+
+ <_name>RectangularWall
+ boxes.exe
+ info.festi.boxes.py.RectangularWall
+
+ rectangularwall
+
+
+
+ 100
+ 100
+
+ - c Abstract Edge Class
+ - C Abstract Edge Class
+ - d Edge (parallel slot wall Holes)
+ - D Edge (parallel slot wall Holes)
+ - e Straight Edge
+ - E Straight Edge (outset by thickness)
+ - f Finger Joint
+ - F Finger Joint (opposing side)
+ - g Corrugated edge useful as an gipping area
+ - h Edge (parallel Finger Joint Holes)
+ - i Straight edge with hinge eye
+ - I Edge with hinge pin
+ - j Straight edge with hinge eye (other end)
+ - J Edge with hinge pin (other end)
+ - k Straight edge with hinge eye (both ends)
+ - K Edge with hinge pin (both ends)
+ - l Edge for slide on lid (back)
+ - L Edge for slide on lid (box back)
+ - m Edge for slide on lid (left)
+ - M Edge for slide on lid (box left)
+ - n Edge for slide on lid (right)
+ - M Edge for slide on lid (box left)
+ - o Edge with chest hinge
+ - O Edge with chest hinge (other end)
+ - p Edge with chest hinge
+ - P Edge with chest hinge (other end)
+ - q Edge with pins for an chest hinge
+ - Q Edge opposing a chest hinge
+ - R Rack (and pinion) Edge
+ - s Stackable (bottom, finger joint holes)
+ - S Stackable (top)
+ - u Edge with cabinet hinges
+ - U Edge with cabinet hinges top side
+ - v Edge with cabinet hinges for 90° lid
+ - V Edge with cabinet hinges 90° lid
+
+
+ - c Abstract Edge Class
+ - C Abstract Edge Class
+ - d Edge (parallel slot wall Holes)
+ - D Edge (parallel slot wall Holes)
+ - e Straight Edge
+ - E Straight Edge (outset by thickness)
+ - f Finger Joint
+ - F Finger Joint (opposing side)
+ - g Corrugated edge useful as an gipping area
+ - h Edge (parallel Finger Joint Holes)
+ - i Straight edge with hinge eye
+ - I Edge with hinge pin
+ - j Straight edge with hinge eye (other end)
+ - J Edge with hinge pin (other end)
+ - k Straight edge with hinge eye (both ends)
+ - K Edge with hinge pin (both ends)
+ - l Edge for slide on lid (back)
+ - L Edge for slide on lid (box back)
+ - m Edge for slide on lid (left)
+ - M Edge for slide on lid (box left)
+ - n Edge for slide on lid (right)
+ - M Edge for slide on lid (box left)
+ - o Edge with chest hinge
+ - O Edge with chest hinge (other end)
+ - p Edge with chest hinge
+ - P Edge with chest hinge (other end)
+ - q Edge with pins for an chest hinge
+ - Q Edge opposing a chest hinge
+ - R Rack (and pinion) Edge
+ - s Stackable (bottom, finger joint holes)
+ - S Stackable (top)
+ - u Edge with cabinet hinges
+ - U Edge with cabinet hinges top side
+ - v Edge with cabinet hinges for 90° lid
+ - V Edge with cabinet hinges 90° lid
+
+
+ - c Abstract Edge Class
+ - C Abstract Edge Class
+ - d Edge (parallel slot wall Holes)
+ - D Edge (parallel slot wall Holes)
+ - e Straight Edge
+ - E Straight Edge (outset by thickness)
+ - f Finger Joint
+ - F Finger Joint (opposing side)
+ - g Corrugated edge useful as an gipping area
+ - h Edge (parallel Finger Joint Holes)
+ - i Straight edge with hinge eye
+ - I Edge with hinge pin
+ - j Straight edge with hinge eye (other end)
+ - J Edge with hinge pin (other end)
+ - k Straight edge with hinge eye (both ends)
+ - K Edge with hinge pin (both ends)
+ - l Edge for slide on lid (back)
+ - L Edge for slide on lid (box back)
+ - m Edge for slide on lid (left)
+ - M Edge for slide on lid (box left)
+ - n Edge for slide on lid (right)
+ - M Edge for slide on lid (box left)
+ - o Edge with chest hinge
+ - O Edge with chest hinge (other end)
+ - p Edge with chest hinge
+ - P Edge with chest hinge (other end)
+ - q Edge with pins for an chest hinge
+ - Q Edge opposing a chest hinge
+ - R Rack (and pinion) Edge
+ - s Stackable (bottom, finger joint holes)
+ - S Stackable (top)
+ - u Edge with cabinet hinges
+ - U Edge with cabinet hinges top side
+ - v Edge with cabinet hinges for 90° lid
+ - V Edge with cabinet hinges 90° lid
+
+
+ - c Abstract Edge Class
+ - C Abstract Edge Class
+ - d Edge (parallel slot wall Holes)
+ - D Edge (parallel slot wall Holes)
+ - e Straight Edge
+ - E Straight Edge (outset by thickness)
+ - f Finger Joint
+ - F Finger Joint (opposing side)
+ - g Corrugated edge useful as an gipping area
+ - h Edge (parallel Finger Joint Holes)
+ - i Straight edge with hinge eye
+ - I Edge with hinge pin
+ - j Straight edge with hinge eye (other end)
+ - J Edge with hinge pin (other end)
+ - k Straight edge with hinge eye (both ends)
+ - K Edge with hinge pin (both ends)
+ - l Edge for slide on lid (back)
+ - L Edge for slide on lid (box back)
+ - m Edge for slide on lid (left)
+ - M Edge for slide on lid (box left)
+ - n Edge for slide on lid (right)
+ - M Edge for slide on lid (box left)
+ - o Edge with chest hinge
+ - O Edge with chest hinge (other end)
+ - p Edge with chest hinge
+ - P Edge with chest hinge (other end)
+ - q Edge with pins for an chest hinge
+ - Q Edge opposing a chest hinge
+ - R Rack (and pinion) Edge
+ - s Stackable (bottom, finger joint holes)
+ - S Stackable (top)
+ - u Edge with cabinet hinges
+ - U Edge with cabinet hinges top side
+ - v Edge with cabinet hinges for 90° lid
+ - V Edge with cabinet hinges 90° lid
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 3.2
+ 5
+ 2
+
+ - inside
+ - outside
+
+ 1.5
+ 0.05
+ 2.0
+
+
+
+ 5
+ 0.1
+ 3.0
+
+
+
+ 50
+ 1.5
+ 0.2
+ 3
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 20.0
+ 0.0
+ 3.0
+ 20.0
+
+
+
+ True
+
+ - wave
+ - bumps
+
+ 0.3
+
+
+
+ 0
+ False
+ 0.5
+
+ - outset
+ - flush
+
+ 2
+ 0
+ 1
+
+
+
+ 1.0
+ 2.0
+ 0.1
+
+
+
+ 90.0
+ True
+
+ - both
+ - none
+ - left
+ - right
+
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 3.0
+ 0.05
+ 2.0
+ 1.0
+
+
+
+ 60
+ 2.0
+ 1.0
+ 4.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.RegularBox.inx b/boxes.py.RegularBox.inx
new file mode 100644
index 0000000..a413ad0
--- /dev/null
+++ b/boxes.py.RegularBox.inx
@@ -0,0 +1,59 @@
+
+
+ <_name>RegularBox
+ boxes.exe
+ info.festi.boxes.py.RegularBox
+
+ regularbox
+
+
+
+ 100.0
+ True
+ 50.0
+ 5
+
+ - none
+ - hole
+ - angled hole
+ - angled lid
+ - angled lid2
+ - round lid
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.RobotArm.inx b/boxes.py.RobotArm.inx
new file mode 100644
index 0000000..b46778f
--- /dev/null
+++ b/boxes.py.RobotArm.inx
@@ -0,0 +1,122 @@
+
+
+ <_name>RobotArm
+ boxes.exe
+ info.festi.boxes.py.RobotArm
+
+ robotarm
+
+
+
+
+ - none
+ - RobotArmMM
+ - RobotArmMm
+ - RobotArmUU
+ - RobotArmUu
+ - RobotArmMu
+
+
+ - Servo9g
+
+
+ - Servo9g
+
+ 50.0
+
+ - none
+ - RobotArmMM
+ - RobotArmMm
+ - RobotArmUU
+ - RobotArmUu
+ - RobotArmMu
+
+
+ - Servo9g
+
+
+ - Servo9g
+
+ 50.0
+
+ - none
+ - RobotArmMM
+ - RobotArmMm
+ - RobotArmUU
+ - RobotArmUu
+ - RobotArmMu
+
+
+ - Servo9g
+
+
+ - Servo9g
+
+ 50.0
+
+ - none
+ - RobotArmMM
+ - RobotArmMm
+ - RobotArmUU
+ - RobotArmUu
+ - RobotArmMu
+
+
+ - Servo9g
+
+
+ - Servo9g
+
+ 50.0
+
+ - none
+ - RobotArmMM
+ - RobotArmMm
+ - RobotArmUU
+ - RobotArmUu
+ - RobotArmMu
+
+
+ - Servo9g
+
+
+ - Servo9g
+
+ 50.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Rotary.inx b/boxes.py.Rotary.inx
new file mode 100644
index 0000000..ba490cd
--- /dev/null
+++ b/boxes.py.Rotary.inx
@@ -0,0 +1,53 @@
+
+
+ <_name>Rotary
+ boxes.exe
+ info.festi.boxes.py.Rotary
+
+ rotary
+
+
+
+ 72.0
+ 5.0
+ 6.0
+ 8.0
+ 32.0
+ 7.1
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.RoundedBox.inx b/boxes.py.RoundedBox.inx
new file mode 100644
index 0000000..7666a19
--- /dev/null
+++ b/boxes.py.RoundedBox.inx
@@ -0,0 +1,63 @@
+
+
+ <_name>RoundedBox
+ boxes.exe
+ info.festi.boxes.py.RoundedBox
+
+ roundedbox
+
+
+
+ 100.0
+ 100.0
+ 100.0
+ True
+ 15
+
+ - 1
+ - 2
+ - 3
+ - 4
+
+
+ - closed
+ - hole
+ - lid
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.RoyalGame.inx b/boxes.py.RoyalGame.inx
new file mode 100644
index 0000000..f59eb7c
--- /dev/null
+++ b/boxes.py.RoyalGame.inx
@@ -0,0 +1,48 @@
+
+
+ <_name>RoyalGame
+ boxes.exe
+ info.festi.boxes.py.RoyalGame
+
+ royalgame
+
+
+
+ 200
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.SBCMicroRack.inx b/boxes.py.SBCMicroRack.inx
new file mode 100644
index 0000000..8e5f2a3
--- /dev/null
+++ b/boxes.py.SBCMicroRack.inx
@@ -0,0 +1,68 @@
+
+
+ <_name>SBCMicroRack
+ boxes.exe
+ 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
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 60
+ 2.0
+ 1.0
+ 4.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.ShutterBox.inx b/boxes.py.ShutterBox.inx
new file mode 100644
index 0000000..0cf088d
--- /dev/null
+++ b/boxes.py.ShutterBox.inx
@@ -0,0 +1,62 @@
+
+
+ <_name>ShutterBox
+ boxes.exe
+ info.festi.boxes.py.ShutterBox
+
+ shutterbox
+
+
+
+ 150
+ 100
+ 100
+ 40.0
+
+ - single
+ - double
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 0.5
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 1.05
+ 2.0
+ 0.75
+ 5.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Silverware.inx b/boxes.py.Silverware.inx
new file mode 100644
index 0000000..d3fb04f
--- /dev/null
+++ b/boxes.py.Silverware.inx
@@ -0,0 +1,39 @@
+
+
+ <_name>Silverware
+ boxes.exe
+ info.festi.boxes.py.Silverware
+
+ silverware
+
+
+
+ 250
+ 154
+ 120
+ 30
+ 150
+ 120
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.SlatwallCaliper.inx b/boxes.py.SlatwallCaliper.inx
new file mode 100644
index 0000000..b60670f
--- /dev/null
+++ b/boxes.py.SlatwallCaliper.inx
@@ -0,0 +1,61 @@
+
+
+ <_name>SlatwallCaliper
+ boxes.exe
+ info.festi.boxes.py.SlatwallCaliper
+
+ slatwallcaliper
+
+
+
+ 100
+ 18.0
+ 6.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ - hook
+ - spring
+ - stud
+ - none
+
+ 1.0
+ 2.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.SlatwallChiselHolder.inx b/boxes.py.SlatwallChiselHolder.inx
new file mode 100644
index 0000000..d084f07
--- /dev/null
+++ b/boxes.py.SlatwallChiselHolder.inx
@@ -0,0 +1,69 @@
+
+
+ <_name>SlatwallChiselHolder
+ boxes.exe
+ info.festi.boxes.py.SlatwallChiselHolder
+
+ slatwallchiselholder
+
+
+
+ 120
+ 30.0
+ 30.0
+ 5.0
+ 5.0
+ 6
+
+ - all
+ - odds
+ - everythird
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 1.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ - hook
+ - spring
+ - stud
+ - none
+
+ 1.0
+ 2.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.SlatwallConsole.inx b/boxes.py.SlatwallConsole.inx
new file mode 100644
index 0000000..b308fbf
--- /dev/null
+++ b/boxes.py.SlatwallConsole.inx
@@ -0,0 +1,63 @@
+
+
+ <_name>SlatwallConsole
+ boxes.exe
+ info.festi.boxes.py.SlatwallConsole
+
+ slatwallconsole
+
+
+
+ 100
+ 100
+ True
+ 50
+ 35
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ - hook
+ - spring
+ - stud
+ - none
+
+ 1.0
+ 2.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.SlatwallDrillBox.inx b/boxes.py.SlatwallDrillBox.inx
new file mode 100644
index 0000000..1fa962a
--- /dev/null
+++ b/boxes.py.SlatwallDrillBox.inx
@@ -0,0 +1,62 @@
+
+
+ <_name>SlatwallDrillBox
+ boxes.exe
+ info.festi.boxes.py.SlatwallDrillBox
+
+ slatwalldrillbox
+
+
+
+ 25*6
+ 10:20:30
+ 25:40:60
+ 15.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ - hook
+ - spring
+ - stud
+ - none
+
+ 1.0
+ 2.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.SlatwallEdges.inx b/boxes.py.SlatwallEdges.inx
new file mode 100644
index 0000000..3093901
--- /dev/null
+++ b/boxes.py.SlatwallEdges.inx
@@ -0,0 +1,59 @@
+
+
+ <_name>SlatwallEdges
+ boxes.exe
+ info.festi.boxes.py.SlatwallEdges
+
+ slatwalledges
+
+
+
+ 120
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ - hook
+ - spring
+ - stud
+ - none
+
+ 1.0
+ 2.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.SlatwallPlaneHolder.inx b/boxes.py.SlatwallPlaneHolder.inx
new file mode 100644
index 0000000..6b2cfc8
--- /dev/null
+++ b/boxes.py.SlatwallPlaneHolder.inx
@@ -0,0 +1,62 @@
+
+
+ <_name>SlatwallPlaneHolder
+ boxes.exe
+ info.festi.boxes.py.SlatwallPlaneHolder
+
+ slatwallplaneholder
+
+
+
+ 80
+ 250
+ 30
+ 80
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ - hook
+ - spring
+ - stud
+ - none
+
+ 1.0
+ 2.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.SlatwallPliersHolder.inx b/boxes.py.SlatwallPliersHolder.inx
new file mode 100644
index 0000000..1d9f847
--- /dev/null
+++ b/boxes.py.SlatwallPliersHolder.inx
@@ -0,0 +1,52 @@
+
+
+ <_name>SlatwallPliersHolder
+ boxes.exe
+ info.festi.boxes.py.SlatwallPliersHolder
+
+ slatwallpliersholder
+
+
+
+ 100*3
+ 50
+ 50
+ True
+ 45
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.SlatwallSlottedHolder.inx b/boxes.py.SlatwallSlottedHolder.inx
new file mode 100644
index 0000000..4832388
--- /dev/null
+++ b/boxes.py.SlatwallSlottedHolder.inx
@@ -0,0 +1,70 @@
+
+
+ <_name>SlatwallSlottedHolder
+ boxes.exe
+ info.festi.boxes.py.SlatwallSlottedHolder
+
+ slatwallslottedholder
+
+
+
+ 120
+ 50.0
+ 50.0
+ 5.0
+ 35.0
+ 5.0
+ 6
+
+ - all
+ - odds
+ - everythird
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 1.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ - hook
+ - spring
+ - stud
+ - none
+
+ 1.0
+ 2.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.SlatwallTypeTray.inx b/boxes.py.SlatwallTypeTray.inx
new file mode 100644
index 0000000..bff7138
--- /dev/null
+++ b/boxes.py.SlatwallTypeTray.inx
@@ -0,0 +1,78 @@
+
+
+ <_name>SlatwallTypeTray
+ boxes.exe
+ info.festi.boxes.py.SlatwallTypeTray
+
+ slatwalltypetray
+
+
+
+ 50*3
+ 50*3
+ 100.0
+ 0.0
+ True
+
+ - F Finger Joint (opposing side)
+ - h Edge (parallel Finger Joint Holes)
+ - s Stackable (bottom, finger joint holes)
+ - e Straight Edge
+
+ 0.0
+ 0.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 60
+ 2.0
+ 1.0
+ 4.0
+
+
+
+
+ - hook
+ - spring
+ - stud
+ - none
+
+ 1.0
+ 2.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.SlatwallWrenchHolder.inx b/boxes.py.SlatwallWrenchHolder.inx
new file mode 100644
index 0000000..3a30dd2
--- /dev/null
+++ b/boxes.py.SlatwallWrenchHolder.inx
@@ -0,0 +1,66 @@
+
+
+ <_name>SlatwallWrenchHolder
+ boxes.exe
+ info.festi.boxes.py.SlatwallWrenchHolder
+
+ slatwallwrenchholder
+
+
+
+ 100
+ 30.0
+ 11
+ 8.0
+ 25.0
+ 3.0
+ 5.0
+ 0.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ - hook
+ - spring
+ - stud
+ - none
+
+ 1.0
+ 2.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.Stachel.inx b/boxes.py.Stachel.inx
new file mode 100644
index 0000000..2edf0d4
--- /dev/null
+++ b/boxes.py.Stachel.inx
@@ -0,0 +1,36 @@
+
+
+ <_name>Stachel
+ boxes.exe
+ info.festi.boxes.py.Stachel
+
+ stachel
+
+
+
+ 115.0
+ 25.0
+ 7.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.StorageRack.inx b/boxes.py.StorageRack.inx
new file mode 100644
index 0000000..b68fd82
--- /dev/null
+++ b/boxes.py.StorageRack.inx
@@ -0,0 +1,70 @@
+
+
+ <_name>StorageRack
+ boxes.exe
+ info.festi.boxes.py.StorageRack
+
+ storagerack
+
+
+
+ 200
+ 30
+ 100.0
+ 50*3
+ True
+
+ - F Finger Joint (opposing side)
+ - h Edge (parallel Finger Joint Holes)
+ - s Stackable (bottom, finger joint holes)
+ - e Straight Edge
+
+
+ - F Finger Joint (opposing side)
+ - h Edge (parallel Finger Joint Holes)
+ - e Straight Edge
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 60
+ 2.0
+ 1.0
+ 4.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.StorageShelf.inx b/boxes.py.StorageShelf.inx
new file mode 100644
index 0000000..c509f97
--- /dev/null
+++ b/boxes.py.StorageShelf.inx
@@ -0,0 +1,140 @@
+
+
+ <_name>StorageShelf
+ boxes.exe
+ info.festi.boxes.py.StorageShelf
+
+ storageshelf
+
+
+
+ 100.0
+ 50*3
+ 50*3
+ True
+
+ - F Finger Joint (opposing side)
+ - h Edge (parallel Finger Joint Holes)
+ - s Stackable (bottom, finger joint holes)
+ - e Straight Edge
+
+
+ - e Straight Edge
+ - f Finger Joint
+ - F Finger Joint (opposing side)
+ - c Abstract Edge Class
+ - E Straight Edge (outset by thickness)
+ - S Stackable (top)
+ - i Straight edge with hinge eye
+ - k Straight edge with hinge eye (both ends)
+ - v Edge with cabinet hinges for 90° lid
+ - f Finger Joint
+ - L Edge for slide on lid (box back)
+ - t Triangle for handle
+
+ 0.0
+ False
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 0.5
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 60
+ 2.0
+ 1.0
+ 4.0
+
+
+
+ 0
+ False
+ 0.5
+
+ - outset
+ - flush
+
+ 2
+ 0
+ 1
+
+
+
+ 3.2
+ 5
+ 2
+
+ - inside
+ - outside
+
+ 1.5
+ 0.05
+ 2.0
+
+
+
+ 90.0
+ True
+
+ - both
+ - none
+ - left
+ - right
+
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 3.0
+ 0.05
+ 2.0
+ 1.0
+
+
+
+ 5
+ 0.1
+ 3.0
+
+
+
+ 150.0
+ 2.0
+ 30.0
+ 1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.TrafficLight.inx b/boxes.py.TrafficLight.inx
new file mode 100644
index 0000000..7f0c45b
--- /dev/null
+++ b/boxes.py.TrafficLight.inx
@@ -0,0 +1,52 @@
+
+
+ <_name>TrafficLight
+ boxes.exe
+ info.festi.boxes.py.TrafficLight
+
+ trafficlight
+
+
+
+ 100.0
+ 100
+ 50
+ 3
+ True
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.TrayInsert.inx b/boxes.py.TrayInsert.inx
new file mode 100644
index 0000000..97a6971
--- /dev/null
+++ b/boxes.py.TrayInsert.inx
@@ -0,0 +1,37 @@
+
+
+ <_name>TrayInsert
+ boxes.exe
+ info.festi.boxes.py.TrayInsert
+
+ trayinsert
+
+
+
+ 50*3
+ 50*3
+ 100.0
+ True
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.TwoPiece.inx b/boxes.py.TwoPiece.inx
new file mode 100644
index 0000000..07c09f8
--- /dev/null
+++ b/boxes.py.TwoPiece.inx
@@ -0,0 +1,52 @@
+
+
+ <_name>TwoPiece
+ boxes.exe
+ info.festi.boxes.py.TwoPiece
+
+ twopiece
+
+
+
+ 100.0
+ 100.0
+ 100.0
+ True
+ 0.05
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.TypeTray.inx b/boxes.py.TypeTray.inx
new file mode 100644
index 0000000..3a7847b
--- /dev/null
+++ b/boxes.py.TypeTray.inx
@@ -0,0 +1,141 @@
+
+
+ <_name>TypeTray
+ boxes.exe
+ info.festi.boxes.py.TypeTray
+
+ typetray
+
+
+
+ 50*3
+ 50*3
+ 100.0
+ 0.0
+ True
+
+ - F Finger Joint (opposing side)
+ - h Edge (parallel Finger Joint Holes)
+ - s Stackable (bottom, finger joint holes)
+ - e Straight Edge
+
+
+ - e Straight Edge
+ - f Finger Joint
+ - F Finger Joint (opposing side)
+ - c Abstract Edge Class
+ - E Straight Edge (outset by thickness)
+ - S Stackable (top)
+ - i Straight edge with hinge eye
+ - k Straight edge with hinge eye (both ends)
+ - v Edge with cabinet hinges for 90° lid
+ - f Finger Joint
+ - L Edge for slide on lid (box back)
+ - t Triangle for handle
+
+ 30
+ 70
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 0.5
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 60
+ 2.0
+ 1.0
+ 4.0
+
+
+
+ 0
+ False
+ 0.5
+
+ - outset
+ - flush
+
+ 2
+ 0
+ 1
+
+
+
+ 3.2
+ 5
+ 2
+
+ - inside
+ - outside
+
+ 1.5
+ 0.05
+ 2.0
+
+
+
+ 90.0
+ True
+
+ - both
+ - none
+ - left
+ - right
+
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 3.0
+ 0.05
+ 2.0
+ 1.0
+
+
+
+ 5
+ 0.1
+ 3.0
+
+
+
+ 150.0
+ 2.0
+ 30.0
+ 1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.UBox.inx b/boxes.py.UBox.inx
new file mode 100644
index 0000000..772987a
--- /dev/null
+++ b/boxes.py.UBox.inx
@@ -0,0 +1,144 @@
+
+
+ <_name>UBox
+ boxes.exe
+ info.festi.boxes.py.UBox
+
+ ubox
+
+
+
+
+ - e Straight Edge
+ - f Finger Joint
+ - F Finger Joint (opposing side)
+ - c Abstract Edge Class
+ - E Straight Edge (outset by thickness)
+ - S Stackable (top)
+ - i Straight edge with hinge eye
+ - k Straight edge with hinge eye (both ends)
+ - v Edge with cabinet hinges for 90° lid
+ - f Finger Joint
+ - L Edge for slide on lid (box back)
+ - t Triangle for handle
+
+ 100.0
+ 100.0
+ 100.0
+ 30.0
+
+ - default (none)
+ - chest
+ - flat
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 60
+ 2.0
+ 1.0
+ 4.0
+
+
+
+ 0
+ False
+ 0.5
+
+ - outset
+ - flush
+
+ 2
+ 0
+ 1
+
+
+
+ 3.2
+ 5
+ 2
+
+ - inside
+ - outside
+
+ 1.5
+ 0.05
+ 2.0
+
+
+
+ 90.0
+ True
+
+ - both
+ - none
+ - left
+ - right
+
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 3.0
+ 0.05
+ 2.0
+ 1.0
+
+
+
+ 5
+ 0.1
+ 3.0
+
+
+
+ 150.0
+ 2.0
+ 30.0
+ 0.0
+
+
+
+ 1.05
+ 1.0
+ 0.5
+ 5.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.UnevenHeightBox.inx b/boxes.py.UnevenHeightBox.inx
new file mode 100644
index 0000000..55f1ca9
--- /dev/null
+++ b/boxes.py.UnevenHeightBox.inx
@@ -0,0 +1,61 @@
+
+
+ <_name>UnevenHeightBox
+ boxes.exe
+ info.festi.boxes.py.UnevenHeightBox
+
+ unevenheightbox
+
+
+
+
+ - F Finger Joint (opposing side)
+ - h Edge (parallel Finger Joint Holes)
+ - s Stackable (bottom, finger joint holes)
+ - e Straight Edge
+
+ 100.0
+ 100.0
+ True
+ 50
+ 50
+ 100
+ 100
+ False
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.UniversalBox.inx b/boxes.py.UniversalBox.inx
new file mode 100644
index 0000000..fee877a
--- /dev/null
+++ b/boxes.py.UniversalBox.inx
@@ -0,0 +1,149 @@
+
+
+ <_name>UniversalBox
+ boxes.exe
+ info.festi.boxes.py.UniversalBox
+
+ universalbox
+
+
+
+
+ - e Straight Edge
+ - f Finger Joint
+ - F Finger Joint (opposing side)
+ - c Abstract Edge Class
+ - E Straight Edge (outset by thickness)
+ - S Stackable (top)
+ - i Straight edge with hinge eye
+ - k Straight edge with hinge eye (both ends)
+ - v Edge with cabinet hinges for 90° lid
+ - f Finger Joint
+ - L Edge for slide on lid (box back)
+ - t Triangle for handle
+
+
+ - F Finger Joint (opposing side)
+ - h Edge (parallel Finger Joint Holes)
+ - s Stackable (bottom, finger joint holes)
+ - e Straight Edge
+
+ 100.0
+ 100.0
+ 100.0
+
+ - default (none)
+ - chest
+ - flat
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+ 60
+ 2.0
+ 1.0
+ 4.0
+
+
+
+ 0
+ False
+ 0.5
+
+ - outset
+ - flush
+
+ 2
+ 0
+ 1
+
+
+
+ 3.2
+ 5
+ 2
+
+ - inside
+ - outside
+
+ 1.5
+ 0.05
+ 2.0
+
+
+
+ 90.0
+ True
+
+ - both
+ - none
+ - left
+ - right
+
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 3.0
+ 0.05
+ 2.0
+ 1.0
+
+
+
+ 5
+ 0.1
+ 3.0
+
+
+
+ 150.0
+ 2.0
+ 30.0
+ 1
+
+
+
+ 1.05
+ 1.0
+ 0.5
+ 5.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.WaivyKnob.inx b/boxes.py.WaivyKnob.inx
new file mode 100644
index 0000000..d521c9c
--- /dev/null
+++ b/boxes.py.WaivyKnob.inx
@@ -0,0 +1,39 @@
+
+
+ <_name>WaivyKnob
+ boxes.exe
+ info.festi.boxes.py.WaivyKnob
+
+ waivyknob
+
+
+
+ 50.0
+ 20
+ 45.0
+ 6.0
+ 1.0
+ 10.0
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/boxes.py.WineRack.inx b/boxes.py.WineRack.inx
new file mode 100644
index 0000000..c728f6e
--- /dev/null
+++ b/boxes.py.WineRack.inx
@@ -0,0 +1,56 @@
+
+
+ <_name>WineRack
+ boxes.exe
+ info.festi.boxes.py.WineRack
+
+ winerack
+
+
+
+ 400
+ 300
+ 210
+ 46.0
+
+ - minimal
+ - no_verticals
+ - all
+
+
+
+
+ 3.0
+ 0.0
+ False
+ 100
+ 0.1
+
+
+
+ 90.0
+
+ - rectangular
+ - springs
+
+ 2.0
+ 1.0
+ 2.0
+ 0.0
+ 2.0
+ 1.0
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/fablabchemnitz_CardLayoutGuides.inx b/fablabchemnitz_CardLayoutGuides.inx
new file mode 100644
index 0000000..e1e39de
--- /dev/null
+++ b/fablabchemnitz_CardLayoutGuides.inx
@@ -0,0 +1,31 @@
+
+
+ <_name>Cards Layout Guides
+ fablabchemnitz.de.CardLayoutGuides
+ fablabchemnitz_CardLayoutGuides.py
+ inkex.py
+
+ - Simple Grid
+ - Folding Grid
+
+ 60
+ 30
+
+ - Horizontal
+ - Vertical
+
+ 2
+ 2
+ 15
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fablabchemnitz_CardLayoutGuides.py b/fablabchemnitz_CardLayoutGuides.py
new file mode 100644
index 0000000..6786f5b
--- /dev/null
+++ b/fablabchemnitz_CardLayoutGuides.py
@@ -0,0 +1,485 @@
+#!/usr/bin/env python2
+
+
+# Copyright 2016 Luke Phillips (lukerazor@hotmail.com)
+# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+# Extension dirs
+# linux:~/.config/inkscape/extensions
+# windows: [Drive]:\Program Files\Inkscape\share\extensions
+
+import inkex
+import simplestyle
+import copy
+import math
+
+FOLD_GAP = 5
+CROP_GAP = 2
+CROP_LENGTH = 3
+
+CSNS = ""
+
+inkex.NSS[u'cs'] = u'http://www.razorfoss.org/cardlayoutguides/'
+
+def PrintDebug(string):
+ inkex.debug( _(str(string)) )
+
+def RoundAndDeduplicatePoints(points):
+ return sorted(list(set(map(lambda x: round(x, 3), points))))
+
+class Point():
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+
+ def rotate(self, angle, origin):
+ """
+ Rotate a point counterclockwise by a given angle around a given origin.
+
+ The angle should be given in degrees.
+ """
+ rads = math.radians(angle)
+ newX = origin.x + math.cos(rads) * (self.x - origin.x) - math.sin(rads) * (self.y - origin.y)
+ newY = origin.y + math.sin(rads) * (self.x - origin.x) + math.cos(rads) * (self.y - origin.y)
+
+ return Point(newX, newY)
+
+ def add(self, point):
+ return Point(self.x + point.x, self.y + point.y)
+
+ @staticmethod
+ def parsePoint(pointString):
+ x, y = map(lambda v: float(v), pointString.split(","))
+ return Point(x, y)
+
+ @staticmethod
+ def parse(pointString, orientationString=None):
+ p1 = Point.parsePoint(pointString)
+ p = Point(p1.x, p1.y)
+
+ if orientationString != None:
+ po = Point.parsePoint(orientationString)
+ p = p1.add(po.rotate(270, Point(0, 0)))
+
+ return p
+
+class LineGeneratorBase(object):
+ def __init__(self, cardWidth, cardHeight, cardMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc):
+ self.UnitConverterFunc = unitConverterFunc
+ self.CardWidth = cardWidth
+ self.CardHeight = cardHeight
+ self.CardMargin = cardMargin
+ self.BleedMargin = bleedMargin
+
+ self.PageWidth = pageWidth
+ self.PageHeight = pageHeight
+ self.PageMargin = pageMargin
+
+ self.ContainerWidth = -1
+ self.ContainerHeight = -1
+
+ self.GuideOffsetsWithFold = [
+ 0,
+ self.BleedMargin, self.CardMargin, self.CardHeight - 2*self.CardMargin, self.CardMargin, self.BleedMargin,
+ 2*FOLD_GAP,
+ self.BleedMargin, self.CardMargin, self.CardHeight - 2*self.CardMargin, self.CardMargin, self.BleedMargin
+ ]
+
+ self.GuideOffsetsNoFold = [
+ 0,
+ self.BleedMargin, self.CardMargin,
+ self.CardWidth - 2*self.CardMargin,
+ self.CardMargin, self.BleedMargin
+ ]
+
+ def CalcPageLeftMargin(self):
+ return (self.PageWidth - self.ContentWidth) / 2.0
+
+ def CalcPageBottomMargin(self):
+ return (self.PageHeight - self.ContentHeight) / 2.0
+
+ def DrawGuide(self, xmlParent, xpos, ypos):
+ posString = "{},{}".format(xpos, ypos)
+ attribs = {'position': posString, 'orientation': posString}
+
+ inkex.etree.SubElement(xmlParent, inkex.addNS('guide',"sodipodi"), attribs)
+
+ def ConvertPoint(self, p):
+ # convert point into svg approriate values, including catering for inkscapes "alternative" axis sytem ie 0, 0 is bottom left not top left
+ newX = self.UnitConverterFunc("{}mm".format(p.x))
+ newY = self.PageHeight - self.UnitConverterFunc("{}mm".format(p.y))
+
+ return Point(newX, newY)
+
+ def DrawLine(self, xmlParent, p1, p2):
+ cp1 = self.ConvertPoint(p1)
+ cp2 = self.ConvertPoint(p2)
+
+ pathStr = "M {},{} {}, {}".format(cp1.x, cp1.y, cp2.x, cp2.y)
+ style = {'stroke': '#000000', 'stroke-width': self.UnitConverterFunc('0.25mm'), 'fill': 'none'}
+ attribs = {'style': simplestyle.formatStyle(style), 'd': pathStr}
+
+ inkex.etree.SubElement(xmlParent, inkex.addNS('path','svg'), attribs )
+
+ def DrawVerticleGuides(self, xmlParent, positions, gap):
+ curPos = self.CalcPageLeftMargin()
+ lastPos = -1
+ while curPos + self.ContainerWidth <= self.PageWidth - self.PageMargin:
+ for offset in positions:
+ curPos += offset
+ if curPos != lastPos: # don't double draw
+ self.DrawGuide(xmlParent, curPos, 0)
+
+ lastPos = curPos
+
+ curPos += gap
+
+ def DrawHorizontalGuides(self, xmlParent, positions, gap):
+ curPos = self.CalcPageBottomMargin()
+ lastPos = -1
+ while curPos + self.ContainerHeight <= self.PageHeight - self.PageMargin:
+ for offset in positions:
+ curPos += offset
+ if curPos != lastPos: # don't double draw
+ self.DrawGuide(xmlParent, 0, curPos)
+
+ lastPos = curPos
+
+ curPos += gap
+
+ def GenerateFoldLines(self, xmlParent):
+ lines = self.GetFoldLinePositions()
+
+ for line in lines:
+ self.DrawLine(xmlParent, line[0], line[1])
+
+ def GenerateCropMarks(self, xmlParent):
+ lines = self.GetCropMarkLines()
+
+ for line in lines:
+ self.DrawLine(xmlParent, line[0], line[1])
+
+ @staticmethod
+ def CreateLineGenerator(layout, orientation, cardWidth, cardHeight, cardMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc):
+ if layout == "SIMPLE":
+ return SimpleGridLineGenerator(orientation, cardWidth, cardHeight, cardMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc)
+
+ if orientation == "HORIZONTAL":
+ return LineGeneratorForHorizontalCards(cardWidth, cardHeight, cardMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc)
+
+ return LineGeneratorForVerticalCards(cardWidth, cardHeight, cardMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc)
+
+class SimpleGridLineGenerator(LineGeneratorBase):
+ def __init__(self, orientation, cardWidth, cardHeight, cardMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc):
+ if orientation == "HORIZONTAL":
+ super(SimpleGridLineGenerator, self).__init__(cardHeight, cardWidth, cardMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc)
+ else:
+ super(SimpleGridLineGenerator, self).__init__(cardWidth, cardHeight, cardMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc)
+
+ self.ContainerWidth = self.CardWidth + 2 * bleedMargin
+ self.ContainerHeight = self.CardHeight + 2 * bleedMargin
+
+ # num across
+ # num down
+ self.NumContainersAcross = int((self.PageWidth - 2*self.PageMargin) // self.ContainerWidth) # round down division
+ self.NumContainersDown = int((self.PageHeight - 2*self.PageMargin) // self.ContainerHeight) # round down division
+
+ # content sizes
+ self.ContentWidth = self.NumContainersAcross * self.ContainerWidth
+ self.ContentHeight = self.NumContainersDown * self.ContainerHeight
+
+ def GenerateGuides(self, xmlParent):
+ horizontalOffsets = self.GuideOffsetsNoFold = [
+ 0,
+ self.BleedMargin, self.CardMargin,
+ self.CardWidth - 2*self.CardMargin,
+ self.CardMargin, self.BleedMargin
+ ]
+
+ verticalOffsets = self.GuideOffsetsNoFold = [
+ 0,
+ self.BleedMargin, self.CardMargin,
+ self.CardHeight - 2*self.CardMargin,
+ self.CardMargin, self.BleedMargin
+ ]
+
+ self.DrawVerticleGuides(xmlParent, horizontalOffsets, 0)
+ self.DrawHorizontalGuides(xmlParent, verticalOffsets, 0)
+
+ def GetFoldLinePositions(self):
+ return [] # no fold lines in simple grid
+
+ def GetCropMarkLines(self):
+ lines = []
+
+ leftMargin = self.CalcPageLeftMargin()
+ bottomMargin = self.CalcPageBottomMargin()
+
+ #determine all horizontal crop marks, duplicates possible
+ # figure out the ypos
+ horizontal_ypos = []
+ for idx in range(self.NumContainersDown):
+ bottomY = self.BleedMargin
+ topY = bottomY + self.CardHeight
+ containerOffset = bottomMargin + idx*self.ContainerHeight
+
+ horizontal_ypos.append(containerOffset + bottomY)
+ horizontal_ypos.append(containerOffset + topY)
+
+ horizontal_ypos = RoundAndDeduplicatePoints(horizontal_ypos) # remove duplicate positions
+
+ horizontal_xpos = [leftMargin - CROP_GAP, self.PageWidth - leftMargin + CROP_GAP + CROP_LENGTH]
+ for xpos in horizontal_xpos:
+ for ypos in horizontal_ypos:
+ lines.append([
+ Point(xpos - CROP_LENGTH, ypos),
+ Point(xpos, ypos)
+ ])
+
+ #determine all vertical crop marks, duplicates possible
+ # figure out the xpos
+ vertical_xpos = []
+ for idx in range(self.NumContainersAcross):
+ leftX = self.BleedMargin
+ rightX = leftX + self.CardWidth
+ containerOffset = leftMargin + idx*self.ContainerWidth
+
+ vertical_xpos.append(containerOffset + leftX)
+ vertical_xpos.append(containerOffset + rightX)
+
+ vertical_xpos = RoundAndDeduplicatePoints(vertical_xpos) # remove duplicate positions
+
+ vertical_ypos = [bottomMargin - CROP_GAP, self.PageHeight - bottomMargin + CROP_GAP + CROP_LENGTH]
+ for xpos in vertical_xpos:
+ for ypos in vertical_ypos:
+ lines.append([
+ Point(xpos, ypos),
+ Point(xpos, ypos - CROP_LENGTH)
+ ])
+
+ return lines
+
+class LineGeneratorForVerticalCards(LineGeneratorBase):
+ def __init__(self, cardWidth, cardHeight, cardMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc):
+ super(LineGeneratorForVerticalCards, self).__init__(cardWidth, cardHeight, cardMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc)
+
+ self.ContainerWidth = cardWidth + 2*bleedMargin
+ self.ContainerHeight = 2*(cardHeight + 2*bleedMargin + FOLD_GAP)
+
+ # num across
+ self.NumContainersAcross = int((self.PageWidth - 2*self.PageMargin) // self.ContainerWidth) # round down division
+
+ # num down
+ contentHeight = lambda n: n * self.ContainerHeight + (n - 1)*(2*FOLD_GAP)
+ workingHeight = self.PageHeight - 2*self.PageMargin
+
+ self.NumContainersDown = int(workingHeight // self.ContainerHeight) # round down division for nominal value
+ if contentHeight(self.NumContainersDown) > workingHeight:
+ self.NumContainersDown -= 1
+
+ # content sizes
+ self.ContentWidth = self.NumContainersAcross * self.ContainerWidth
+ self.ContentHeight = contentHeight(self.NumContainersDown)
+
+ def GenerateGuides(self, xmlParent):
+ self.DrawVerticleGuides(xmlParent, self.GuideOffsetsNoFold, 0)
+ self.DrawHorizontalGuides(xmlParent, self.GuideOffsetsWithFold, 2*FOLD_GAP)
+
+ def GetFoldLinePositions(self):
+ lines = []
+ leftMargin = self.CalcPageLeftMargin()
+
+ for idx in range(self.NumContainersDown):
+ foldY = self.CalcPageBottomMargin() + idx*(self.ContainerHeight + 2*FOLD_GAP) + self.ContainerHeight/2
+ lines.append([Point(leftMargin, foldY), Point(self.PageWidth - leftMargin, foldY)])
+
+ return lines
+
+ def GetCropMarkLines(self):
+ lines = []
+
+ leftMargin = self.CalcPageLeftMargin()
+ bottomMargin = self.CalcPageBottomMargin()
+ vertical_ypos = []
+ # determine all of the hornzontal facing crop marks, no duplicates possible
+ for idx in range(self.NumContainersDown):
+ bottomY = self.BleedMargin
+ topY = bottomY + self.CardHeight
+ containerOffset = bottomMargin + idx*(self.ContainerHeight + 2*FOLD_GAP)
+ vertical_ypos += [
+ containerOffset - CROP_GAP,
+ containerOffset + 2*self.BleedMargin + 2*self.CardMargin + self.CardHeight + CROP_GAP + CROP_LENGTH
+ ] # stash for later
+
+ for ypos in [containerOffset + bottomY, containerOffset + topY]:
+ for xpos in [leftMargin - CROP_GAP, self.PageWidth - leftMargin + CROP_GAP + CROP_LENGTH]:
+ lines.append([
+ Point(xpos, ypos),
+ Point(xpos - CROP_LENGTH, ypos)
+ ])
+
+ #determine all vertical crop marks, duplicates possible
+ # figure out the xpos
+ vertical_xpos = []
+ for idx in range(self.NumContainersAcross):
+ leftX = self.BleedMargin
+ rightX = leftX + self.CardWidth
+ containerOffset = leftMargin + idx*self.ContainerWidth
+
+ vertical_xpos.append(containerOffset + leftX)
+ vertical_xpos.append(containerOffset + rightX)
+
+ vertical_xpos = list(set(vertical_xpos)) # remove duplicate positions
+
+ for xpos in vertical_xpos:
+ for ypos in vertical_ypos:
+ lines.append([
+ Point(xpos, ypos),
+ Point(xpos, ypos - CROP_LENGTH)
+ ])
+
+ return lines
+
+class LineGeneratorForHorizontalCards(LineGeneratorBase):
+ def __init__(self, cardWidth, cardHeight, cardMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc):
+ super(LineGeneratorForHorizontalCards, self).__init__(cardWidth, cardHeight, cardMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc)
+
+ self.ContainerWidth = 2*(cardHeight + 2 * bleedMargin + FOLD_GAP)
+ self.ContainerHeight = cardWidth + 2 * bleedMargin
+
+ # num across
+ contentWidth = lambda n: n * self.ContainerWidth + (n - 1)*(2*FOLD_GAP)
+ workingWidth = self.PageWidth - 2*self.PageMargin
+
+ self.NumContainersAcross = int(workingWidth // self.ContainerWidth) # round down division for nominal value
+ if contentWidth(self.NumContainersAcross) > workingWidth:
+ self.NumContainersAcross -= 1
+
+ # num down
+ self.NumContainersDown = int((self.PageHeight - 2*self.PageMargin) // self.ContainerHeight) # round down division
+
+ # content sizes
+ self.ContentWidth = contentWidth(self.NumContainersAcross)
+ self.ContentHeight = self.NumContainersDown * self.ContainerHeight
+
+ def GenerateGuides(self, xmlParent):
+ self.DrawVerticleGuides(xmlParent, self.GuideOffsetsWithFold, 2*FOLD_GAP)
+ self.DrawHorizontalGuides(xmlParent, self.GuideOffsetsNoFold, 0)
+
+ def GetFoldLinePositions(self):
+ lines = []
+ bottomMargin = self.CalcPageBottomMargin()
+
+ for idx in range(self.NumContainersAcross):
+ foldX = self.CalcPageLeftMargin() + idx*(self.ContainerWidth + 2*FOLD_GAP) + self.ContainerWidth/2
+ lines.append([Point(foldX, bottomMargin), Point(foldX, self.PageHeight - bottomMargin)])
+
+ return lines
+
+ def GetCropMarkLines(self):
+ lines = []
+
+ leftMargin = self.CalcPageLeftMargin()
+ bottomMargin = self.CalcPageBottomMargin()
+ horizontal_xpos = []
+
+ # determine all of the vertical facing crop marks, no duplicates possible
+ for idx in range(self.NumContainersAcross):
+ leftX = self.BleedMargin
+ rightX = leftX + self.CardHeight
+ containerOffset = leftMargin + idx*(self.ContainerWidth + 2*FOLD_GAP)
+ horizontal_xpos += [
+ containerOffset - CROP_GAP,
+ containerOffset + 2*self.BleedMargin + 2*self.CardMargin + self.CardHeight + CROP_GAP + CROP_LENGTH
+ ] # stash for later
+
+ for xpos in [containerOffset + leftX, containerOffset + rightX]:
+ for ypos in [bottomMargin - CROP_GAP, self.PageHeight - bottomMargin + CROP_GAP + CROP_LENGTH]:
+ lines.append([
+ Point(xpos, ypos),
+ Point(xpos, ypos - CROP_LENGTH)
+ ])
+
+ #determine all horizontal crop marks, duplicates possible
+ # figure out the xpos
+ horizontal_ypos = []
+ for idx in range(self.NumContainersDown):
+ bottomY = self.BleedMargin
+ topY = bottomY + self.CardWidth
+ containerOffset = bottomMargin + idx*self.ContainerHeight
+
+ horizontal_ypos.append(containerOffset + bottomY)
+ horizontal_ypos.append(containerOffset + topY)
+
+ horizontal_ypos = RoundAndDeduplicatePoints(horizontal_ypos) # remove duplicate positions
+
+ for ypos in horizontal_ypos:
+ for xpos in horizontal_xpos:
+ lines.append([
+ Point(xpos, ypos),
+ Point(xpos - CROP_LENGTH, ypos)
+ ])
+
+ return lines
+
+class FoldedCardLayoutGuidesEffect(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+
+ self.OptionParser.add_option('-l', '--layout', action = 'store', type = 'string', dest = 'Layout')
+ self.OptionParser.add_option('-w', '--card_width', action = 'store', type = 'float', dest = 'CardWidth')
+ self.OptionParser.add_option('-d', '--card_height', action = 'store', type = 'float', dest = 'CardHeight')
+ self.OptionParser.add_option('-o', '--orientation', action = 'store', type = 'string', dest = 'Orientation')
+ self.OptionParser.add_option('-c', '--card_margin', action = 'store', type = 'float', dest = 'CardMargin')
+ self.OptionParser.add_option('-b', '--bleed_margin', action = 'store', type = 'float', dest = 'BleedMargin')
+ self.OptionParser.add_option('-p', '--page_margin', action = 'store', type = 'float', dest = 'PageMargin')
+
+ def effect(self):
+ # find dimensions of page
+ pageWidth = self.uutounit(self.unittouu(self.getDocumentWidth()), "mm")
+ pageHeight = self.uutounit(self.unittouu(self.getDocumentHeight()), "mm")
+
+ opt = self.options
+
+ guideParent = self.document.xpath('//sodipodi:namedview',namespaces=inkex.NSS)[0]
+
+ ### GUIDES
+ # remove all the existing guides
+ [node.getparent().remove(node) for node in self.document.xpath('//sodipodi:guide',namespaces=inkex.NSS)]
+
+ # create the generator object
+ gen = LineGeneratorBase.CreateLineGenerator(opt.Layout, opt.Orientation, opt.CardWidth, opt.CardHeight, opt.CardMargin, opt.BleedMargin, pageWidth, pageHeight, opt.PageMargin, self.unittouu)
+
+ gen.GenerateGuides(guideParent)
+
+ ### FOLD LINES
+ # remove any existing 'Crop marks' layer
+ [node.getparent().remove(node) for node in self.document.xpath("//svg:g[@inkscape:label='Crop Marks']",namespaces=inkex.NSS)]
+
+ svg = self.document.xpath('//svg:svg', namespaces=inkex.NSS)[0]
+ layer = inkex.etree.SubElement(svg, inkex.addNS('g',"svg"), {})
+ layer.set(inkex.addNS('label', 'inkscape'), "Crop Marks")
+ layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
+ #layer.set(inkex.addNS('insensitive', 'sodipodi'), 'true')
+
+ gen.GenerateFoldLines(layer)
+
+ ### CROP MARKS
+ gen.GenerateCropMarks(layer)
+
+if __name__ == '__main__':
+ effect = FoldedCardLayoutGuidesEffect()
+
+ effect.affect()
diff --git a/fablabchemnitz_CreateTuckbox.inx b/fablabchemnitz_CreateTuckbox.inx
new file mode 100644
index 0000000..99ebe8a
--- /dev/null
+++ b/fablabchemnitz_CreateTuckbox.inx
@@ -0,0 +1,28 @@
+
+
+ <_name>Tuckbox
+ fablabchemnitz.de.CreateTuckbox
+ fablabchemnitz_CreateTuckbox.py
+ inkex.py
+
+ - Tuckbox
+ - Slipcase/Deck band
+ - MatchBox
+
+ 1
+ 63.5
+ 88
+ 20
+ 1.5
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fablabchemnitz_CreateTuckbox.py b/fablabchemnitz_CreateTuckbox.py
new file mode 100644
index 0000000..1f1c721
--- /dev/null
+++ b/fablabchemnitz_CreateTuckbox.py
@@ -0,0 +1,499 @@
+#!/usr/bin/env python2
+
+
+# Copyright 2016 Luke Phillips (lukerazor@hotmail.com)
+# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+# Extension dirs
+# linux:~/.config/inkscape/extensions
+# windows: [Drive]:\Program Files\Inkscape\share\extensions
+
+import inkex
+import simplestyle
+import copy
+
+CSNS = ""
+
+inkex.NSS[u'cs'] = u'http://www.razorfoss.org/tuckboxextension/'
+
+class EffectDimensionProvider():
+ def __init__(self, effect, x = 0, y = 0):
+ self.Effect = effect
+
+ self.Layer = effect.current_layer
+
+ self.Width = effect.options.DeckWidth
+ self.Height = effect.options.DeckHeight
+ self.Depth = effect.options.DeckDepth
+ self.Allowance = effect.options.DeckAllowance
+
+ self.X = x
+ self.Y = y
+
+ def MMtoUU(self, mmval):
+ if hasattr(self.Effect, "unittouu"):
+ return str(self.Effect.unittouu("{0}mm".format(mmval)))
+ else:
+ MM_TO_PIXELS = 3.5433071
+
+ return str(MM_TO_PIXELS * mmval)
+
+ def MaximiseHeight(self):
+ if self.Height < self.Width: # always choose the smallest to be the "width"
+ temp = self.Width
+ self.Width = self.Height
+ self.Height = temp
+
+class BoxBase():
+ def __init__(self, dimensionProvider):
+ #create a group
+ self.DimProvider = dimensionProvider
+ self.Group = inkex.etree.SubElement(dimensionProvider.Layer, inkex.addNS('g','svg'), {} )
+
+ self.Width = dimensionProvider.Width + dimensionProvider.Allowance
+ self.Height = dimensionProvider.Height + dimensionProvider.Allowance
+ self.Depth = dimensionProvider.Depth + dimensionProvider.Allowance
+ self.X = dimensionProvider.X
+ self.Y = dimensionProvider.Y
+ self.MinY = 0
+ self.MinX = 0
+
+ ### init some common sizes ###
+ self.ThumbSize = 20
+
+ # tuck flap size
+ self.FlapOffset = 1.5
+ self.FlapHeight = self.Depth
+ if self.Depth < 7 or self.Depth > 25:
+ self.FlapHeight = 20
+
+ # main flap size
+ self.MainFlapHeight = (4 * self.Depth)/3
+ if self.MainFlapHeight < self.ThumbSize:
+ self.MainFlapHeight = 24
+
+ ### colour ###
+ self.Fill = '#ffffff'
+ self.StrokeWidth = self.DimProvider.MMtoUU(0.5)
+
+ def _CreateRectangleInMillimetres(self, height, width, x, y):
+ style = {'stroke': '#000000', 'stroke-width': self.StrokeWidth, 'fill' : self.Fill}
+ attribs = {'style': simplestyle.formatStyle(style), 'height': self.DimProvider.MMtoUU(height), 'width': self.DimProvider.MMtoUU(width), 'x': self.DimProvider.MMtoUU(x), 'y': self.DimProvider.MMtoUU(y)}
+ inkex.etree.SubElement(self.Group, inkex.addNS('rect','svg'), attribs )
+
+ def _CreatePathinMillimetres(self, cmds):
+ pathStr = ""
+ for cmd in cmds:
+ pathStr += cmd[0] + " "
+ for coord in cmd[1:]:
+ pathStr += self.DimProvider.MMtoUU(coord) + " "
+
+ pathStr += "z"
+ #raise Exception(pathStr)
+
+ style = {'stroke': '#000000', 'stroke-width': self.StrokeWidth, 'fill' : self.Fill}
+ attribs = {'style': simplestyle.formatStyle(style), 'd': pathStr}
+ inkex.etree.SubElement(self.Group, inkex.addNS('path','svg'), attribs )
+
+class SingleFlappedTuckBox(BoxBase):
+ def __init__(self, dimensionProvider):
+ BoxBase.__init__(self, dimensionProvider)
+
+ def Create(self):
+ self.FlapOffset = 1.5
+ self.FlapHeight = 20
+
+ # Figure out some row and column values,
+ # note rows and cols work left to right, top to bottom
+ col5 = self.X - self.Depth
+ col4 = col5 - self.Width
+ col3 = col4 - self.Depth
+ col2 = col3 - self.Width
+ col1 = col2 - self.Depth
+ self.MinX = col1
+
+ row4 = self.Y - self.Depth
+ row3 = row4 - self.Height
+ row2 = row3 - self.Depth
+ row1 = row2 - self.Depth
+ self.MinY = row1
+
+ ### COLUMN 1 ###
+ #create left glue panel
+ self._CreateRectangleInMillimetres(self.Height, self.Depth, col1, row3)
+
+ ### COLUMN 2 ###
+ #create box back print panel
+ self._CreatePathinMillimetres(
+ [
+ ["m", col2, row3],
+ ["h", (self.Width - self.ThumbSize)/2],
+ ["c", 0, (3*self.ThumbSize)/4, self.ThumbSize, (3*self.ThumbSize)/4, self.ThumbSize, 0],
+ ["h", (self.Width - self.ThumbSize)/2],
+ ["v", self.Height],
+ ["h", -self.Width]
+ ])
+
+ #create box bottom glue panel
+ self._CreateRectangleInMillimetres(self.Depth, self.Width, col2, row4)
+
+ ### COLUMN 3 ###
+ #create left flap
+ self._CreatePathinMillimetres(
+ [
+ ["m", col3, row3],
+ ["h", self.Depth],
+ ["l", -self.FlapOffset, -self.FlapHeight],
+ ["h", -(self.Depth - (2*self.FlapOffset))],
+ ])
+
+ #create left print panel
+ self._CreateRectangleInMillimetres(self.Height, self.Depth, col3, row3)
+
+ #create bottom left glue panel
+ self._CreateRectangleInMillimetres(self.Depth, self.Depth, col3, row4)
+
+ ### COLUMN 4 ###
+ #create main flap
+ self._CreatePathinMillimetres(
+ [
+ ["m", col4, row2],
+ ["c", 0, -self.MainFlapHeight, self.Width, -self.MainFlapHeight, self.Width, 0]
+ ])
+
+ #create box top print panel
+ self._CreateRectangleInMillimetres(self.Depth, self.Width, col4, row2)
+
+ #create box front print panel
+ self._CreateRectangleInMillimetres(self.Height, self.Width, col4, row3)
+
+ #create box bottom print panel
+ self._CreateRectangleInMillimetres(self.Depth, self.Width, col4, row4)
+
+ ### COLUMN 5 ###
+ #create right flap
+ self._CreatePathinMillimetres(
+ [
+ ["m", col5, row3],
+ ["h", self.Depth],
+ ["l", -self.FlapOffset, -self.FlapHeight],
+ ["h", -(self.Depth - (2*self.FlapOffset))],
+ ])
+
+ #create right print panel
+ self._CreateRectangleInMillimetres(self.Height, self.Depth, col5, row3)
+
+ #create bottom right glue panel
+ self._CreateRectangleInMillimetres(self.Depth, self.Depth, col5, row4)
+
+class DoubleFlappedTuckBox(BoxBase):
+ def __init__(self, dimensionProvider):
+ BoxBase.__init__(self, dimensionProvider)
+
+ def Create(self):
+
+ # Figure out some row and column values,
+ # note rows and cols work left to right, top to bottom
+ col5 = self.X - self.Depth
+ col4 = col5 - self.Width
+ col3 = col4 - self.Depth
+ col2 = col3 - self.Width
+ col1 = col2 - self.Depth
+ self.MinX = col1
+
+ row5 = self.Y - self.Depth
+ row4 = row5 - self.Depth
+ row3 = row4 - self.Height
+ row2 = row3 - self.Depth
+ row1 = row2 - self.Depth
+ self.MinY = row1
+
+ ### COLUMN 1 ###
+ #create left glue panel
+ self._CreateRectangleInMillimetres(self.Height, self.Depth, col1, row3)
+
+ ### COLUMN 2 ###
+ #create box back print panel
+ self._CreatePathinMillimetres(
+ [
+ ["m", col2, row3],
+ ["h", (self.Width - self.ThumbSize)/2],
+ ["c", 0, (3*self.ThumbSize)/4, self.ThumbSize, (3*self.ThumbSize)/4, self.ThumbSize, 0],
+ ["h", (self.Width - self.ThumbSize)/2],
+ ["v", self.Height],
+ ["h", -(self.Width - self.ThumbSize)/2],
+ ["c", 0, -(3*self.ThumbSize)/4, -self.ThumbSize, -(3*self.ThumbSize)/4, -self.ThumbSize, 0],
+ ["h", -(self.Width - self.ThumbSize)/2]
+ ])
+
+ ### COLUMN 3 ###
+ #create top left flap
+ self._CreatePathinMillimetres(
+ [
+ ["m", col3, row3],
+ ["h", self.Depth],
+ ["l", -self.FlapOffset, -self.FlapHeight],
+ ["h", -(self.Depth - (2*self.FlapOffset))],
+ ])
+
+ #create left print panel
+ self._CreateRectangleInMillimetres(self.Height, self.Depth, col3, row3)
+
+ #create bottom left flap
+ self._CreatePathinMillimetres(
+ [
+ ["m", col3, row4],
+ ["h", self.Depth],
+ ["l", -self.FlapOffset, self.FlapHeight],
+ ["h", -(self.Depth - (2*self.FlapOffset))],
+ ])
+
+ ### COLUMN 4 ###
+ #create top main flap
+ self._CreatePathinMillimetres(
+ [
+ ["m", col4, row2],
+ ["c", 0, -self.MainFlapHeight, self.Width, -self.MainFlapHeight, self.Width, 0]
+ ])
+
+ #create box top print panel
+ self._CreateRectangleInMillimetres(self.Depth, self.Width, col4, row2)
+
+ #create box front print panel
+ self._CreateRectangleInMillimetres(self.Height, self.Width, col4, row3)
+
+ #create box bottom print panel
+ self._CreateRectangleInMillimetres(self.Depth, self.Width, col4, row4)
+
+ #create bottom main flap
+ self._CreatePathinMillimetres(
+ [
+ ["m", col4, row5],
+ ["c", 0, self.MainFlapHeight, self.Width, self.MainFlapHeight, self.Width, 0]
+ ])
+
+ ### COLUMN 5 ###
+ #create top right flap
+ self._CreatePathinMillimetres(
+ [
+ ["m", col5, row3],
+ ["h", self.Depth],
+ ["l", -self.FlapOffset, -self.FlapHeight],
+ ["h", -(self.Depth - (2*self.FlapOffset))],
+ ])
+
+ #create right print panel
+ self._CreateRectangleInMillimetres(self.Height, self.Depth, col5, row3)
+
+ #create bottom right flap
+ self._CreatePathinMillimetres(
+ [
+ ["m", col5, row4],
+ ["h", self.Depth],
+ ["l", -self.FlapOffset, self.FlapHeight],
+ ["h", -(self.Depth - (2*self.FlapOffset))],
+ ])
+
+class SlipcaseTuckBox(BoxBase):
+ def __init__(self, dimensionProvider):
+ BoxBase.__init__(self, dimensionProvider)
+
+ def Create(self):
+ self.FlapOffset = 1.5
+
+ # Figure out some row and column values,
+ # note rows and cols work left to right, top to bottom
+ col5 = self.X - self.Depth
+ col4 = col5 - self.Width
+ col3 = col4 - self.Depth
+ col2 = col3 - self.Width
+ col1 = col2 - self.Depth
+
+ row1 = self.Y - self.Height
+
+ ### COLUMN 1 ###
+ #create left glue flap
+ self._CreatePathinMillimetres(
+ [
+ ["m", col2, row1],
+ ["v", self.Height],
+ ["l", -(self.Depth - self.FlapOffset), -self.FlapOffset],
+ ["v", -(self.Height - (2*self.FlapOffset))],
+ ])
+
+ ### COLUMN 2 ###
+ #create box back print panel
+ self._CreateRectangleInMillimetres(self.Height, self.Width, col2, row1)
+
+ ### COLUMN 3 ###
+ #create left print panel
+ self._CreateRectangleInMillimetres(self.Height, self.Depth, col3, row1)
+
+ ### COLUMN 4 ###
+ #create box front print panel
+ self._CreateRectangleInMillimetres(self.Height, self.Width, col4, row1)
+
+ ### COLUMN 5 ###
+ #create right print panel
+ self._CreateRectangleInMillimetres(self.Height, self.Depth, col5, row1)
+
+class Matchbox(BoxBase):
+ def __init__(self, dimensionProvider, numFlaps):
+ BoxBase.__init__(self, dimensionProvider)
+
+ self.NumFlaps = numFlaps
+
+ def Create(self):
+ if self.NumFlaps == 2:
+ tuckbox = DoubleFlappedTuckBox(self.DimProvider)
+ else:
+ tuckbox = SingleFlappedTuckBox(self.DimProvider)
+ tuckbox.Create()
+
+ #################################3
+ # Create matching double flap tuck box
+ dimProvider = copy.copy(self.DimProvider)
+ dimProvider.Width -= 2
+ dimProvider.Height -= 2
+ dimProvider.Depth -= 2
+ dimProvider.Y = tuckbox.MinY - 20
+ drawer = MatcboxDrawer(dimProvider)
+ drawer.Create()
+
+class MatcboxDrawer(BoxBase):
+ def __init__(self, dimensionProvider):
+ BoxBase.__init__(self, dimensionProvider)
+
+ def Create(self):
+
+ fudgeDepth = self.Depth - 2 # overlap panels should be a little smaller to avoid touching box base
+ # Figure out some row and column co-ord values,
+ # note rows and cols work left to right, top to bottom, values start at 0 and go negative
+ col5 = self.X - fudgeDepth
+ col4 = col5 - self.Depth
+ col3 = col4 - self.Width
+ col2 = col3 - self.Depth
+ col1 = col2 - fudgeDepth
+
+ row5 = self.Y - fudgeDepth
+ row4 = row5 - self.Depth
+ row3 = row4 - self.Height
+ row2 = row3 - self.Depth
+ row1 = row2 - fudgeDepth
+
+ ### COLUMN 1 ###
+ #create left overlap panel
+ self._CreateRectangleInMillimetres(self.Height, fudgeDepth, col1, row3)
+
+ ### COLUMN 2 ###
+ #create top left flap
+ self._CreatePathinMillimetres(
+ [
+ ["m", col2, row3],
+ ["h", self.Depth],
+ ["l", -self.FlapOffset, -self.FlapHeight],
+ ["h", -(self.Depth - (2*self.FlapOffset))],
+ ])
+
+ #create box left side print panel
+ self._CreateRectangleInMillimetres(self.Height, self.Depth, col2, row3)
+
+ #create bottom left flap
+ self._CreatePathinMillimetres(
+ [
+ ["m", col2, row4],
+ ["h", self.Depth],
+ ["l", -self.FlapOffset, self.FlapHeight],
+ ["h", -(self.Depth - (2*self.FlapOffset))],
+ ])
+
+ ### COLUMN 3 ###
+
+ #create top side overlap panel
+ self._CreateRectangleInMillimetres(fudgeDepth, self.Width, col3, row1)
+
+ #create top box side panel
+ self._CreateRectangleInMillimetres(self.Depth, self.Width, col3, row2)
+
+ #create box bottom
+ self._CreateRectangleInMillimetres(self.Height, self.Width, col3, row3)
+
+ #create bottom box side panel
+ self._CreateRectangleInMillimetres(self.Depth, self.Width, col3, row4)
+
+ #create bottom side overlap panel
+ self._CreateRectangleInMillimetres(fudgeDepth, self.Width, col3, row5)
+
+ ### COLUMN 4 ###
+ #create top right flap
+ self._CreatePathinMillimetres(
+ [
+ ["m", col4, row3],
+ ["h", self.Depth],
+ ["l", -self.FlapOffset, -self.FlapHeight],
+ ["h", -(self.Depth - (2*self.FlapOffset))],
+ ])
+
+ #create box right side print panel
+ self._CreateRectangleInMillimetres(self.Height, self.Depth, col4, row3)
+
+ #create bottom right flap
+ self._CreatePathinMillimetres(
+ [
+ ["m", col4, row4],
+ ["h", self.Depth],
+ ["l", -self.FlapOffset, self.FlapHeight],
+ ["h", -(self.Depth - (2*self.FlapOffset))],
+ ])
+
+ ### COLUMN 5 ###
+ #create right overlap panel
+ self._CreateRectangleInMillimetres(self.Height, fudgeDepth, col5, row3)
+
+class TuckboxCreateEffect(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option('-t', '--type', action = 'store', type = 'string', dest = 'BoxType')
+ self.OptionParser.add_option('-n', '--num_flaps', action = 'store', type = 'int', dest = 'NumFlaps')
+ self.OptionParser.add_option('-w', '--deck_width', action = 'store', type = 'float', dest = 'DeckWidth')
+ self.OptionParser.add_option('-r', '--deck_height', action = 'store', type = 'float', dest = 'DeckHeight')
+ self.OptionParser.add_option('-d', '--deck_depth', action = 'store', type = 'float', dest = 'DeckDepth')
+ self.OptionParser.add_option('-a', '--box_allowance', action = 'store', type = 'float', dest = 'DeckAllowance')
+
+ def GetPaths(self):
+ paths = []
+
+ def effect(self):
+ dimProvider = EffectDimensionProvider(self)
+
+ if self.options.BoxType == "TUCKBOX":
+ if self.options.NumFlaps == 2:
+ box = DoubleFlappedTuckBox(dimProvider)
+ else:
+ box = SingleFlappedTuckBox(dimProvider)
+ elif self.options.BoxType == "SLIPCASE":
+ box = SlipcaseTuckBox(dimProvider)
+ elif self.options.BoxType == "MATCHBOX":
+ dimProvider.MaximiseHeight()
+ box = Matchbox(dimProvider, self.options.NumFlaps)
+ else:
+ raise Exception("Box type '{0}' is undefined".format(self.options.BoxType))
+
+ box.Create()
+
+if __name__ == '__main__':
+ creator = TuckboxCreateEffect()
+
+ creator.affect()
diff --git a/fablabchemnitz_HexLayoutGuides.inx b/fablabchemnitz_HexLayoutGuides.inx
new file mode 100644
index 0000000..572d3f1
--- /dev/null
+++ b/fablabchemnitz_HexLayoutGuides.inx
@@ -0,0 +1,22 @@
+
+
+ <_name>Hex Tiles
+ fablabchemnitz.de.HexLayoutGuides
+ fablabchemnitz_HexLayoutGuides.py
+ inkex.py
+ 19
+ 2
+ 2
+ 15
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fablabchemnitz_HexLayoutGuides.py b/fablabchemnitz_HexLayoutGuides.py
new file mode 100644
index 0000000..7a32043
--- /dev/null
+++ b/fablabchemnitz_HexLayoutGuides.py
@@ -0,0 +1,469 @@
+#!/usr/bin/env python2
+
+
+# Copyright 2016 Luke Phillips (lukerazor@hotmail.com)
+# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+# Extension dirs
+# linux:~/.config/inkscape/extensions
+# windows: [Drive]:\Program Files\Inkscape\share\extensions
+
+import inkex
+import simplestyle
+import copy
+import math
+
+FOLD_GAP = 5
+CROP_GAP = 2
+CROP_LENGTH = 3
+
+CSNS = ""
+
+inkex.NSS[u'cs'] = u'http://www.razorfoss.org/tuckboxextension/'
+
+def PrintDebug(string):
+ inkex.debug( _(str(string)) )
+
+def RoundAndDeduplicatePoints(points):
+ return sorted(list(set(map(lambda x: round(x, 3), points))))
+
+class Point():
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+
+ def rotate(self, angle, origin):
+ """
+ Rotate a point counterclockwise by a given angle around a given origin.
+
+ The angle should be given in degrees.
+ """
+ rads = math.radians(angle)
+ newX = origin.x + math.cos(rads) * (self.x - origin.x) - math.sin(rads) * (self.y - origin.y)
+ newY = origin.y + math.sin(rads) * (self.x - origin.x) + math.cos(rads) * (self.y - origin.y)
+
+ return Point(newX, newY)
+
+ def add(self, point):
+ return Point(self.x + point.x, self.y + point.y)
+
+ @staticmethod
+ def parsePoint(pointString):
+ x, y = map(lambda v: float(v), pointString.split(","))
+ return Point(x, y)
+
+ @staticmethod
+ def parse(pointString, orientationString=None):
+ p1 = Point.parsePoint(pointString)
+ p = Point(p1.x, p1.y)
+
+ if orientationString != None:
+ po = Point.parsePoint(orientationString)
+ p = p1.add(po.rotate(270, Point(0, 0)))
+
+ return p
+
+class HexGeneratorBase(object):
+ def __init__(self, hexDiameter, hexMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc):
+ self.UnitConverterFunc = unitConverterFunc
+ self.HexDiameter = hexDiameter
+ self.HexMargin = hexMargin
+ self.BleedMargin = bleedMargin
+
+ self.PageWidth = pageWidth
+ self.PageHeight = pageHeight
+ self.PageMargin = pageMargin
+
+ self.ContainerWidth = -1
+ self.ContainerHeight = -1
+
+ def CalcPageLeftMargin(self):
+ return (self.PageWidth - self.ContentWidth) / 2.0
+
+ def CalcPageBottomMargin(self):
+ return (self.PageHeight - self.ContentHeight) / 2.0
+
+ def DrawGuide(self, xmlParent, xpos, ypos):
+ posString = "{},{}".format(xpos, ypos)
+ attribs = {'position': posString, 'orientation': posString}
+
+ inkex.etree.SubElement(xmlParent, inkex.addNS('guide',"sodipodi"), attribs)
+
+ def DrawAngledGuide(self, xmlParent, xpos, ypos, angle):
+ # Angles are taken from the horizontal axis, positive angles move clockwise
+ posString = "{},{}".format(xpos, ypos)
+ orientationString = "{}, {}".format(math.sin(math.radians(angle)), math.cos(math.radians(angle)))
+ attribs = {'position': posString, 'orientation': orientationString}
+
+ inkex.etree.SubElement(xmlParent, inkex.addNS('guide',"sodipodi"), attribs)
+
+ def ConvertPoint(self, p):
+ # convert point into svg approriate values, including catering for inkscapes "alternative" axis sytem ie 0, 0 is bottom left not top left
+ newX = self.UnitConverterFunc("{}mm".format(p.x))
+ newY = self.PageHeight - self.UnitConverterFunc("{}mm".format(p.y))
+
+ return Point(newX, newY)
+
+ def DrawLine(self, xmlParent, p1, p2):
+ cp1 = self.ConvertPoint(p1)
+ cp2 = self.ConvertPoint(p2)
+
+ pathStr = "M {},{} {}, {}".format(cp1.x, cp1.y, cp2.x, cp2.y)
+ style = {'stroke': '#000000', 'stroke-width': self.UnitConverterFunc('0.25mm'), 'fill': 'none'}
+ attribs = {'style': simplestyle.formatStyle(style), 'd': pathStr}
+
+ inkex.etree.SubElement(xmlParent, inkex.addNS('path','svg'), attribs )
+
+ def DrawVerticleGuides(self, xmlParent, positions, gap):
+ curPos = self.CalcPageLeftMargin()
+ lastPos = -1
+ while curPos + self.ContainerWidth <= self.PageWidth - self.PageMargin:
+ for offset in positions:
+ curPos += offset
+ if curPos != lastPos: # don't double draw
+ self.DrawGuide(xmlParent, curPos, 0)
+
+ lastPos = curPos
+
+ curPos += gap
+
+ def DrawHorizontalGuides(self, xmlParent, positions, gap):
+ curPos = self.CalcPageBottomMargin()
+ lastPos = -1
+ while curPos + self.ContainerHeight <= self.PageHeight - self.PageMargin:
+ for offset in positions:
+ curPos += offset
+ if curPos != lastPos: # don't double draw
+ self.DrawGuide(xmlParent, 0, curPos)
+
+ lastPos = curPos
+
+ curPos += gap
+
+ def DrawAngledGuides(self, xmlParent, offsetPositions, angle, gap):
+
+ numExtraTopContainers = 0
+ numExtraBottomContainers = 0
+ if angle > 0:
+ numExtraTopContainers = math.ceil(self.NumContainersAcross / 2.0) - 1
+ if angle < 0:
+ numExtraBottomContainers = math.ceil(self.NumContainersAcross / 2.0) - 1
+
+ # draw sets of guides per point avoiding duplicate lines
+ # NOTE: Effectivly we draw the bottom guides first and then move up (ie y is increasing)
+ curPos = self.CalcPageBottomMargin() - numExtraBottomContainers * self.ContainerHeight
+ lastPos = -1
+
+ while curPos + self.ContainerHeight <= self.PageHeight - self.PageMargin + numExtraTopContainers*self.ContainerHeight:
+ for offset in offsetPositions:
+ curPos += offset
+ if curPos != lastPos: # don't double draw
+ self.DrawAngledGuide(xmlParent, self.CalcPageLeftMargin() + self.ContainerWidth/2, curPos, angle)
+
+ lastPos = curPos
+
+ curPos += gap
+
+ def GenerateFoldLines(self, xmlParent):
+ lines = self.GetFoldLinePositions()
+
+ for line in lines:
+ self.DrawLine(xmlParent, line[0], line[1])
+
+ def GenerateCropMarks(self, xmlParent):
+ lines = self.GetCropMarkLines()
+
+ for line in lines:
+ self.DrawLine(xmlParent, line[0], line[1])
+
+ @staticmethod
+ def CalculateBorderVerticalOffset(borderWidth):
+ if borderWidth == 0:
+ return 0
+
+ return borderWidth / math.sin(math.radians(60))
+
+ @staticmethod
+ def CreateHexGenerator(hexDiameter, hexMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc):
+ return SimpleHexGridLineGenerator(hexDiameter, hexMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc)
+
+class SimpleHexGridLineGenerator(HexGeneratorBase):
+ def __init__(self, hexDiameter, hexMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc):
+ super(SimpleHexGridLineGenerator, self).__init__(hexDiameter, hexMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc)
+
+ self.HexWidth = math.sqrt(3) * (hexDiameter/2)
+ self.ContainerWidth = self.HexWidth + 2*bleedMargin
+ self.ContainerHeight = hexDiameter + 2*HexGeneratorBase.CalculateBorderVerticalOffset(bleedMargin)
+
+ # num across
+ # num down
+ self.NumContainersAcross = int((self.PageWidth - 2*self.PageMargin) // self.ContainerWidth) # round down division
+ self.NumContainersDown = int((self.PageHeight - 2*self.PageMargin) // self.ContainerHeight) # round down division
+
+ # content sizes
+ self.ContentWidth = self.NumContainersAcross * self.ContainerWidth
+ self.ContentHeight = self.NumContainersDown * self.ContainerHeight
+
+ def GenerateGuides(self, xmlParent):
+ verticalGuideOffsets = [
+ 0,
+ self.BleedMargin,
+ self.HexMargin,
+ self.HexWidth - 2*self.HexMargin,
+ self.HexMargin,
+ self.BleedMargin
+ ]
+
+ bleedVerticalOffset = HexGeneratorBase.CalculateBorderVerticalOffset(self.BleedMargin)
+ hexMarginVerticalOffset = HexGeneratorBase.CalculateBorderVerticalOffset(self.HexMargin)
+ horizontalGuideOffsets = [
+ 0,
+ bleedVerticalOffset,
+ hexMarginVerticalOffset,
+ self.HexDiameter - 2*hexMarginVerticalOffset,
+ hexMarginVerticalOffset,
+ bleedVerticalOffset
+ ]
+
+ self.DrawVerticleGuides(xmlParent, verticalGuideOffsets, 0)
+ self.DrawAngledGuides(xmlParent, horizontalGuideOffsets, -30, 0)
+ self.DrawAngledGuides(xmlParent, horizontalGuideOffsets, 30, 0)
+
+ def GetFoldLinePositions(self):
+ return [] # no fold lines in simple grid
+
+ def GetCropMarkLines(self):
+ lines = []
+
+ leftMargin = self.CalcPageLeftMargin()
+ bottomMargin = self.CalcPageBottomMargin()
+ bleedVerticalOffset = HexGeneratorBase.CalculateBorderVerticalOffset(self.BleedMargin)
+
+ def CalcOppositeDeltaGivenAdjacentDelta(xdelta, angle):
+ return math.tan(math.radians(angle)) * xdelta
+
+ #---------------------------------------------------------------------------------
+ #determine all vertical crop marks, duplicates possible
+ # figure out the xpos'
+ vertical_xpos = []
+ for idx in range(self.NumContainersAcross):
+ leftX = self.BleedMargin
+ rightX = leftX + self.HexWidth
+ containerOffset = leftMargin + idx * self.ContainerWidth
+
+ vertical_xpos.append(containerOffset + leftX)
+ vertical_xpos.append(containerOffset + rightX)
+
+ vertical_xpos = RoundAndDeduplicatePoints(vertical_xpos)
+
+ # add to list of lines
+ vertical_ypos = [bottomMargin - CROP_GAP, self.PageHeight - bottomMargin + CROP_GAP + CROP_LENGTH]
+ for xpos in vertical_xpos:
+ for ypos in vertical_ypos:
+ lines.append([
+ Point(xpos, ypos),
+ Point(xpos, ypos - CROP_LENGTH)
+ ])
+
+ #---------------------------------------------------------------------------------
+ # figure out NW, SW, NE and SE crop marks for both sides of the page
+ vertical_ypos_nw = []
+ vertical_ypos_sw = []
+ vertical_ypos_ne = []
+ vertical_ypos_se = []
+
+ yoffset = CalcOppositeDeltaGivenAdjacentDelta(self.ContainerWidth/2 + CROP_GAP, 30)
+
+ lastColumnHasHalfStep = (self.NumContainersAcross % 2) == 0 # an even numbered containers across means that the last column is 1/2 a continer up and has 1 less container
+ staggeredContainerOffset = 0
+ if lastColumnHasHalfStep:
+ staggeredContainerOffset = self.ContainerHeight/2
+
+ for idx in range(self.NumContainersDown):
+ leftContainerOffset = idx * self.ContainerHeight + bottomMargin
+ rightContainerOffset = leftContainerOffset + staggeredContainerOffset
+
+ bottomY = bleedVerticalOffset
+ topY = self.ContainerHeight - bleedVerticalOffset
+
+ vertical_ypos_nw.append(leftContainerOffset + topY + yoffset)
+ vertical_ypos_nw.append(leftContainerOffset + bottomY + yoffset)
+
+ vertical_ypos_sw.append(leftContainerOffset + topY - yoffset)
+ vertical_ypos_sw.append(leftContainerOffset + bottomY - yoffset)
+
+ vertical_ypos_ne.append(rightContainerOffset + topY + yoffset)
+ vertical_ypos_ne.append(rightContainerOffset + bottomY + yoffset)
+
+ vertical_ypos_se.append(rightContainerOffset + topY - yoffset)
+ vertical_ypos_se.append(rightContainerOffset + bottomY - yoffset)
+
+ # sort out a staggered last col
+ if lastColumnHasHalfStep: # if it's a half step column we need to remove the last container and add acouple of extra lines
+ vertical_ypos_ne = vertical_ypos_ne[:-2]
+ vertical_ypos_se = vertical_ypos_se[:-2]
+ vertical_ypos_ne.append(max(vertical_ypos_ne) + 2*bleedVerticalOffset)
+ vertical_ypos_se.append(min(vertical_ypos_se) - 2*bleedVerticalOffset)
+
+ # remove duplicate positions
+ vertical_ypos_nw = RoundAndDeduplicatePoints(vertical_ypos_nw)
+ vertical_ypos_sw = RoundAndDeduplicatePoints(vertical_ypos_sw)
+ vertical_ypos_ne = RoundAndDeduplicatePoints(vertical_ypos_ne)
+ vertical_ypos_se = RoundAndDeduplicatePoints(vertical_ypos_se)
+
+ # add to list of lines
+ xpos_left = leftMargin - CROP_GAP
+ xpos_right = self.PageWidth - leftMargin + CROP_GAP
+ yoffset = CalcOppositeDeltaGivenAdjacentDelta(CROP_LENGTH, 30)
+
+ for ypos in vertical_ypos_nw:
+ lines.append([
+ Point(xpos_left, ypos),
+ Point(xpos_left - CROP_LENGTH, ypos + yoffset)
+ ])
+
+ for ypos in vertical_ypos_sw:
+ lines.append([
+ Point(xpos_left, ypos),
+ Point(xpos_left - CROP_LENGTH, ypos - yoffset)
+ ])
+
+ for ypos in vertical_ypos_ne:
+ lines.append([
+ Point(xpos_right, ypos),
+ Point(xpos_right + CROP_LENGTH, ypos + yoffset)
+ ])
+
+ for ypos in vertical_ypos_se:
+ lines.append([
+ Point(xpos_right, ypos),
+ Point(xpos_right + CROP_LENGTH, ypos - yoffset)
+ ])
+
+ #---------------------------------------------------------------------------------
+ # figure out NW, SW, NE and SE crop marks for top and bottom of the page
+ xpos_nw = []
+ xpos_sw = []
+ xpos_ne = []
+ xpos_se = []
+
+ xoffset_near = CalcOppositeDeltaGivenAdjacentDelta(bleedVerticalOffset + CROP_GAP, 60)
+ xoffset_far = CalcOppositeDeltaGivenAdjacentDelta(self.ContainerHeight - bleedVerticalOffset + CROP_GAP, 60)
+
+ for idx in range(0, self.NumContainersAcross, 2): #we only need to do every other container because of the stepping
+ containerOffset = idx * self.ContainerWidth + leftMargin
+ topY = self.ContainerHeight - bleedVerticalOffset
+ bottomY = bleedVerticalOffset
+ xpos = self.ContainerWidth/2
+
+ xpos_nw.append(containerOffset + xpos - xoffset_near)
+ xpos_nw.append(containerOffset + xpos - xoffset_far)
+
+ xpos_sw.append(containerOffset + xpos - xoffset_near)
+ xpos_sw.append(containerOffset + xpos - xoffset_far)
+
+ xpos_ne.append(containerOffset + xpos + xoffset_near)
+ xpos_ne.append(containerOffset + xpos + xoffset_far)
+
+ xpos_se.append(containerOffset + xpos + xoffset_near)
+ xpos_se.append(containerOffset + xpos + xoffset_far)
+
+
+ # remove duplicate positions
+ xpos_nw = RoundAndDeduplicatePoints(xpos_nw)
+ xpos_sw = RoundAndDeduplicatePoints(xpos_sw)
+ xpos_ne = RoundAndDeduplicatePoints(xpos_ne)
+ xpos_se = RoundAndDeduplicatePoints(xpos_se)
+
+ # add to list of lines
+ ypos_bottom = bottomMargin - CROP_GAP
+ ypos_top = self.PageHeight - bottomMargin + CROP_GAP
+ yoffset = CalcOppositeDeltaGivenAdjacentDelta(CROP_LENGTH, 30)
+
+ for xpos in xpos_nw:
+ if xpos > 0 and xpos < self.PageWidth:
+ lines.append([
+ Point(xpos, ypos_top),
+ Point(xpos - CROP_LENGTH, ypos_top + yoffset)
+ ])
+
+ for xpos in xpos_sw:
+ if xpos > 0 and xpos < self.PageWidth:
+ lines.append([
+ Point(xpos, ypos_bottom),
+ Point(xpos - CROP_LENGTH, ypos_bottom - yoffset)
+ ])
+
+ for xpos in xpos_ne:
+ if xpos > 0 and xpos < self.PageWidth:
+ lines.append([
+ Point(xpos, ypos_top),
+ Point(xpos + CROP_LENGTH, ypos_top + yoffset)
+ ])
+
+ for xpos in xpos_se:
+ if xpos > 0 and xpos < self.PageWidth:
+ lines.append([
+ Point(xpos, ypos_bottom),
+ Point(xpos + CROP_LENGTH, ypos_bottom - yoffset)
+ ])
+
+ #---------------------------------------------------------------------------------
+ return lines
+
+class HexLayoutGuidesEffect(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+
+ self.OptionParser.add_option('-d', '--hex_diameter', action = 'store', type = 'float', dest = 'HexDiameter')
+ self.OptionParser.add_option('-c', '--hex_margin', action = 'store', type = 'float', dest = 'HexMargin')
+ self.OptionParser.add_option('-b', '--bleed_margin', action = 'store', type = 'float', dest = 'BleedMargin')
+ self.OptionParser.add_option('-p', '--page_margin', action = 'store', type = 'float', dest = 'PageMargin')
+
+ def effect(self):
+ # find dimensions of page
+ pageWidth = self.uutounit(self.unittouu(self.getDocumentWidth()), "mm")
+ pageHeight = self.uutounit(self.unittouu(self.getDocumentHeight()), "mm")
+
+ opt = self.options
+
+ guideParent = self.document.xpath('//sodipodi:namedview',namespaces=inkex.NSS)[0]
+
+ ### GUIDES
+ # remove all the existing guides
+ [node.getparent().remove(node) for node in self.document.xpath('//sodipodi:guide',namespaces=inkex.NSS)]
+
+ # create the object generator
+ gen = HexGeneratorBase.CreateHexGenerator(opt.HexDiameter, opt.HexMargin, opt.BleedMargin, pageWidth, pageHeight, opt.PageMargin, self.unittouu)
+
+ gen.GenerateGuides(guideParent)
+
+ ### CROP MARKS
+ # remove any existing 'Crop marks' layer
+ [node.getparent().remove(node) for node in self.document.xpath("//svg:g[@inkscape:label='Crop Marks']",namespaces=inkex.NSS)]
+
+ svg = self.document.xpath('//svg:svg', namespaces=inkex.NSS)[0]
+ layer = inkex.etree.SubElement(svg, inkex.addNS('g',"svg"), {})
+ layer.set(inkex.addNS('label', 'inkscape'), "Crop Marks")
+ layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
+ #layer.set(inkex.addNS('insensitive', 'sodipodi'), 'true')
+
+
+ gen.GenerateCropMarks(layer)
+
+if __name__ == '__main__':
+ effect = HexLayoutGuidesEffect()
+
+ effect.affect()
diff --git a/fablabchemnitz_InsertPaperTemplate.inx b/fablabchemnitz_InsertPaperTemplate.inx
new file mode 100644
index 0000000..f393724
--- /dev/null
+++ b/fablabchemnitz_InsertPaperTemplate.inx
@@ -0,0 +1,32 @@
+
+
+ <_name>Insert Paper Template
+ fablabchemnitz.de.InsertPaperTemplate
+ fablabchemnitz_InsertPaperTemplate.py
+ inkex.py
+
+ - A5
+ - A4
+ - A3
+ - A2
+ - A1
+ - A0
+ - Poker Card
+ - Bridge Card
+ - Mini American Card
+ - Mini European Card
+ - Tarot Card
+
+ true
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fablabchemnitz_InsertPaperTemplate.py b/fablabchemnitz_InsertPaperTemplate.py
new file mode 100644
index 0000000..4b1f6e4
--- /dev/null
+++ b/fablabchemnitz_InsertPaperTemplate.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python2
+
+
+# Copyright 2016 Luke Phillips (lukerazor@hotmail.com)
+# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+# Extension dirs
+# linux:~/.config/inkscape/extensions
+# windows: D:\Program Files\Inkscape\share\extensions
+
+import inkex
+import simplestyle
+import simplepath
+from inkex import *
+import csv
+import fnmatch
+import re
+import os
+import os.path
+import lxml
+from lxml import etree
+from copy import deepcopy
+import sys
+import copy
+
+CSNS = ""
+
+NSS[u'cs'] = u'http://www.razorfoss.org/tuckboxextension/'
+
+class PaperBase():
+ def __init__(self, width, height):
+ self.Width = width
+ self.Height = height
+
+class A5(PaperBase):
+ def __init__(self):
+ PaperBase.__init__(self, 150, 100)
+
+class InsertPaperTemplateEffect(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option('-p', '--papertype', action = 'store', type = 'string', dest = 'PaperType')
+ self.OptionParser.add_option('-s', '--show_type', action = 'store', type = 'string', dest = 'ShowType')
+
+ ### colour ###
+ self.StrokeWidthFloat = 0.5
+ self.StrokeWidth = self.MMtoUU(self.StrokeWidthFloat)
+
+ self.FontSize = "11px"
+
+ def effect(self):
+ self.Group = inkex.etree.SubElement(self.current_layer, inkex.addNS('g','svg'), {} )
+
+ paperTypes = {}
+ paperTypes["A5"] = (148, 210, "#ffeeaa") # yellow
+ paperTypes["A4"] = (210, 297, "#ffccaa") # orange
+ paperTypes["A3"] = (297, 420, "#afdde9") # blue
+ paperTypes["A2"] = (420, 594, "#ccaaff") # purple
+ paperTypes["A1"] = (594, 841, "#afe9c6") # green
+ paperTypes["A0"] = (841, 1189, "#ffd5d5") # red
+
+ paperTypes["POKER"] = (63.5, 88, "#ffffff") # white
+ paperTypes["BRIDGE"] = (56, 88, "#ffffff") # white
+ paperTypes["MINI_US"] = (41, 63, "#ffffff") # white
+ paperTypes["MINI_EU"] = (44, 68, "#ffffff") # white
+ paperTypes["TAROT"] = (70, 120, "#ffffff") # white
+
+
+ if self.options.PaperType in paperTypes:
+ self.CreateTemplate(self.options.PaperType, *(paperTypes[self.options.PaperType]))
+ else:
+ raise Exception("Paper type '{0}' is undefined".format(self.options.PaperType))
+
+ def CreateTemplate(self, label, width, height, colour):
+ # TODO Read mouse position
+ x = 0
+ y = 0
+ self._CreateRectangleInMillimetres(width-self.StrokeWidthFloat, height-self.StrokeWidthFloat, x, y, colour)
+ if self.options.ShowType == "true":
+ self._CreateText(label, x + width/2 , y + height/2)
+
+ def _CreateText(self, labelText, x, y):
+ style = {'stroke': '#000000',
+ 'stroke-width': self.StrokeWidth,
+ 'fill' : '#000000',
+ 'font-size' : self.FontSize,
+ 'text-align' : 'center',
+ 'text-anchor' : 'middle'
+ }
+
+ attribs = {'style': simplestyle.formatStyle(style), 'x': self.MMtoUU(x), 'y': self.MMtoUU(y)}
+
+ text = inkex.etree.Element(inkex.addNS('text','svg'), attribs)
+ text.text = labelText
+
+ self.Group.append(text)
+
+ def _CreateRectangleInMillimetres(self, width, height, x, y, color):
+ style = {'stroke': '#000000', 'stroke-width': self.StrokeWidth, 'fill' : color}
+ attribs = {'style': simplestyle.formatStyle(style), 'height': self.MMtoUU(height), 'width': self.MMtoUU(width), 'x': self.MMtoUU(x), 'y': self.MMtoUU(y)}
+ inkex.etree.SubElement(self.Group, inkex.addNS('rect','svg'), attribs )
+
+ def MMtoUU(self, mmval):
+ if hasattr(self, "xxxunittouu"):
+ return str(self.unittouu("{0}mm".format(mmval)))
+ else:
+ MM_TO_PIXELS = 1
+
+ return str(MM_TO_PIXELS * mmval)
+
+
+if __name__ == '__main__':
+ creator = InsertPaperTemplateEffect()
+
+ creator.affect()
diff --git a/fablabchemnitz_add_kerf_width_offset.inx b/fablabchemnitz_add_kerf_width_offset.inx
new file mode 100644
index 0000000..fce0316
--- /dev/null
+++ b/fablabchemnitz_add_kerf_width_offset.inx
@@ -0,0 +1,21 @@
+
+
+ <_name>Kerf Width Offset
+ fablabchemnitz.de.add_kerf_width_offset
+ fablabchemnitz_add_kerf_width_offset.py
+ fablabchemnitz_metaext.py
+ simplestyle.py
+ inkex.py
+ 0.
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fablabchemnitz_add_kerf_width_offset.py b/fablabchemnitz_add_kerf_width_offset.py
new file mode 100644
index 0000000..ec1b082
--- /dev/null
+++ b/fablabchemnitz_add_kerf_width_offset.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+
+# These two lines are only needed if you don't put the script directly into
+# the installation directory
+import sys
+import simplestyle # will be needed here for styles support
+sys.path.append('/usr/share/inkscape/extensions')
+
+# We will use the inkex module with the predefined Effect base class.
+import inkex
+import fablabchemnitz_metaext
+
+class AddKerf(inkex.Effect):
+ """
+ An Inkscape extension which joins multiple lines in one object and adds
+ kerf to it.
+ """
+ def __init__(self):
+ """
+ Constructor.
+ """
+ # Call the base class constructor.
+ inkex.Effect.__init__(self)
+
+ # Define string option "--what" with "-w" shortcut and default value "World".
+ # TODO: better name
+ self.OptionParser.add_option('-w', '--what', action = 'store',
+ type = 'float', dest = 'what', default = '0.0',
+ help = 'Kerf in 1/10 mm.')
+
+ def set_appropriate_width(self, kerf_in_mm):
+ found = False
+ for _, node in self.selected.iteritems():
+ if node.tag == inkex.addNS('g', 'svg'):
+ inkex.error("Can't add kerf to a group, please ungroup first.")
+ if node.tag == inkex.addNS('path', 'svg'):
+ style = {'stroke-width': kerf_in_mm, 'fill': 'none',
+ 'stroke':'#000000'}
+ style = simplestyle.formatStyle(style)
+ node.set('style', style)
+ found = True
+ return found
+
+ def effect(self):
+ """% mod
+ Effect behaviour.
+ """
+ # Get script's "--what" option value.
+ what = self.options.what / 10.
+ negative_kerf = False
+ if what < 0.:
+ what = -what
+ negative_kerf = True
+
+ kerf_in_mm = self.unittouu(str(what) + " mm")
+
+ operation_list = ["StrokeToPath", "SelectionUnion",
+ "SelectionBreakApart", "SelectionUnion", "SelectionBreakApart"]
+ if negative_kerf:
+ operation_list[-2] = "SelectionIntersect"
+
+ join_ext = fablabchemnitz_metaext.MetaEffect(operation_list)
+
+ if not self.set_appropriate_width(kerf_in_mm):
+ objectify = fablabchemnitz_metaext.MetaEffect(["ObjectToPath"])
+ objectify.effect(self.document, self.selected, self.doc_ids)
+ if not self.set_appropriate_width(kerf_in_mm):
+ inkex.error("Didn't found any selected path, breaking")
+ return
+
+ join_ext.effect(self.document, self.selected, self.doc_ids)
+
+ for _, node in self.selected.iteritems():
+ #inkex.debug("node %s" % inkex.etree.tostring(node))
+ if node.tag == inkex.addNS('path', 'svg'):
+ new_width = self.unittouu("0.5mm")
+ colour = '#ff0000' if negative_kerf else '#000000'
+ style = {'stroke-width': new_width, 'fill': 'none',
+ 'stroke': colour}
+ style = simplestyle.formatStyle(style)
+ node.set('style', style)
+ # inkex.debug("node %s" % inkex.etree.tostring(node))
+
+
+# Create effect instance and apply it.
+effect = AddKerf()
+effect.affect()
diff --git a/fablabchemnitz_ai_eps_output.inx b/fablabchemnitz_ai_eps_output.inx
new file mode 100644
index 0000000..de34875
--- /dev/null
+++ b/fablabchemnitz_ai_eps_output.inx
@@ -0,0 +1,15 @@
+
+
+ <_name>AI compatible EPS output
+ fablabchemnitz.de.ai_eps_output
+ fablabchemnitz_ai_eps_output.py
+
+
+
diff --git a/fablabchemnitz_ai_eps_output.py b/fablabchemnitz_ai_eps_output.py
new file mode 100644
index 0000000..4709967
--- /dev/null
+++ b/fablabchemnitz_ai_eps_output.py
@@ -0,0 +1,1110 @@
+#!/usr/bin/env python
+"""
+Mainly written by Andras Prim github_at_primandras.hu
+
+Arc to bezier converting method is ported from:
+http://code.google.com/p/core-framework/source/browse/trunk/plugins/svg.js
+written by Angel Kostadinov, with MIT license
+"""
+
+try:
+ from lxml import etree as ET
+except Exception:
+ import xml.etree.ElementTree as ET
+
+import re
+import math
+
+def wrap(text, width):
+ """ A word-wrap function that preserves existing line breaks """
+ retstr = ""
+ for word in text.split(' '):
+ if len(retstr)-retstr.rfind('\n')-1 + len(word.split('\n',1)[0]) >= width:
+ retstr += ' \n' + word
+ else:
+ retstr += ' ' + word
+ return retstr
+
+def css2dict(css):
+ """returns a dictionary representing the given css string"""
+ cssdict = {}
+ if None == css:
+ return cssdict
+ for pair in css.split(';'): #TODO: what about escaped separators
+ if pair.find(':') >= 0:
+ key, value = pair.split(':')
+ cssdict[ key.strip() ] = value.strip()
+ return cssdict
+
+def cssColor2Eps(cssColor, colors='RGB'):
+ """converts css color definition (a hexa code with leading #)
+ to eps color definition"""
+ r = float(int(cssColor[1:3],16)) / 255
+ g = float(int(cssColor[3:5],16)) / 255
+ b = float(int(cssColor[5:7],16)) / 255
+ if colors == 'RGB':
+ return "%f %f %f" % (r, g, b)
+ elif colors == 'CMYKRGB':
+ if (r == 0) and (g == 0) and (b == 0):
+ c = 0
+ m = 0
+ y = 0
+ k = 1
+ else:
+ c = 1 - r
+ m = 1 - g
+ y = 1 - b
+
+ # extract out k [0,1]
+ min_cmy = min(c, m, y)
+ c = (c - min_cmy) / (1 - min_cmy)
+ m = (m - min_cmy) / (1 - min_cmy)
+ y = (y - min_cmy) / (1 - min_cmy)
+ k = min_cmy
+
+ return "%f %f %f %f %f %f %f" % (c, m, y, k, r, g, b)
+
+class svg2eps:
+ def __init__(self, filename=None):
+ self.filename = filename
+ self.svg = None
+ self.rePathDSplit = re.compile('[^a-zA-Z0-9.-]+')
+ self.reTransformFind = re.compile('([a-z]+)\\(([^)]+)\\)')
+ self.reNumberFind = re.compile('[0-9.eE+-]+')
+ # must update reNumberUnitFind, if e is a valid character in a unit
+ self.reNumberUnitFind = re.compile('([0-9.eE+-]+)([a-z]*)')
+ # px to pt conversion rate varies based on inkscape versions, it is added during parsing
+ self.toPt = {'in': 72.0, 'pt': 1.0, 'mm': 2.8346456695, 'cm': 28.346456695, 'm': 2834.6456695, 'pc': 12.0}
+
+ def unitConv(self, string, toUnit):
+ match = self.reNumberUnitFind.search(string)
+ number = float(match.group(1))
+ unit = match.group(2)
+ if unit not in self.toPt:
+ unit = 'uu'
+
+ if unit == toUnit:
+ return number
+ else:
+ return number * self.toPt[unit] / self.toPt[toUnit]
+
+ def lengthConv(self, svgLength):
+ """converts svgLength to eps length using the current transformation matrix"""
+ matrix = self.matrices[-1]
+ epsx = matrix[0] * svgLength
+ epsy = matrix[1] * svgLength
+
+ return math.sqrt(epsx*epsx + epsy*epsy)
+
+ def coordConv(self, svgx, svgy, relative=False):
+ """converts svgx, svgy coordinates to eps coordinates using the current transformation matrix"""
+ if relative:
+ svgx = float(svgx) + self.curPoint[0]
+ svgy = float(svgy) + self.curPoint[1]
+ else:
+ svgx = float(svgx)
+ svgy = float(svgy)
+ matrix = self.matrices[-1]
+ epsx = matrix[0] * svgx + matrix[2] * svgy + matrix[4]
+ epsy = matrix[1] * svgx + matrix[3] * svgy + matrix[5]
+
+ return (epsx, epsy)
+
+ def matrixMul(self, matrix, matrix2):
+ """multiplies matrix with matrix2"""
+ matrix0 = matrix[:]
+ matrix[0] = matrix0[0] * matrix2[0] + matrix0[2]*matrix2[1] # + matrix0[4]*0
+ matrix[1] = matrix0[1] * matrix2[0] + matrix0[3]*matrix2[1] # + matrix0[5]*0
+ matrix[2] = matrix0[0] * matrix2[2] + matrix0[2]*matrix2[3] # + matrix0[4]*0
+ matrix[3] = matrix0[1] * matrix2[2] + matrix0[3]*matrix2[3] # + matrix0[5]*0
+ matrix[4] = matrix0[0] * matrix2[4] + matrix0[2]*matrix2[5] + matrix0[4]
+ matrix[5] = matrix0[1] * matrix2[4] + matrix0[3]*matrix2[5] + matrix0[5]
+
+
+ def alert(self, string, elem):
+ """adds an alert to the collection"""
+ if not string in self.alerts:
+ self.alerts[string] = set()
+ elemId = elem.get('id')
+ if elemId != None:
+ self.alerts[string].add(elemId)
+
+ def showAlerts(self):
+ """show alerts collected by the alert() function"""
+ for string, ids in self.alerts.iteritems():
+ idstring = ', '.join(ids)
+ print(string, idstring)
+
+ def elemSvg(self, elem):
+ """handles the