chiark / gitweb /
Merge commit '92f9d78ecdcd2ee53ac7519c19c91cdd71122d22'
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sat, 16 Sep 2023 10:54:25 +0000 (11:54 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sat, 16 Sep 2023 10:54:25 +0000 (11:54 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
214 files changed:
.gitignore [new file with mode: 0644]
35mmjack-dummy.scad [new file with mode: 0644]
GPL-3 [new file with mode: 0644]
Huxley-INSTRUCTIONS [new file with mode: 0644]
Makefile [new file with mode: 0644]
Marlin.hex [new file with mode: 0644]
README [new file with mode: 0644]
adafruit-powerboost-1000.scad [new file with mode: 0644]
adafruit-powerboost-500.scad [new file with mode: 0644]
adafruit-powerboost-common.scad [new file with mode: 0644]
anglepoise-neck.scad [new file with mode: 0644]
anglepoise-neck.slic3r [new file with mode: 0644]
anke-gps-bracket.scad [new file with mode: 0644]
atreic-piano-stand.scad [new file with mode: 0644]
axlepin.scad [new file with mode: 0644]
belt-cut-jig-common.scad [new file with mode: 0644]
belt-hole-cut-jig-simple.scad [new file with mode: 0644]
belt-hole-cut-jig.scad [new file with mode: 0644]
belt-slot-cut-jig,JigT.auto.slic3r [new file with mode: 0644]
belt-slot-cut-jig,Kit.auto.slic3r [new file with mode: 0644]
belt-slot-cut-jig.scad [new file with mode: 0644]
bike-lipo-box-gland.scad [new file with mode: 0644]
bike-lipo-box.scad [new file with mode: 0644]
bike-phone-mount.scad [new file with mode: 0644]
bike-stalk-led-mount.scad [new file with mode: 0644]
biscuits.scad [new file with mode: 0644]
brompton-computer-guard.scad [new file with mode: 0644]
cable-hole-trunking-cover.scad [new file with mode: 0644]
cable-splice-clamp.scad [new file with mode: 0644]
calib-fit.scad [new file with mode: 0644]
camera-mount.scad [new file with mode: 0644]
clip-spring-holder-clip.scad [new file with mode: 0644]
cliphook.scad [new file with mode: 0644]
commitid-2d-test.scad [new file with mode: 0644]
commitid-best-test.scad.pl [new file with mode: 0755]
commitid-cube-test-X.scad [new file with mode: 0644]
commitid-cube-test-Y.scad [new file with mode: 0644]
commitid-cube-test.scad [new file with mode: 0644]
commitid-layering-test.scad [new file with mode: 0644]
commitid.scad.pl [new file with mode: 0755]
crossbar-computer-led-mount.scad [new file with mode: 0644]
dell-psu-glow-lampshade.scad [new file with mode: 0644]
deore-crank-remover.scad [new file with mode: 0644]
distort-stl [new file with mode: 0755]
diziet-utils/COPYING [new file with mode: 0644]
diziet-utils/README.md [moved from README.md with 100% similarity]
doveclip.scad [new file with mode: 0644]
dovecliptest.scad [new file with mode: 0644]
dungeonquest-cone.scad [new file with mode: 0644]
dungeonquest-cone.slic3r [new file with mode: 0644]
earring-stand.scad [new file with mode: 0644]
electron-token.scad.pl [new file with mode: 0755]
experimental-prefix-nonworking [new file with mode: 0644]
fairphone-battery-case.scad [new file with mode: 0644]
fairphone-case-mounted.scad [new file with mode: 0644]
fairphone-case.scad [new file with mode: 0644]
fairphone4-case-coarse.scad [new file with mode: 0644]
fairphone4-case-mounted.scad [new file with mode: 0644]
fairphone4-case-tripod.scad [new file with mode: 0644]
fairphone4-case.scad [new file with mode: 0644]
filament-test.scad [new file with mode: 0644]
filamentclip.scad [new file with mode: 0644]
filamentspool-lt.scad [new file with mode: 0644]
filamentspool-number.eps.pl [new file with mode: 0755]
filamentspool-sm.scad [new file with mode: 0644]
filamentspool-storarm3.scad [new file with mode: 0644]
filamentspool.scad [new file with mode: 0644]
filamentteeth.scad [new file with mode: 0644]
filamenttrestle.scad [new file with mode: 0644]
fire-blanket-wall-mushroom.scad [new file with mode: 0644]
fire-blanket-wall-mushroom.slic3r [new file with mode: 0644]
firmware-eeprom.hex [new file with mode: 0644]
firmware-efuse.hex [new file with mode: 0644]
firmware-flash.hex [new file with mode: 0644]
firmware-hfuse.hex [new file with mode: 0644]
firmware-lfuse.hex [new file with mode: 0644]
floating-phases.scad [new file with mode: 0644]
floating-test.scad [new file with mode: 0644]
flyscreen-handle.scad [new file with mode: 0644]
flyscreen-wall-spacer.scad [new file with mode: 0644]
fruit-bowl-stand.scad [new file with mode: 0644]
funcs.scad.cpp [new file with mode: 0644]
hole-repair-20191117.scad [new file with mode: 0644]
hole-transfer-punch.scad [new file with mode: 0644]
holetest.scad [new file with mode: 0644]
hotel-piece-model.scad [new file with mode: 0644]
itx-aperture-grommet.scad [new file with mode: 0644]
keyring-kay.scad [new file with mode: 0644]
knifeblock,BlockPrint.auto.slic3r [new file with mode: 0644]
knifeblock-knives-filter [new file with mode: 0755]
knifeblock-knives-photo.jpg [new file with mode: 0644]
knifeblock-knives-trace.fig [new file with mode: 0644]
knifeblock.scad [new file with mode: 0644]
ksafe-base.scad [new file with mode: 0644]
laptop-camera-tripod-bracket.scad [new file with mode: 0644]
laptop-sound-cable-hooks.scad [new file with mode: 0644]
led-panel-ceiling-bracket.scad [new file with mode: 0644]
led-panel-ceiling-bracket.slic3r [new file with mode: 0644]
lemon-stand.scad.pl [new file with mode: 0755]
light-bracket.scad [new file with mode: 0644]
lipo-flat-mount.scad [new file with mode: 0644]
lock-inframe-bracket.scad [new file with mode: 0644]
maglite-holder-photo.jpg [new file with mode: 0644]
maglite-holder-torch.fig [new file with mode: 0644]
maglite-holder.scad [new file with mode: 0644]
makita-drill-handle-blivet.scad [new file with mode: 0644]
manual-gcode-generator [new file with mode: 0755]
mic-camera-adapter.scad [new file with mode: 0644]
mic-table-clamp.scad [new file with mode: 0644]
nook-case-test.scad [new file with mode: 0644]
nook-case.scad [new file with mode: 0644]
nutbox.scad.m4 [new file with mode: 0644]
osstest-arm-hub-bracket.scad [new file with mode: 0644]
osstest-arm-net-bracket.scad [new file with mode: 0644]
osstest-arm-psu-bracket.scad [new file with mode: 0644]
pandemic-counter-letters.fig [new file with mode: 0644]
pandemic-counter.scad [new file with mode: 0644]
pandemic-counter.slic3r [new file with mode: 0644]
pandemic-quarantine-numbers.fig [new file with mode: 0644]
pandemic-quarantines.scad [new file with mode: 0644]
pannierstay.scad [new file with mode: 0644]
pattress-boxes-3-cover.scad [new file with mode: 0644]
pawn.scad [new file with mode: 0644]
pawn.slic3r [new file with mode: 0644]
pin-hinge.scad [new file with mode: 0644]
poster-tube-lid-coarse.scad [new file with mode: 0644]
poster-tube-lid-parametric.scad.pl [new file with mode: 0755]
poster-tube-lid.scad [new file with mode: 0644]
powerbank-anker-10000.eps [new file with mode: 0644]
powerbank-anker-10000.svg [new file with mode: 0644]
powerbank-bike-clamp.scad [new file with mode: 0644]
pronsolerc [new file with mode: 0644]
pull-cord-keeper.scad [new file with mode: 0644]
quacks-ingredients-L1.scad [new file with mode: 0644]
quacks-ingredients-L2.scad [new file with mode: 0644]
quacks-ingredients-L3.scad [new file with mode: 0644]
quacks-ingredients-L4.scad [new file with mode: 0644]
quacks-ingredients-L5.scad [new file with mode: 0644]
quacks-ingredients-counts [new file with mode: 0755]
quacks-ingredients-counts.scad [new file with mode: 0644]
quacks-ingredients-demos.scad [new file with mode: 0644]
quacks-ingredients-make-copy-gcodes [new file with mode: 0755]
quacks-ingredients-update-levels [new file with mode: 0755]
quacks-ingredients.scad [new file with mode: 0644]
quacks.ini [new file with mode: 0644]
question-question.fig [new file with mode: 0644]
question-token.scad [new file with mode: 0644]
read-firmware [new file with mode: 0755]
reprap-objects.make [new file with mode: 0644]
ring-tests.scad [new file with mode: 0644]
rope-adjuster.scad [new file with mode: 0644]
rpi-mount.scad [new file with mode: 0644]
salter-scale-hook.scad [new file with mode: 0644]
scaffold-clamp-cleat.scad [new file with mode: 0644]
scaffold-clamp-common.scad [new file with mode: 0644]
scaffold-clamp-linear-bracket.scad [new file with mode: 0644]
scaffold-clamp-straphook.scad [new file with mode: 0644]
scaffold-clamp-tensioner.scad [new file with mode: 0644]
screw-recess-test-number.fig.pl [new file with mode: 0755]
screw-recess-test.scad [new file with mode: 0644]
sealing-box.scad.m4 [new file with mode: 0644]
secateurs-clip.scad [new file with mode: 0644]
sewing-table-end-profile-photo.jpg [new file with mode: 0644]
sewing-table-end-profile.fig [new file with mode: 0644]
sewing-table-front-profile-photo.jpg [new file with mode: 0644]
sewing-table-front-profile.fig [new file with mode: 0644]
sewing-table-jig.scad [new file with mode: 0644]
sewing-table-rear-profile-photo.jpg [new file with mode: 0644]
sewing-table-rear-profile.fig [new file with mode: 0644]
sewing-table-test.scad [new file with mode: 0644]
sewing-table.scad.m4 [new file with mode: 0644]
shelf-label-holder.scad [new file with mode: 0644]
simplephone-case-test.scad [new file with mode: 0644]
simplephone-case.scad [new file with mode: 0644]
size-tests.m-g [new file with mode: 0644]
sleepphone-cable-box.scad [new file with mode: 0644]
slic3r-config.ini [new file with mode: 0644]
smallfilamentclip.scad [new file with mode: 0644]
splitpin.scad [new file with mode: 0644]
sprinkler-spike-receptacle.scad [new file with mode: 0644]
startech-dell-usb-cable-retainer.scad [new file with mode: 0644]
steamer-handle-clip.scad [new file with mode: 0644]
stringing-test.scad [new file with mode: 0644]
summit-lantern-hook.scad [new file with mode: 0644]
svg-prep-dxf [new file with mode: 0755]
tablet-case-corner-mount.scad [new file with mode: 0644]
tablet-stand.scad [new file with mode: 0644]
test-cup.scad [new file with mode: 0644]
test-object.scad [new file with mode: 0644]
thread-external-test.scad [new file with mode: 0644]
thread-internal-test.scad [new file with mode: 0644]
threads.scad [new file with mode: 0644]
topeak-mtx-tortec-expeditionrack-adapter.scad [new file with mode: 0644]
topeak-seatstay-lock.scad [new file with mode: 0644]
toplevel-find [new file with mode: 0755]
toplevel-make [new file with mode: 0755]
tower-base.scad [new file with mode: 0644]
trackpump-mutlihead-clip.scad [new file with mode: 0644]
trailerhubcap.scad [new file with mode: 0644]
treefoil.scad.pl [new file with mode: 0755]
tube-crossdrill-jig.scad [new file with mode: 0644]
utils.scad [new file with mode: 0644]
velux-window-grip.scad [new file with mode: 0644]
velux-window-grip.slic3r [new file with mode: 0644]
wall-cable-hook.scad [new file with mode: 0644]
wardrobe-hook.scad [new file with mode: 0644]
warptest.scad [new file with mode: 0644]
warptest2.scad [new file with mode: 0644]
warptest3.scad [new file with mode: 0644]
wine-vacuum-adapter.scad [new file with mode: 0644]
write-firmware [new file with mode: 0755]
xeno-drivebay-bracket.scad [new file with mode: 0644]
y-large-axlebar-washer.scad [new file with mode: 0644]
yubikey-5c-nano-loop.scad [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..f328663
--- /dev/null
@@ -0,0 +1,40 @@
+*~
+*.gcode
+light-bracket.stl
+*.aside
+*.stl
+*,*.auto.scad
+.*.d
+*.tmp
+*.fig.bak
+funcs.scad
+nutbox.scad
+powerbank-anker-10000.dxf*
+knifeblock-knives-*.dxf*
+pandemic-counter-l*.dxf
+pandemic-counter-l*.eps
+pandemic-quarantine-l*.dxf
+pandemic-quarantine-l*.eps
+screw-recess-test-number-s*.*
+question-question.dxf
+question-question.eps
+lemon-stand.scad
+electron-token.scad
+commitid.scad
+commitid-best-test.scad
+filamentspool-number-n*.dxf
+filamentspool-number-n*.eps
+sealing-box.scad
+sewing-table.scad
+maglite-holder-torch-curve.dxf
+maglite-holder-torch-curve.eps
+sewing-table-rear-profile.dxf
+sewing-table-rear-profile.eps
+sewing-table-front-profile.dxf
+sewing-table-front-profile.eps
+sewing-table-end-profile.dxf
+sewing-table-end-profile.eps
+sewing-table,Demo-flat.png
+poster-tube-lid-parametric.scad
+treefoil.scad
+quacks-L*.auto.ini
diff --git a/35mmjack-dummy.scad b/35mmjack-dummy.scad
new file mode 100644 (file)
index 0000000..1a675a5
--- /dev/null
@@ -0,0 +1,32 @@
+// -*- C -*-
+
+p2 = [ 0, 3.0 /2 ];
+p1 = p2 + [ -1.0, -1.0 ];
+p3 = [ 3.0, 2.5 /2 ];
+p4 = [ p3[0] + (3.2-2.5)/2 , 3.2 /2 ];
+p8 = [ 13.0, 8.0 /2 ];
+p5 = [ p8[0] - 8.5, 3.2 /2 ];
+p6 = [ p5[0] + (3.5-3.2)/2, 3.5 /2];
+p7 = [ p8[0], p6[1] ];
+p9 = p8 + [ 10, 0 ];
+
+$fa = 1;
+$fs = 0.1;
+
+module Plan(){
+  polygon([[ p1[0], 0.1 ],
+          p1, p2, p3, p4, p5, p6, p7, p8, p9,
+          [ p9[0], 0.1 ]]);
+}
+
+module Dummy(){
+  rotate_extrude()
+    rotate([0,0,-90])
+    Plan();
+  translate([0,0, -p1[0]])
+    mirror([0,0,1])
+    cylinder(r= 0.2, h= p9[0] - p1[0]);
+}
+
+//Plan();
+Dummy();
diff --git a/GPL-3 b/GPL-3
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
+++ b/GPL-3
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/Huxley-INSTRUCTIONS b/Huxley-INSTRUCTIONS
new file mode 100644 (file)
index 0000000..c5efa32
--- /dev/null
@@ -0,0 +1,79 @@
+To prepare file:
+
+ * Make stl file.
+     openscad -o test-object.tmp.stl test-object.scad
+   or equivalent.
+
+ * Slice the file.  On appropriate OS run Slic3r.
+   I was using 0.9.7.
+   Download from here:
+      http://slic3r.org/download
+
+   Then
+
+     /home/reprap/Slic3r/bin/slic3r --load /home/reprap/play/slic3r-config.ini test-object.stl
+
+   Produces test-object.gcoce.
+
+To print:
+
+ * Turn on printer and connect computer.
+ * Start pronterface
+     cd /home/reprap/reprappro-software.git/
+     ./pronterface/pronterface.py 
+ * Select "connect" (make sure it's ttyUSB0 115200),
+   wait for printer to connect
+ * Tick "watch"
+ * Turn on the hotend and bed heaters
+ * Remove MicroSD card from printer and copy the file to it
+   (give it an 8.3 filename ending in .G)
+ * Insert MicroSD card back into printer
+ * Wait for bed (and hotend) to get up to temperature
+ * If there's lots of ooze, pick off nozzle with fine-nosed pliers
+ * "SD" / "Print", select the file you uploaded earlier
+ * Watch while it starts and make sure that the skirt sticks to
+   printbed and nothing gets tangled
+ * Check that filament feed path from feedstock coil looks good.
+
+After printing:
+
+ * You can right away unclip the bed plate.
+ * Wait for the object and bed plate to cool before removing it.
+
+*** DO NOT TURN PRINTER OFF WHILE HOT END IS HOT ***
+
+Levelling (occasionally, or after transport):
+
+ * Home in pronterface
+ * Set level:
+     Use FL/FR to move to front left / right
+     Use Home Z to make it go up and down
+     Observe motion of bed - should be just none
+     Turn RH thread with fingers to adjust so above is true
+ (Levelling screws front/back are at RH of bed plate.)
+
+Bed cleaning: use isopropanol
+
+------------------------------------------------------------
+
+make these symlinks
+~/.pronsolerc -> /home/reprap/play/pronsolerc (adjust to taste)
+
+Slic3r - is from slic3r-linux-x86-0-9-7.tar.gz
+ /home/reprap/Slic3r/bin/slic3r --load /home/reprap/play/slic3r-config.ini --ignore-nonexistent-config
+
+repsnapper - only used this so far for its gcode viewer
+
+------------------------------------------------------------
+
+firmware upgrade:
+
+ firmware is in marlin.git#iwj and works with make
+   various other firmwares for comparison only
+    sprinter-reprappro.git sprinter.git
+ produces Marlin.hex
+ depends on stuff in arduino-0022 (doesn't work with other arduino
+   versions, incompatible)
+ program with
+   avrdude -b 38400 -v -P /dev/ttyUSB0 -p atmega644P -c arduino -U flash:w:Marlin.hex
+ needs the reset jumper (which is right next to the mcu) fitted
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..2821dee
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,150 @@
+# reprap-objects Makefile
+#
+# Build scripts for various 3D designs
+# Copyright 2012-2016 Ian Jackson
+#
+# This work is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This work 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 work.  If not, see <http://www.gnu.org/licenses/>.
+
+FILAMENTSPOOL_AUTOS = filamentspool filamentspool-lt filamentspool-sm
+FILAMENTSPOOL_AUTOS += filamentspool-storarm3
+
+QUACKSES = $(addprefix quacks-ingredients-L, 1 2 3 4 5)
+QUACKS_SCADS = $(addsuffix .scad, $(QUACKSES))
+
+USING_AUTOS ?= $(FILAMENTSPOOL_AUTOS) xeno-drivebay-bracket dungeonquest-cone anke-gps-bracket cable-hole-trunking-cover anglepoise-neck crossbar-computer-led-mount wardrobe-hook knifeblock pandemic-counter pattress-boxes-3-cover bike-lipo-box earring-stand bike-stalk-led-mount sewing-table sewing-table-test sewing-table-jig maglite-holder poster-tube-lid poster-tube-lid-coarse fairphone-case fairphone-battery-case fairphone4-case fairphone4-case-coarse lock-inframe-bracket ksafe-base $(QUACKSES) quacks-ingredients-demos mic-table-clamp nook-case nook-case-test scaffold-clamp-common scaffold-clamp-tensioner scaffold-clamp-linear-bracket scaffold-clamp-straphook powerbank-bike-clamp topeak-mtx-tortec-expeditionrack-adapter lipo-flat-mount laptop-sound-cable-hooks $(foreach x,500 1000,adafruit-powerboost-$x)
+
+AUTO_INCS += sealing-box.scad sewing-table.scad nutbox.scad \
+            powerbank-anker-10000.dxf \
+            poster-tube-lid-parametric.scad \
+            $(QUACKS_SCADS)
+
+AUTO_STLS_INCS += poster-tube-lid,CatchPostDistort-fa3.stl
+AUTO_STLS_INCS += poster-tube-lid,CatchPostDistort-fa20.stl
+
+include reprap-objects.make
+
+dovecliptest.stl: doveclip.scad $(AUTO_INCS)
+
+KNIFEBLOCK_KNIVES= 0 1 2
+KNIFEBLOCK_TEMPLATES= bl hl
+KNIFEBLOCK_TEMPLATE_FILES=\
+       $(foreach k,$(KNIFEBLOCK_KNIVES), \
+       $(foreach t,$(KNIFEBLOCK_TEMPLATES), \
+       knifeblock-knives-t$k$t.dxf))
+
+knifeblock-knives-templates knifeblock.stl: $(KNIFEBLOCK_TEMPLATE_FILES)
+
+.PRECIOUS: knifeblock-knives-t%.dxf
+knifeblock-knives-t%.dxf: knifeblock-knives-filter knifeblock-knives-trace.fig
+               ./$< $(notdir $*) <$(filter %.fig, $^) >$@.tmp.fig
+               fig2dev -D -30 -L eps <$@.tmp.fig >$@.tmp.eps
+               pstoedit -dt -f "dxf: -polyaslines -mm" $@.tmp.eps $@
+
+PANDEMICCOUNTER_LETTERS=30 31 32 33 34 35
+PANDEMICCOUNTER_DXFS=$(foreach l,$(PANDEMICCOUNTER_LETTERS), \
+       pandemic-counter-l$l.dxf)
+
+pandemic-counter-letters: $(PANDEMICCOUNTER_DXFS)
+pandemic-counter%.stl: $(PANDEMICCOUNTER_DXFS)
+
+.PRECIOUS: pandemic-counter-l%.eps
+pandemic-counter-l%.eps: pandemic-counter-letters.fig
+               fig2dev -D +$(notdir $*) -L eps <$< >$@.tmp
+               @mv -f $@.tmp $@
+
+.PRECIOUS: maglite-holder-torch-curve.eps
+maglite-holder-torch-curve.eps: maglite-holder-torch.fig
+               fig2dev -D +1:70 -L eps <$< >$@.tmp
+               @mv -f $@.tmp $@
+
+maglite-holder-torch-curve.dxf: maglite-holder-torch-curve.eps
+               pstoedit -dt -flat 0.05 -f "dxf: -polyaslines -mm" $< $@
+
+powerbank-anker-10000.dxf: powerbank-anker-10000.eps
+               pstoedit -dt -f "dxf: -polyaslines -mm" $< $@
+
+PANDEMICQUARANTINES_NUMBERS=1 2
+PANDEMICQUARANTINES_DXFS=$(foreach l,$(PANDEMICQUARANTINES_NUMBERS), \
+       pandemic-quarantine-l$l.dxf)
+
+pandemic-quarantine-numbers: $(PANDEMICQUARANTINES_DXFS)
+pandemic-quarantine%.stl: $(PANDEMICQUARANTINES_DXFS)
+
+.PRECIOUS: pandemic-quarantine-l%.eps
+pandemic-quarantine-l%.eps: pandemic-quarantine-numbers.fig
+               fig2dev -D +$(notdir $*) -L eps <$< >$@.tmp
+               @mv -f $@.tmp $@
+
+FILAMENTSPOOL_NUMBERS=$(shell seq 300 100 1500)
+filamentspool-number-n%.eps:   filamentspool-number.eps.pl
+       ./$< $* >$@.tmp && mv -f $@.tmp $@
+
+FILAMENTSPOOL_DXFS=$(foreach n,$(FILAMENTSPOOL_NUMBERS), \
+       filamentspool-number-n$n.dxf)
+
+$(addsuffix .auto.stl, $(foreach f,$(FILAMENTSPOOL_AUTOS),$(shell \
+       $(PLAY)/toplevel-find $(PLAY)/$f))): $(FILAMENTSPOOL_DXFS)
+
+filamentspool-numbers filamentspool.stl: $(FILAMENTSPOOL_DXFS)
+
+SCREWRECESSTEST_SIZES= 2 3 4 5 6
+SCREWRECESSTEST_DXFS=$(foreach s,$(SCREWRECESSTEST_SIZES), \
+       screw-recess-test-number-s$s.dxf)
+
+screw-recess-test-number-s%.fig: screw-recess-test-number.fig.pl
+       ./$< $* >$@.tmp && mv -f $@.tmp $@
+
+screw-recess-test-number-s%.eps: screw-recess-test-number-s%.fig
+               fig2dev -L eps <$< >$@.tmp
+               @mv -f $@.tmp $@
+
+screw-recess-test-numbers screw-recess-test.stl: $(SCREWRECESSTEST_DXFS)
+
+question-question.eps: question-question.fig
+               fig2dev -L eps <$< >$@.tmp
+               @mv -f $@.tmp $@
+
+sewing-table%.stl: sewing-table-rear-profile.dxf
+sewing-table%.stl: sewing-table-front-profile.dxf
+sewing-table%.stl: sewing-table-end-profile.dxf
+
+sewing-table-%-profile.eps: sewing-table-%-profile.fig
+               fig2dev -L eps -D +40 <$< >$@.tmp
+               @mv -f $@.tmp $@
+
+question-token.stl: question-question.dxf
+
+lemon-stand.stl: lemon-stand.scad
+
+electron-token.stl: electron-token.scad
+
+quacks-scads: $(addsuffix .auto.scads, $(QUACKSES))
+quacks-scads: quacks-ingredients-demos.auto.scads
+
+quacks-stls: $(addsuffix .auto.stls, $(QUACKSES))
+
+.PRECIOUS: $(SCREWRECESSTEST_DXFS) $(SCREWRECESSTEST_DXFS) \
+       $(foreach s,$(SCREWRECESSTEST_SIZES), \
+               screw-recess-test-number-s$s.fig \
+               screw-recess-test-number-s$s.eps)
+
+poster-tube-lid,CatchAssembly.auto.stl: poster-tube-lid,CatchPostDistort-fa3.stl
+poster-tube-lid-coarse,CatchAssembly.auto.stl: poster-tube-lid,CatchPostDistort-fa20.stl
+
+poster-tube-lid,CatchPostDistort-fa%.stl: \
+               distort-stl poster-tube-lid,CatchPreDistort.auto.stl
+       ./distort-stl <poster-tube-lid,CatchPreDistort.auto.stl \
+               set-fa $(notdir $*) project-cylinder 100 >$@.tmp
+       mv -f $@.tmp $@
+
diff --git a/Marlin.hex b/Marlin.hex
new file mode 100644 (file)
index 0000000..30cf7e0
--- /dev/null
@@ -0,0 +1,3895 @@
+:100000000C94DD090C94050A0C94050A0C94050A5D\r
+:100010000C94050A0C94050A0C94050A0C94050A24\r
+:100020000C94050A0C94050A0C94050A0C94050A14\r
+:100030000C94050A0C944F510C94050A0C94050A73\r
+:100040000C94050A0C94CB610C94B32A0C94050A09\r
+:100050000C94752C0C94050A0C94050A0C94050A52\r
+:100060000C94050A0C94050A0C94050A0C94050AD4\r
+:100070000C94050A0C94050A0C94050A084AD73B0F\r
+:100080003BCE016E84BCBFFDC12F3D6C74319ABD67\r
+:1000900056833DDA3D00C77F11BED9E4BB4C3E918B\r
+:1000A0006BAAAABE000000803F05A84CCDB2D44E7A\r
+:1000B000B93836A9020C50B9918688083CA6AAAA7C\r
+:1000C0002ABE000000803F6563686F3A00656E716C\r
+:1000D0007565696E67202200220073746172740076\r
+:1000E000506F7765725570002045787465726E6147\r
+:1000F0006C205265736574002042726F776E206FBA\r
+:100100007574205265736574002057617463686468\r
+:100110006F672052657365740020536F6674776152\r
+:100120007265205265736574004D61726C696E2052\r
+:1001300000312E302E302052433200204C61737437\r
+:1001400020557064617465643A2000323031322D7C\r
+:1001500030372D323900207C20417574686F723A37\r
+:10016000200069776A002046726565204D656D6FD5\r
+:1001700072793A20002020506C616E6E6572427573\r
+:100180006666657242797465733A200053746F72C3\r
+:1001900065642073657474696E6773207265747228\r
+:1001A00065697665643A006F6B00446F6E65207315\r
+:1001B0006176696E672066696C652E004572726FA4\r
+:1001C000723A004C696E65204E756D6265722069E9\r
+:1001D00073206E6F74204C617374204C696E6520BF\r
+:1001E0004E756D6265722B312C204C617374204CFE\r
+:1001F000696E653A00636865636B73756D206D6940\r
+:10020000736D617463682C204C617374204C696E4B\r
+:10021000653A004E6F20436865636B73756D207798\r
+:10022000697468206C696E65206E756D6265722CEC\r
+:10023000204C617374204C696E653A004E6F204CFF\r
+:10024000696E65204E756D626572207769746820ED\r
+:10025000636865636B73756D2C204C617374204CFF\r
+:10026000696E653A006F6B005072696E746572203A\r
+:1002700073746F707065642064657520746F206599\r
+:1002800072726F72732E20466978207468652065DB\r
+:1002900072726F7220616E6420757365204D3939FA\r
+:1002A0003920746F2072657374617274212E202856\r
+:1002B00054656D7065726174757265206973207222\r
+:1002C000657365742E2053657420697420626566B9\r
+:1002D0006F72652072657374617274696E6729004C\r
+:1002E000446F6E65207072696E74696E672066690E\r
+:1002F0006C6500426567696E2066696C65206C6993\r
+:10030000737400456E642066696C65206C69737453\r
+:10031000006F6B20543A00202F0020423A00202F1B\r
+:100320000020403A00543A0020453A0020573A0055\r
+:10033000543A0020453A0020423A004649524D576F\r
+:100340004152455F4E414D453A4D61726C696E2098\r
+:1003500056313B20537072696E7465722F677262FA\r
+:100360006C206D617368757020666F722067656EB2\r
+:1003700036204649524D574152455F55524C3A68D6\r
+:100380007474703A2F2F7777772E6D656E64656C75\r
+:100390002D70617274732E636F6D2050524F544FE5\r
+:1003A000434F4C5F56455253494F4E3A312E302001\r
+:1003B0004D414348494E455F545950453A4D656E4D\r
+:1003C00064656C2045585452554445525F434F551F\r
+:1003D0004E543A310A00583A00593A005A3A004508\r
+:1003E0003A0020436F756E7420583A00593A005A0B\r
+:1003F0003A00785F6D696E3A00795F6D696E3A0018\r
+:100400007A5F6D696E3A00556E6B6E6F776E206322\r
+:100410006F6D6D616E643A22002200FFFFFF0000E5\r
+:1004200000000000000000000000000000000000CC\r
+:100430000000000080BF00001B43000016430000C6\r
+:10044000B4420000A0400000A0400000803F000037\r
+:100450001B43000016430000B64253657474696E76\r
+:1004600067732053746F72656400537465707320F2\r
+:1004700070657220756E69743A0020204D39322003\r
+:100480005800205900205A002045004D6178696DC0\r
+:10049000756D2066656564726174657320286D6D85\r
+:1004A0002F73293A0020204D323033205800205934\r
+:1004B00000205A002045004D6178696D756D20411E\r
+:1004C0006363656C65726174696F6E20286D6D2F52\r
+:1004D0007332293A0020204D323031205800205903\r
+:1004E00000205A00204500416363656C65726174A9\r
+:1004F000696F6E3A20533D616363656C6572617428\r
+:10050000696F6E2C20543D72657472616374206152\r
+:100510006363656C65726174696F6E0020204D3293\r
+:100520003034205300205400416476616E6365646A\r
+:10053000207661726961626C65733A20533D4D6942\r
+:100540006E20666565647261746520286D6D2F7319\r
+:10055000292C20543D4D696E2074726176656C20A3\r
+:10056000666565647261746520286D6D2F73292C32\r
+:1005700020423D6D696E696D756D207365676D65AF\r
+:100580006E742074696D6520286D73292C20583D88\r
+:100590006D6178696D756D207859206A65726B2080\r
+:1005A000286D6D2F73292C20205A3D6D6178696D5F\r
+:1005B000756D205A206A65726B20286D6D2F732926\r
+:1005C0000020204D323035205300205400204200BE\r
+:1005D000205800205A00204500486F6D65206F6646\r
+:1005E0006673657420286D6D293A0020204D3230E5\r
+:1005F00036205800205900205A0050494420736585\r
+:100600007474696E67733A002020204D33303120B6\r
+:100610005000204900204400526573656E643A0022\r
+:100620006F6B005072696E7465722068616C7465DE\r
+:10063000642E206B696C6C28292063616C6C656486\r
+:10064000202121005072696E7465722073746F707E\r
+:100650007065642064657520746F206572726F72B6\r
+:10066000732E2046697820746865206572726F72F7\r
+:1006700020616E6420757365204D39393920746F9F\r
+:100680002072657374617274212E202854656D7018\r
+:100690006572617475726520697320726573657423\r
+:1006A0002E20536574206974206265666F72652020\r
+:1006B00072657374617274696E6729007C3C3E5E7A\r
+:1006C0002B3D3F2F5B5D3B2C2A225C006563686FEE\r
+:1006D0003A0020636F6C6420657874727573696F7B\r
+:1006E0006E2070726576656E7465640020746F6F3D\r
+:1006F000206C6F6E6720657874727573696F6E20F9\r
+:1007000070726576656E746564006563686F3A0043\r
+:10071000656E6473746F7073206869743A2000208A\r
+:10072000583A0020593A00205A3A0024F404D920BB\r
+:100730001BC40C5C0E9804C4095F0265077101F4C8\r
+:1007400005F900FB04B30048048700C103690058A1\r
+:1007500003550003034500BE023A008402310053F2\r
+:10076000022A002902250004022000E4011C00C81E\r
+:10077000011900AF011700980114008401130071E2\r
+:100780000110006101100051010E0043010D0036FF\r
+:10079000010B002B010B0020010B00150109000CBF\r
+:1007A00001090003010800FB000800F3000800EB4A\r
+:1007B000000700E4000600DE000600D8000600D2B4\r
+:1007C000000600CC000500C7000500C2000500BD02\r
+:1007D000000400B9000400B5000400B1000400AD3D\r
+:1007E000000400A9000400A5000300A20003009F6C\r
+:1007F0000004009B00030098000300950002009392\r
+:10080000000300900003008D0002008B00030088AD\r
+:100810000002008600020084000300810002007FC5\r
+:100820000002007D0002007B0002007900020077D8\r
+:1008300000010076000200740002007200010071E5\r
+:100840000002006F0002006D0001006C0002006AEF\r
+:1008500000010069000200670001006600010065F8\r
+:1008600000010064000200620001006100010060FC\r
+:100870000001005F0002005D0001005C0001005B00\r
+:100880000001005A00010059000100580001005702\r
+:100890000001005600010055000100540001005302\r
+:1008A00000000053000100520001005100010050FF\r
+:1008B0000001004F0001004E0000004E0001004DFD\r
+:1008C0000001004C0001004B0000004B0001004AF9\r
+:1008D00000010049000100480000004800010047F5\r
+:1008E00000010046000000460001004500000045F0\r
+:1008F00000010044000100430000004300010042E9\r
+:1009000000000042000100410000004100010040E1\r
+:100910000001003F0000003F0001003E0000003EDB\r
+:100920000001003D0000003D0001003C0000003CD3\r
+:100930000000003C0001003B0000003B0001003AC9\r
+:100940000000003A000100390000003900010038C1\r
+:1009500000000038000000380001003700000037B8\r
+:1009600000010036000000360000003600010035AE\r
+:1009700000000035000000350001003400000034A4\r
+:100980000000003400010033000000330000003399\r
+:10099000000100320000003200000032000100318E\r
+:1009A0000000003100000031000100300000003084\r
+:1009B000000000300001002F0000002F0000002F79\r
+:1009C0000000002F0001002E0000002E0000002E6D\r
+:1009D0000001002D0000002D0000002D0000002D62\r
+:1009E0000001002C0000002C0000002C0000002C56\r
+:1009F0000001002B0000002B0000002B0000002B4A\r
+:100A00000001002A0000002A0000002A0000002A3D\r
+:100A10000001002900000029000000290000002931\r
+:100A20000000002900010028000000280000002824\r
+:100A30000000002800000028000100270000002717\r
+:100A4000000000270000002700000027000100260A\r
+:100A500000000026000000260000002600000026FE\r
+:100A600000010025000000250000002500000025F1\r
+:100A700000000025000000250001002400000024E3\r
+:100A800000000024000000240000002400010023D6\r
+:100A900000000023000000230000002300000023CA\r
+:100AA00000000023000000230001002200000022BB\r
+:100AB00000000022000000220000002200000022AE\r
+:100AC00000010021000000210000002100000021A1\r
+:100AD0000000002100000021000000210001002092\r
+:100AE0000000002000000020000000200000002086\r
+:100AF0000000002000000020000000200001001F76\r
+:100B00000000001F0000001F0000001F0000001F69\r
+:100B10000000001F0000001F0001001E0000001E5A\r
+:100B20000000001E0000001E00000024F4D430501D\r
+:100B3000C38E20C2A24017828B7011127A910D8150\r
+:100B40006CD90AA861E108C7586607615143061EBF\r
+:100B50004B5D05C145A7041A411104093D98037175\r
+:100B60003931034036DB0265339102D430540280C0\r
+:100B70002E1D02632CEE01752AC501B028A00110BC\r
+:100B80002781018F2564012B244B01E0223401AC25\r
+:100B9000211F018D200D01801FFC00841EED009798\r
+:100BA0001DDF00B81CD200E61BC600201BBC006481\r
+:100BB0001AB200B219A8000A19A0006A189900D147\r
+:100BC00017910040178B00B516840031167E00B3D4\r
+:100BD0001579003A157300C7146F0058146A00EEB7\r
+:100BE0001366008813630025135E00C7125B006C58\r
+:100BF00012570015125400C111510070114F0021FD\r
+:100C0000114B00D61049008D1047004610440002D9\r
+:100C1000104200C00F4000800F3E00420F3C000613\r
+:100C20000F3B00CB0E3800930E37005C0E350027CB\r
+:100C30000E3400F30D3200C10D3100900D30006014\r
+:100C40000D2E00320D2D00050D2C00D90C2B00AE01\r
+:100C50000C2900850C29005C0C2700350C27000EA0\r
+:100C60000C2600E80B2400C40B2400A00B23007DFD\r
+:100C70000B23005A0B2100390B2100180B2000F820\r
+:100C80000A1F00D90A1E00BB0A1E009D0A1D008013\r
+:100C90000A1D00630A1C00470A1B002C0A1B0011D6\r
+:100CA0000A1A00F7091A00DD091900C4091900AB76\r
+:100CB000091900920917007B091800630917004CF5\r
+:100CC00009160036091600200916000A091500F554\r
+:100CD000081500E0081400CC081400B8081400A49B\r
+:100CE000081400900813007D0812006B08130058C8\r
+:100CF00008120046081200340811002308110012DF\r
+:100D000008110001081100F0071000E0071000D0E2\r
+:100D1000071000C0071000B0070F00A107100091D6\r
+:100D2000070E0083070F0074070F0065070E0057BA\r
+:100D3000070E0049070E003B070D002E070E00208E\r
+:100D4000070D0013070D0006070D00F9060C00ED56\r
+:100D5000060D00E0060C00D4060C00C8060C00BC12\r
+:100D6000060C00B0060C00A4060B0099060C008DC2\r
+:100D7000060B0082060B0077060B006C060B006169\r
+:100D8000060A0057060B004C060A0042060A003805\r
+:100D9000060A002E060A0024060A001A060A001097\r
+:100DA00006090007060A00FD050900F4050900EB25\r
+:100DB000050900E2050900D9050900D0050900C7A9\r
+:100DC000050900BE050900B5050800AD050800A528\r
+:100DD0000509009C050800940508008C050800849E\r
+:100DE0000508007C050800740508006C050700650F\r
+:100DF0000508005D050700560508004E0507004779\r
+:100E00000507004005080038050700310507002ADE\r
+:100E1000050700230507001C050600160507000F3F\r
+:100E20000507000805060002050700FB040600F59B\r
+:100E3000040700EE040600E8040600E2040700DBF5\r
+:100E4000040600D5040600CF040600C9040600C34A\r
+:100E5000040600BD040600B7040600B1040500AC9A\r
+:100E6000040600A6040600A00405009B04060095E5\r
+:100E7000040500900406008A04050085040500802E\r
+:100E80000406007A04050075040500700405006B73\r
+:100E900004050066040500610405005C04050057B4\r
+:100EA000040500520405004D0405004804050043F4\r
+:100EB0000405003E0404003A040500350405003032\r
+:100EC0000404002C04050027040400230405001E6C\r
+:100ED0000404001A04040016040500110404000DA3\r
+:100EE000040400090405000404040000040400FCD8\r
+:100EF000030400F8030400F4030400F0030400EC0E\r
+:100F0000030400E8030400E4030400E0030400DC3D\r
+:100F1000030400D8030400D4030400D0030400CC6D\r
+:100F2000030400C8030300C5030300206269617362\r
+:100F30003A200020643A2000206D696E3A2000209B\r
+:100F40006D61783A2000204B753A20002054753AA4\r
+:100F5000200020436C6173696320504944200020C5\r
+:100F60004B703A2000204B693A2000204B643A2015\r
+:100F700000504944204175746F74756E6520666138\r
+:100F8000696C6564212054656D7065726174757259\r
+:100F90006520746F2068696768006F6B20543A00A1\r
+:100FA00020403A00504944204175746F74756E6555\r
+:100FB000206661696C6564212074696D656F757464\r
+:100FC00000504944204175746F74756E65206669E0\r
+:100FD0006E6973686564202120506C6163652074BC\r
+:100FE0006865204B702C204B6920616E64204B6437\r
+:100FF00020636F6E7374616E747320696E20746801\r
+:101000006520636F6E66696775726174696F6E2EB5\r
+:1010100068004572726F723A00202D20496E766129\r
+:101020006C6964206578747275646572206E756D84\r
+:10103000626572210070012C0190012701B001222C\r
+:1010400001C0011D01F00118011002130130020E50\r
+:10105000016002090190020401C002FF000003FACE\r
+:10106000004003F5008003F000D003EB002004E60D\r
+:10107000007004E100E004DC004005D700C005D2A8\r
+:10108000004006CD00D006C8008007C3003008BE6F\r
+:1010900000F008B900C009B400B00AAF00B00BAA54\r
+:1010A00000D00CA500000EA000500F9B00C01096B1\r
+:1010B000005012910000148C00C0158700B01782F8\r
+:1010C00000B0197D00D01B7800001E730040206E18\r
+:1010D0000090226900F024640040275F0090295AA4\r
+:1010E00000E02B5500102E500020304B00103246EF\r
+:1010F00000E033410090353C001037370070383243\r
+:1011000000A0392D00B03A2800A03B2300603C1E0F\r
+:1011100000103D1900903D1400103E0F00703E0A73\r
+:1011200000C03E0500003F0000202D20496E766182\r
+:101130006C6964206578747275646572206E756D73\r
+:101140006265722021003A20457874727564657278\r
+:10115000207377697463686564206F66662E204D1E\r
+:10116000415854454D5020747269676765726564D3\r
+:101170002021003A20457874727564657220737777\r
+:10118000697463686564206F66662E204D494E540D\r
+:10119000454D50207472696767657265642021004F\r
+:1011A00054656D70657261747572652068656174EF\r
+:1011B0006564206265642073776974636865642080\r
+:1011C0006F66662E204D415854454D50207472690B\r
+:1011D000676765726564202121006563686F3A0066\r
+:1011E000534420696E6974206661696C00457272AF\r
+:1011F0006F723A00766F6C756D652E696E6974203A\r
+:101200006661696C6564006F70656E526F6F742003\r
+:101210006661696C65640053442063617264206F89\r
+:101220006B006F70656E206661696C65642C20468A\r
+:10123000696C653A20002E0046696C65206F706508\r
+:101240006E65643A002053697A653A0046696C65B8\r
+:101250002073656C6563746564006F70656E2066ED\r
+:1012600061696C65642C2046696C653A20002E002B\r
+:101270006F70656E206661696C65642C2046696CD0\r
+:10128000653A20002E0057726974696E6720746F8A\r
+:101290002066696C653A20006F70656E2066616932\r
+:1012A0006C65642C2046696C653A20002E00466906\r
+:1012B0006C652064656C657465643A0044656C65B2\r
+:1012C00074696F6E206661696C65642C2046696C78\r
+:1012D000653A20002E005344207072696E74696E66\r
+:1012E00067206279746520002F004E6F742053448C\r
+:1012F000207072696E74696E67006572726F722019\r
+:1013000077726974696E6720746F2066696C650016\r
+:1013100000000001020000000000000004030706B6\r
+:1013200000000000000000000000000000000000BD\r
+:1013300001020408102040800102040810204080AF\r
+:10134000010204081020408080402010080402019F\r
+:10135000020202020202020204040404040404045D\r
+:10136000030303030303030301010101010101015D\r
+:10137000002225282B002124272A494E46494E4980\r
+:1013800054594E414E000020410000C84200401C0C\r
+:101390004620BCBE4CCA1B0E5AAEC59D74CDCCCCEB\r
+:1013A0003D0AD7233C17B7D13877CC2B329595E639\r
+:1013B000241FB14F0A00AB2AAF2A11241FBECFEF62\r
+:1013C000D0E1DEBFCDBF12E0A0E0B1E0ECE6F1EF8E\r
+:1013D00002C005900D92A63EB107D9F71EE0A6EE19\r
+:1013E000B2E001C01D92A534B107E1F713E1C8EBEB\r
+:1013F000D3E104C02297FE010E94B472C63BD1071C\r
+:10140000C9F70E947D2A0C94A9780C940000FC0175\r
+:101410004591559165917491E52F362F272F642FB3\r
+:101420007E2F832F922F08952F923F924F925F929B\r
+:101430006F927F928F929F92AF92BF92CF92DF92E4\r
+:10144000EF92FF920F931F93CF93DF9300D000D0C2\r
+:1014500000D0CDB7DEB78C019C01220F331F220FC5\r
+:10146000331F3E832D832B503D4F3A832983F9014F\r
+:10147000108211821282138221E033E04DEF52E09C\r
+:1014800069EF72E085EF92E00E94F74F8D819E81B7\r
+:10149000825B9B4F0E94070AF801E55EFB4F0491B7\r
+:1014A0002EE8222E25E0322E2D813E81220E331E83\r
+:1014B00020E030E040EC5FE30E9457712B013C01DB\r
+:1014C000602F772767FD7095872F972F0E94246FD5\r
+:1014D0004B015C019B01AC01C301B2010E94577139\r
+:1014E000F10160837183828393832D813E81245E29\r
+:1014F0003E4F3C832B83F90160817181828193810E\r
+:1015000060932C0170932D0180932E0190932F01F5\r
+:1015100020E030E040E752E40E94896E38EEC32EAE\r
+:1015200032E0D32E7B018C012AE935E046E955E013\r
+:1015300062E975E08EE895E00E9468450E94AC572C\r
+:10154000E981FA81108211821282138221E033E054\r
+:101550004DEF52E069EF72E085EF92E00E94F74FA5\r
+:101560008D819E818E5B9B4F0E94070A2B013C015F\r
+:101570009058A50194010E945771F1016083718315\r
+:101580008283938320E030E040E752E460912C01B5\r
+:1015900070912D0180912E0190912F010E94896EF2\r
+:1015A0007B018C012AE935E046E955E062E975E006\r
+:1015B0008EE895E00E9468450E94AC57A301920115\r
+:1015C000C301B2010E94A96DA50194010E94577147\r
+:1015D000F101608371838283938320E030E040E0F7\r
+:1015E0005FE3EB81FC8160817181828193810E9444\r
+:1015F0005771FB01462F5F2F682F792F40932C01E5\r
+:1016000050932D0160932E0170932F0120E030E064\r
+:1016100040E752E4BF010E94896E7B018C012AE9F8\r
+:1016200035E046E955E062E975E08EE895E00E9414\r
+:1016300068450E94AC578D819E81825E9B4F0E94BF\r
+:10164000070AED81FE81E751FD4F80809180A280E5\r
+:10165000B380A50194010E94A96D2B013C01982F34\r
+:10166000442D552D692F772DE981FA8140835183CF\r
+:10167000628373838D819E81865D9B4F0E94070AE2\r
+:101680000D811E81045F1E4FA50194010E94A96D6A\r
+:10169000F80160837183828393838D819E818A5C4C\r
+:1016A0009B4F0E94070A0D811E8100501F4FA5010C\r
+:1016B00094010E94A96DF8016083718382839383F2\r
+:1016C000842D952DA62DB72DF10180839183A283C2\r
+:1016D000B38310922C0110922D0110922E011092C2\r
+:1016E0002F0126960FB6F894DEBF0FBECDBFDF9157\r
+:1016F000CF911F910F91FF90EF90DF90CF90BF900F\r
+:10170000AF909F908F907F906F905F904F903F90A1\r
+:101710002F900C943F5107C03091C00035FFFCCF93\r
+:101720002093C6000196FC0124912111F5CF22E0FF\r
+:1017300030E081EC95E00C94B92E07C03091C000E8\r
+:1017400035FFFCCF2093C6000196FC0124912111A6\r
+:10175000F5CF22E030E081EC95E00C94B92E07C083\r
+:101760003091C00035FFFCCF2093C6000196FC01EC\r
+:1017700024912111F5CF2AE030E081EC95E00C9422\r
+:101780009C2DCF93DF93C7ECD0E0FE0107C0909172\r
+:10179000C00095FFFCCF8093C600319684918111E3\r
+:1017A000F6CFEAE6F4E007C09091C00095FFFCCFC9\r
+:1017B0008093C600319684918111F6CF8091C0004C\r
+:1017C00085FFFCCF8AE08093C600FE018491E7ECA0\r
+:1017D000F0E008C09091C00095FFFCCF8093C60058\r
+:1017E000319684918111F6CF4091730D5091740D13\r
+:1017F0006091750D7091760D8AE794E00E948B0BD5\r
+:101800004091770D5091780D6091790D70917A0D1E\r
+:1018100082E894E00E948B0B40917B0D50917C0DEF\r
+:1018200060917D0D70917E0D85E894E00E948B0B98\r
+:1018300040917F0D5091800D6091810D7091820DCE\r
+:1018400088E894E00E948B0B8091C00085FFFCCF5C\r
+:101850008AE08093C600FE018491E7ECF0E008C0C6\r
+:101860009091C00095FFFCCF8093C6003196849183\r
+:101870008111F6CFEBE8F4E007C09091C00095FF2E\r
+:10188000FCCF8093C600319684918111F6CF809170\r
+:10189000C00085FFFCCF8AE08093C600FE018491E2\r
+:1018A000E7ECF0E008C09091C00095FFFCCF80937A\r
+:1018B000C600319684918111F6CF4091830D5091ED\r
+:1018C000840D6091850D7091860D85EA94E00E94EB\r
+:1018D0008B0B4091870D5091880D6091890D70910F\r
+:1018E0008A0D8EEA94E00E948B0B40918B0D5091F3\r
+:1018F0008C0D60918D0D70918E0D81EB94E00E94A6\r
+:101900008B0B40918F0D5091900D6091910D7091C6\r
+:10191000920D84EB94E00E948B0B8091C00085FFB8\r
+:10192000FCCF8AE08093C600FE018491E7ECF0E0F2\r
+:1019300008C09091C00095FFFCCF8093C6003196FF\r
+:1019400084918111F6CFE7EBF4E007C09091C000DD\r
+:1019500095FFFCCF8093C600319684918111F6CF1C\r
+:101960008091C00085FFFCCF8AE08093C600FE0115\r
+:101970008491E7ECF0E008C09091C00095FFFCCFA7\r
+:101980008093C600319684918111F6CF4091630D0A\r
+:101990005091640D6091650D7091660D85ED94E038\r
+:1019A0000E94AF0B4091670D5091680D6091690DD9\r
+:1019B00070916A0D8EED94E00E94AF0B40916B0D1B\r
+:1019C00050916C0D60916D0D70916E0D81EE94E0F3\r
+:1019D0000E94AF0B40916F0D5091700D6091710D91\r
+:1019E0007091720D84EE94E00E94AF0B8091C00064\r
+:1019F00085FFFCCF8AE08093C600FE018491E7EC6E\r
+:101A0000F0E008C09091C00095FFFCCF8093C60025\r
+:101A1000319684918111F6CFE7EEF4E007C0909102\r
+:101A2000C00095FFFCCF8093C60031968491811150\r
+:101A3000F6CF8091C00085FFFCCF8AE08093C6007E\r
+:101A4000FE018491E7ECF0E008C09091C00095FFA2\r
+:101A5000FCCF8093C600319684918111F6CF4091DE\r
+:101A60005B0D50915C0D60915D0D70915E0D8CE190\r
+:101A700095E00E948B0B4091570D5091580D60914D\r
+:101A8000590D70915A0D85E295E00E948B0B809163\r
+:101A9000C00085FFFCCF8AE08093C600FE018491E0\r
+:101AA000E7ECF0E008C09091C00095FFFCCF809378\r
+:101AB000C600319684918111F6CFE8E2F5E007C0C7\r
+:101AC0009091C00095FFFCCF8093C6003196849121\r
+:101AD0008111F6CF8091C00085FFFCCF8AE0809312\r
+:101AE000C600FE018491E7ECF0E008C09091C000D0\r
+:101AF00095FFFCCF8093C600319684918111F6CF7B\r
+:101B000040915F0D5091600D6091610D7091620D7B\r
+:101B100081EC95E00E948B0B4091470D5091480D50\r
+:101B20006091490D70914A0D8AEC95E00E948B0BF3\r
+:101B30004091930D5091940D6091950D7091960D7B\r
+:101B40008DEC95E00E94AF0B4091530D5091540DD8\r
+:101B50006091550D7091560D80ED95E00E948B0BB4\r
+:101B600040914F0D5091500D6091510D7091520D5B\r
+:101B700083ED95E00E948B0B40914B0D50914C0DE5\r
+:101B800060914D0D70914E0D86ED95E00E948B0B8E\r
+:101B90008091C00085FFFCCF8AE08093C600FE01E3\r
+:101BA0008491E7ECF0E008C09091C00095FFFCCF75\r
+:101BB0008093C600319684918111F6CFE9EDF5E06E\r
+:101BC00007C09091C00095FFFCCF8093C60031966E\r
+:101BD00084918111F6CF8091C00085FFFCCF8AE00F\r
+:101BE0008093C600FE018491E7ECF0E008C090917C\r
+:101BF000C00095FFFCCF8093C6003196849181117F\r
+:101C0000F6CF4091E9025091EA026091EB027091A7\r
+:101C1000EC028BEE95E00E948B0B4091ED0250910F\r
+:101C2000EE026091EF027091F00284EF95E00E9465\r
+:101C30008B0B4091F1025091F2026091F30270918E\r
+:101C4000F40287EF95E00E948B0B8091C00085FF26\r
+:101C5000FCCF8AE08093C600FE018491E7ECF0E0BF\r
+:101C600008C09091C00095FFFCCF8093C6003196CC\r
+:101C700084918111F6CFEAEFF5E007C09091C000A2\r
+:101C800095FFFCCF8093C600319684918111F6CFE9\r
+:101C90008091C00085FFFCCF8AE08093C600FE01E2\r
+:101CA000C491E7ECF0E008C08091C00085FFFCCF54\r
+:101CB000C093C6003196C491C111F6CF4091520134\r
+:101CC00050915301609154017091550188E096E064\r
+:101CD0000E948B0B2DEB37E346E05EE360914E01F3\r
+:101CE00070914F0180915001909151010E94896E35\r
+:101CF000AB01BC0182E196E00E949D0B2DEB37E326\r
+:101D000046E05EE360914A0170914B0180914C0185\r
+:101D100090914D010E945771AB01BC0185E196E0A5\r
+:101D20000E949D0B8091C00085FFFCCF8AE08093CC\r
+:101D3000C600DF91CF910895CF93DF9300D0CDB748\r
+:101D4000DEB780913F0E9091400E9E012F5F3F4F76\r
+:101D5000009719F425543E4002C0281B390B3A83E2\r
+:101D6000298389819A810F900F90DF91CF910895F7\r
+:101D70002091E6033091E703243031050CF06AC06E\r
+:101D80004091E8035091E90360E6649F9001659FEC\r
+:101D9000300D1124BC01C90186519C4F0E947A75F7\r
+:101DA000E7ECF0E007C09091C00095FFFCCF809376\r
+:101DB000C600319684918111F6CFEDECF0E007C0BA\r
+:101DC0009091C00095FFFCCF8093C600319684911E\r
+:101DD0008111F6CF8091E8039091E90320E6289FD6\r
+:101DE000F001299FF00D1124E651FC4F06C090919F\r
+:101DF000C00095FFFCCF8093C60081918111F7CF81\r
+:101E0000E8EDF0E007C09091C00095FFFCCF809313\r
+:101E1000C600319684918111F6CF8091C00085FF74\r
+:101E2000FCCF8AE08093C6008091E8039091E9039B\r
+:101E3000019664E070E00E9440729093E903809301\r
+:101E4000E8038091E6039091E70301969093E703FE\r
+:101E50008093E6030895809174059091750560E084\r
+:101E600070E001960C94B87280917405909175059C\r
+:101E70004AE050E060E070E001960C940F744091ED\r
+:101E80006E0550916F0590E6949F9001959F300DDF\r
+:101E90001124682F70E0C90186519C4F0E946F7514\r
+:101EA000909375058093740521E0892B09F420E057\r
+:101EB000822F08950E94FD2A6093860570938705FE\r
+:101EC0008093880590938905E0916E05F0916F05E8\r
+:101ED000E659FA4F8081811114C0E0E2F6E007C0B4\r
+:101EE0009091C00095FFFCCF8093C60031968491FD\r
+:101EF0008111F6CF8091C00085FFFCCF8AE08093EE\r
+:101F0000C600089581EC95E00E94FE2CE8E1F6E021\r
+:101F100007C09091C00095FFFCCF8093C60031961A\r
+:101F200084918111F6CF40917A0550917B056091A3\r
+:101F30007C0570917D054F5F5F4F6F4F7F4F2AE0AB\r
+:101F400030E081EC95E00E94AF2D8091C00085FFCC\r
+:101F5000FCCF8AE08093C6000C945A0F2F923F92D8\r
+:101F60004F925F926F927F928F929F92AF92BF92A9\r
+:101F7000CF92DF92EF92FF920F931F93CF93DF9355\r
+:101F8000CDB7DEB76F970FB6F894DEBF0FBECDBFEB\r
+:101F9000A0E67A2EB4E04B2E512CE8E6F2E08490D5\r
+:101FA0001AE0B12EE5E6F2E0A49099249394809192\r
+:101FB0004206909143062091440630914506821BCB\r
+:101FC000930B8F779927892B09F004C234C38D3086\r
+:101FD00051F08A3321F490917105992321F02F3526\r
+:101FE00031050CF4DAC12115310509F49FC2809145\r
+:101FF000E8039091E903789E7001799EF00C11241A\r
+:10200000F701E20FF31FE651FC4F1082209171059A\r
+:102010002111BEC110927105FC01E659FA4F1082E0\r
+:10202000870106511C4F6EE470E0C8010E946F7575\r
+:10203000009709F404C19093750580937405801B83\r
+:10204000910B8E0D9F1D4AE050E060E070E08551DD\r
+:102050009C4F0E940F746093760570937705809370\r
+:10206000780590937905C0907A05D0907B05E09033\r
+:102070007C05F0907D05970186010F5F1F4F2F4F64\r
+:102080003F4F6017710782079307C1F12091E80362\r
+:102090003091E903729EC001739E900D11246BE88C\r
+:1020A00071E086519C4F0E949E75892B39F5ECEBAF\r
+:1020B000F1E007C09091C00095FFFCCF8093C6006F\r
+:1020C000319684918111F6CFE3ECF1E007C0909155\r
+:1020D000C00095FFFCCF8093C6003196849181119A\r
+:1020E000F6CF2AE030E0B701A60181EC95E00E942E\r
+:1020F000AF2D8091C00085FFFCCF8AC08091E8039E\r
+:102100009091E903789E1001799E300C1124810191\r
+:1021100006511C4F6AE270E0C8010E946F7500977B\r
+:1021200021F450C0F3262F5F02C020E0F12CF10112\r
+:10213000E20FF11DE651FC4F30813A3299F790934E\r
+:10214000750580937405801B910B820D931D60E0D3\r
+:1021500070E085519C4F0E94B8720E94F16E2F2D45\r
+:1021600030E02617370709F459C0ECEBF1E007C05F\r
+:102170009091C00095FFFCCF8093C600319684916A\r
+:102180008111F6CFE5EFF1E007C09091C00095FF17\r
+:10219000FCCF8093C600319684918111F6CF409197\r
+:1021A0007A0550917B0560917C0570917D052AE050\r
+:1021B00030E081EC95E00E94AF2D8091C00085FF5A\r
+:1021C000FCCF26C0ECEBF1E007C09091C00095FF7A\r
+:1021D000FCCF8093C600319684918111F6CFE3E164\r
+:1021E000F2E007C09091C00095FFFCCF8093C6003D\r
+:1021F000319684918111F6CF2AE030E0B701A60133\r
+:1022000081EC95E00E94AF2D8091C00085FFFCCF4E\r
+:102210008AE08093C6000E94820F47C080917605B5\r
+:1022200090917705A0917805B091790580937A0512\r
+:1022300090937B05A0937C05B0937D053BC06AE23B\r
+:1022400070E0C8010E946F75892BA1F1ECEBF1E001\r
+:1022500007C09091C00095FFFCCF8093C6003196D7\r
+:1022600084918111F6CFECE3F2E007C09091C000B9\r
+:1022700095FFFCCF8093C600319684918111F6CFF3\r
+:1022800040917A0550917B0560917C0570917D05A8\r
+:102290002AE030E081EC95E00E94AF2D8091C000F3\r
+:1022A00085FFFCCF8AE08093C600109273051092E0\r
+:1022B0007205EFC18091E8039091E903789E800157\r
+:1022C000799E100D1124FAEEEF2EF3E0FF2EE00EB2\r
+:1022D000F11E67E470E0C7010E946F75009709F472\r
+:1022E00042C090937505809374058E199F09800FE5\r
+:1022F000911F60E070E085519C4F0E94B8720E946F\r
+:10230000F16E6430710578F58091E602811117C095\r
+:1023100080910C03811127C08A2DE5E6F2E008C008\r
+:102320009091C00095FFFCCF8093C60031968491B8\r
+:102330008111F6CF8091C00085FFFCCF12C0882D9F\r
+:10234000E8E6F2E008C09091C00095FFFCCF8093D2\r
+:10235000C600319684918111F6CF8091C00085FF2F\r
+:10236000FCCFB092C6008091E8039091E9030196FA\r
+:10237000B2010E9440729093E9038093E803809138\r
+:10238000E6039091E70301969093E7038093E603B9\r
+:1023900010927305109272050ACE8B3311F490924D\r
+:1023A000710590917105911102CE4091E803509111\r
+:1023B000E903749ED001759EB00D1124A20FB31FC6\r
+:1023C000A651BC4F8C932F5F3F4F309373052093E2\r
+:1023D0007205EDCD8091E6039091E70304970CF42C\r
+:1023E0002FC129C18091720590917305892B09F441\r
+:1023F000E8C04FC14D3081F04A3321F4209171057E\r
+:10240000222351F020917205309173052F3531054B\r
+:102410001CF4019609F0B8C04091DC035091DD0333\r
+:102420006091DE037091DF038091D4039091D50316\r
+:10243000A091D603B091D703481759076A077B07C5\r
+:1024400008F46DC08F8DE0EEF2E008C09091C000FE\r
+:1024500095FFFCCF8093C600319684918111F6CF11\r
+:102460008091C00085FFFCCF7092C6000E94FD2ABB\r
+:1024700060937E0570937F0580938005909381051E\r
+:102480002091820530918305409184055091850506\r
+:10249000621B730B840B950BA70196010E9454726B\r
+:1024A000CA01B901A50194010E9454727F936F93F0\r
+:1024B0003F932F9380E991E09F938F933F922F92C8\r
+:1024C0000E94B875862D0FB6F894DEBF0FBECDBF43\r
+:1024D000E7ECF0E008C09091C00095FFFCCF80933E\r
+:1024E000C600319684918111F6CFF10106C090911A\r
+:1024F000C00095FFFCCF8093C60081918111F7CF7A\r
+:102500008091C00085FFFCCF7092C6008CE093E004\r
+:102510000E94D56A61E08CE093E00E94CC69209132\r
+:102520007205309173052115310519F4109271056A\r
+:10253000B0C08091E8039091E903189FF001199FC2\r
+:10254000F00D1124E20FF31FE651FC4F1082FC0145\r
+:10255000E659FA4F00832091E6033091E7032F5F9D\r
+:102560003F4F3093E7032093E6030196B2010E94A8\r
+:1025700040729093E9038093E803109271051092E2\r
+:102580007305109272053BC04B3311F40093710533\r
+:1025900080917105811133C08091E8039091E90326\r
+:1025A000189FF001199FF00D1124E20FF31FE6515F\r
+:1025B000FC4F40832F5F3F4F30937305209372058C\r
+:1025C0001EC010E601E044E0442E512CE0EEF2E0A3\r
+:1025D00034913F8F5AE0752E68EEC62E63E0D62EFA\r
+:1025E000E12CF12C7CE3872E912CA12CB12C222400\r
+:1025F0002394312C2C0E3D1EE7ECF0E064904091CA\r
+:10260000DC035091DD036091DE037091DF03809164\r
+:10261000D4039091D503A091D603B091D703481766\r
+:1026200059076A077B07A8F58091E6039091E703B5\r
+:102630000497A4F02EC080910D038111D3CE29C040\r
+:1026400081EC95E00E94E22C809370052091720548\r
+:10265000309173058A3009F0BACCC5CC8091C103A2\r
+:102660009091C203A091C303B091C4038093DC0393\r
+:102670009093DD03A093DE03B093DF0389EB93E037\r
+:102680000E944631482F809370058A3009F0B2CEFF\r
+:10269000C3CE6F960FB6F894DEBF0FBECDBFDF91ED\r
+:1026A000CF911F910F91FF90EF90DF90CF90BF904F\r
+:1026B000AF909F908F907F906F905F904F903F90E2\r
+:1026C0002F9008958F929F92AF92BF92CF92DF92F8\r
+:1026D000EF92FF920F931F93CF93DF9300E4A02E0E\r
+:1026E00002E0B02E05EF12E0CEE8D5E088E0882EBB\r
+:1026F00083E0982EF50181915F010E943F0F8823AE\r
+:1027000019F10E942B0F6B017C01F4018081811172\r
+:1027100003C060919E0501C061E070E080E090E040\r
+:102720000E94246FF80120813181428153810E94EF\r
+:1027300057719B01AC01C701B6010E94A96D688366\r
+:1027400079838A839B8309C0F80180819181A2816A\r
+:10275000B38188839983AA83BB830C5F1F4F249620\r
+:10276000FFEF8F1A9F0A84E4A81682E0B80611F6DC\r
+:1027700086E40E943F0F8823D9F00E942B0F6B0143\r
+:102780007C016093A8057093A9058093AA05909396\r
+:10279000AB0520E030E0A9010E948570181644F4D2\r
+:1027A000C0922C01D0922D01E0922E01F0922F01C7\r
+:1027B000DF91CF911F910F91FF90EF90DF90CF901D\r
+:1027C000BF90AF909F908F9008950E94621389E40C\r
+:1027D0000E943F0F882359F00E942B0F6093AC0595\r
+:1027E0007093AD058093AE059093AF0508C010922D\r
+:1027F000AC051092AD051092AE051092AF058AE4BB\r
+:102800000E943F0F882359F00E942B0F6093B00560\r
+:102810007093B1058093B2059093B305089510921B\r
+:10282000B0051092B1051092B2051092B30508954B\r
+:10283000CF92DF92EF92FF92CF93DF93EC01C090A3\r
+:102840000C01D0900D01E0900E01F0900F01A70156\r
+:102850009601688179818A819B810E94826E87FFBF\r
+:1028600004C0C882D982EA82FB82C0901001D09055\r
+:102870001101E0901201F0901301A70196016C8103\r
+:102880007D818E819F810E94826E87FF04C0CC82F1\r
+:10289000DD82EE82FF82C0901401D0901501E0909D\r
+:1028A0001601F0901701A7019601688579858A8540\r
+:1028B0009B850E94826E87FF04C0C886D986EA86FF\r
+:1028C000FB86C0900001D0900101E0900201F090E1\r
+:1028D0000301A7019601688179818A819B810E9409\r
+:1028E0008570181624F4C882D982EA82FB82C090CF\r
+:1028F0000401D0900501E0900601F0900701A701C6\r
+:1029000096016C817D818E819F810E948570181651\r
+:1029100024F4CC82DD82EE82FF82C0900801D09048\r
+:102920000901E0900A01F0900B01A701960168856A\r
+:1029300079858A859B850E948570181624F4C8863F\r
+:10294000D986EA86FB86DF91CF91FF90EF90DF90EA\r
+:10295000CF900895CF92DF92EF92FF920F931F9343\r
+:102960008EE895E00E9418140E94FD2A6093860567\r
+:1029700070938705809388059093890560911A016B\r
+:1029800070911B01882777FD8095982F0E94246FF6\r
+:1029900020912C0130912D0140912E0150912F0159\r
+:1029A0000E94577120E030E040E752E40E94896EB7\r
+:1029B00020E030E048EC52E40E94896E28EEC22EFE\r
+:1029C00022E0D22E7B018C012AE935E046E955E070\r
+:1029D00062E975E08EE895E00E94684580918E0579\r
+:1029E00090918F05A0919005B09191058093F5028B\r
+:1029F0009093F602A093F702B093F80280919205AB\r
+:102A000090919305A0919405B09195058093F9025A\r
+:102A10009093FA02A093FB02B093FC02809196057A\r
+:102A200090919705A0919805B09199058093FD022A\r
+:102A30009093FE02A093FF02B093000380919A0549\r
+:102A400090919B05A0919C05B0919D0580930103F9\r
+:102A500090930203A0930303B09304031F910F917B\r
+:102A6000FF90EF90DF90CF900895AF92BF92CF92FA\r
+:102A7000DF92EF92FF920F931F93CF93DF93182F64\r
+:102A80002091B0053091B1054091B2055091B30548\r
+:102A90006091AC057091AD058091AE059091AF0548\r
+:102AA0000E949270EB01082FF92E60911A0170912B\r
+:102AB0001B01882777FD8095982F0E94246F209115\r
+:102AC0002C0130912D0140912E0150912F010E9437\r
+:102AD000577120E030E040E752E40E94896E20E028\r
+:102AE00030E048EC52E40E94896E2091E8022F9376\r
+:102AF0001F93FF920F93DF93CF935B016C0132E042\r
+:102B0000E32E01E020E04CEA55E06EE875E085EF49\r
+:102B100092E00E94263C80918E0590918F05A091B5\r
+:102B20009005B09191058093F5029093F602A093E1\r
+:102B3000F702B093F8028091920590919305A091CD\r
+:102B40009405B09195058093F9029093FA02A093B1\r
+:102B5000FB02B093FC028091960590919705A0919D\r
+:102B60009805B09199058093FD029093FE02A09381\r
+:102B7000FF02B093000380919A0590919B05A0916C\r
+:102B80009C05B0919D058093010390930203A0934F\r
+:102B90000303B09304030E94FD2A6093860570939B\r
+:102BA000870580938805909389050F900F900F906B\r
+:102BB0000F900F900F90DF91CF911F910F91FF9089\r
+:102BC000EF90DF90CF90BF90AF900895F8940E945F\r
+:102BD000DF5C5E9A5E9A159A5E9AECEBF1E007C0B4\r
+:102BE0009091C00095FFFCCF8093C60031968491F0\r
+:102BF0008111F6CFE3E2F6E007C09091C00095FFA7\r
+:102C0000FCCF8093C600319684918111F6CF8091DC\r
+:102C1000C00085FFFCCF8AE08093C600FFCFCF9233\r
+:102C2000DF92EF92FF920F931F930E94FD2A009173\r
+:102C3000A0051091A1052091A2053091A305C09097\r
+:102C40008605D0908705E0908805F09089056C197D\r
+:102C50007D098E099F09061717072807390730F4E1\r
+:102C6000012B022B032B11F00E94E61580913101FC\r
+:102C700090913201A0913301B0913401892B8A2BBC\r
+:102C80008B2B09F10E94FD2A009186051091870582\r
+:102C90002091880530918905601B710B820B930B85\r
+:102CA00000913101109132012091330130913401B2\r
+:102CB000061717072807390730F4909155088091B7\r
+:102CC0005408981741F01F910F91FF90EF90DF90FB\r
+:102CD000CF900C9444455E9A5E9A159A5E9AF3CF13\r
+:102CE0000E94DF5C8091E602811134C081E0809314\r
+:102CF000E60280917A0590917B05A0917C05B091C8\r
+:102D00007D058093A4059093A505A093A605B09397\r
+:102D1000A705ECEBF1E007C09091C00095FFFCCF58\r
+:102D20008093C600319684918111F6CFE4E4F6E0F9\r
+:102D300007C09091C00095FFFCCF8093C6003196EC\r
+:102D400084918111F6CF8091C00085FFFCCF8AE08D\r
+:102D50008093C60008958091E6020895EF92FF9255\r
+:102D60000F931F93CF93DF938C017B0184E0E80ED8\r
+:102D7000F11CEB016991F801808191819C012F5F29\r
+:102D80003F4F318320830E94C177CE15DF0591F735\r
+:102D900084E090E0DF91CF911F910F91FF90EF9031\r
+:102DA0000895EF92FF920F931F93CF93DF938C01BF\r
+:102DB0007B0180E1E80EF11CEB016991F801808153\r
+:102DC00091819C012F5F3F4F318320830E94C17707\r
+:102DD000CE15DF0591F780E190E0DF91CF911F9153\r
+:102DE0000F91FF90EF900895EF92FF920F931F9332\r
+:102DF000CF93DF938C017B0184E0E80EF11CEB01A3\r
+:102E00006991F801808191819C012F5F3F4F31834F\r
+:102E100020830E94C177CE15DF0591F784E090E012\r
+:102E2000DF91CF911F910F91FF90EF9008950F9335\r
+:102E30001F93CF93DF93CDB7DEB72A970FB6F894E1\r
+:102E4000DEBF0FBECDBF80E390E3A0E3B0E08D8393\r
+:102E50009E83AF83B88784E690E09A878987BE0116\r
+:102E60006B5F7F4FCE0109960E94AE1663E77DE04F\r
+:102E7000CE0109960E94D11663E87DE0CE01099645\r
+:102E80000E94D11603E61DE0F80161918F0189854A\r
+:102E90009A859C012F5F3F4F3A8729870E94C1770F\r
+:102EA000FDE003371F0781F76BE57DE0CE01099652\r
+:102EB0000E94F41667E57DE0CE0109960E94F416A3\r
+:102EC0006FE57DE0CE0109960E94F41667E47DE08F\r
+:102ED000CE0109960E94F41603E91DE0F801619104\r
+:102EE0008F0189859A859C012F5F3F4F3A872987FB\r
+:102EF0000E94C177FDE007391F0781F763E57DE098\r
+:102F0000CE0109960E94F4166FE47DE0CE01099689\r
+:102F10000E94F4166BE47DE0CE0109960E94F4163F\r
+:102F200009EE12E0F80161918F0189859A859C0173\r
+:102F30002F5F3F4F3A8729870E94C177F2E0053F14\r
+:102F40001F0781F762E571E0CE0109960E94F41631\r
+:102F50006EE471E0CE0109960E94F4166AE471E015\r
+:102F6000CE0109960E94F41686E590E3A6E3B0E050\r
+:102F700089839A83AB83BC8384E690E09A878987B0\r
+:102F8000BE016F5F7F4FCE0109960E94AE16E7EC3F\r
+:102F9000F0E007C09091C00095FFFCCF8093C60081\r
+:102FA000319684918111F6CFEAE5F4E007C0909163\r
+:102FB000C00095FFFCCF8093C600319684918111AB\r
+:102FC000F6CF8091C00085FFFCCF8AE08093C600D9\r
+:102FD0002A960FB6F894DEBF0FBECDBFDF91CF911A\r
+:102FE0001F910F910895EF92FF920F931F93CF932C\r
+:102FF000DF938C017B0180E1E80EF11CEB01F8010D\r
+:10300000808191819C012F5F3F4F318320830E94FB\r
+:10301000B9778993CE15DF0591F780E190E0DF91D4\r
+:10302000CF911F910F91FF90EF900895EF92FF9233\r
+:103030000F931F93CF93DF938C017B0184E0E80E05\r
+:10304000F11CEB01F801808191819C012F5F3F4FC2\r
+:10305000318320830E94B9778993CE15DF0591F7DC\r
+:1030600084E090E0DF91CF911F910F91FF90EF905E\r
+:103070000895AF92BF92CF92DF92EF92FF920F939B\r
+:103080001F93CF93DF93CDB7DEB7EC970FB6F894CD\r
+:10309000DEBF0FBECDBFD82E24E630E03AAF29AF59\r
+:1030A00046E550E366E370E049AB5AAB6BAB7CABF3\r
+:1030B0008E010B5C1F4FB8017E0189E3E80EF11C05\r
+:1030C00089AD9AAD9C012F5F3F4F3AAF29AF6BAFEF\r
+:1030D0007CAF0E94B977F80181938F016BAD7CAD15\r
+:1030E000EE15FF0569F7D110A4C043E050E0CE0112\r
+:1030F000C1960E948175009709F09BC063E77DE04F\r
+:10310000C8010E94F31763E87DE0C8010E94F3172D\r
+:1031100003E61DE089AD9AAD9C012F5F3F4F3AAFAA\r
+:1031200029AF0E94B977F80181938F01FDE0033741\r
+:103130001F0781F76BE57DE0CE01C9960E94161846\r
+:1031400067E57DE0CE01C9960E9416186FE57DE027\r
+:10315000CE01C9960E94161867E47DE0CE01C9969B\r
+:103160000E94161803E91DE089AD9AAD9C012F5FFE\r
+:103170003F4F3AAF29AF0E94B977F80181938F0191\r
+:10318000FDE007391F0781F763E57DE0CE01C996B1\r
+:103190000E9416186FE47DE0CE01C9960E941618B1\r
+:1031A0006BE47DE0CE01C9960E94161809EE12E08C\r
+:1031B00089AD9AAD9C012F5F3F4F3AAF29AF0E9476\r
+:1031C000B977F80181938F01F2E0053F1F0781F77E\r
+:1031D00062E571E0CE01C9960E9416186EE471E0B6\r
+:1031E000CE01C9960E9416186AE471E0CE01C99614\r
+:1031F0000E941618E7ECF0E007C09091C00095FF20\r
+:10320000FCCF8093C600319684918111F6CFECE813\r
+:10321000F1E007C09091C00095FFFCCF8093C600FD\r
+:10322000319684918111F6CF8091C00085FFFCCF4B\r
+:10323000DDC080E1EBE5F1E0DE01919601900D92B9\r
+:103240008A95E1F780E1EBE6F1E0DE01519601902D\r
+:103250000D928A95E1F780E1EBE7F1E0DE0111964E\r
+:1032600001900D928A95E1F76E0181E2C80ED11CA2\r
+:1032700043E7E42E4DE0F42E8E010F5E1F4F63E80E\r
+:103280007DE0AE014F5F5F4FE3E6AE2EEDE0BE2E78\r
+:1032900024E030E0F60181919191A191B1916F010B\r
+:1032A000F70181939193A193B1937F01F8018191EB\r
+:1032B0009191A191B1918F01FB0181939193A19380\r
+:1032C000B193BF01FA0181919191A191B191AF01A7\r
+:1032D000F50181939193A193B1935F01215031093D\r
+:1032E000C9F680E090E0AAE7B4E480935B0D909388\r
+:1032F0005C0DA0935D0DB0935E0D8093570D909380\r
+:10330000580DA093590DB0935A0D10925F0D109265\r
+:10331000600D1092610D1092620D80E29EE4A0E0BB\r
+:10332000B0E08093930D9093940DA093950DB0937E\r
+:10333000960D1092470D1092480D1092490D109263\r
+:103340004A0D80E090E0A0E7B1E48093530D9093A4\r
+:10335000540DA093550DB093560D4DEC5CEC6CECF8\r
+:103360007EE340934F0D5093500D6093510D709339\r
+:10337000520D80934B0D90934C0DA0934D0DB09337\r
+:103380004E0D1092F1021092F2021092F30210927E\r
+:10339000F4021092ED021092EE021092EF021092DF\r
+:1033A000F0021092E9021092EA021092EB021092DF\r
+:1033B000EC02E7ECF0E007C09091C00095FFFCCF75\r
+:1033C0008093C600319684918111F6CFEFE9F1E048\r
+:1033D00006C09091C00095FFFCCF8093C6008191FC\r
+:1033E0008111F7CF8091C00085FFFCCF8AE08093E8\r
+:1033F000C600EC960FB6F894DEBF0FBECDBFDF91CE\r
+:10340000CF911F910F91FF90EF90DF90CF90BF90E1\r
+:10341000AF9008952F923F924F925F926F927F925A\r
+:103420008F929F92AF92BF92CF92DF92EF92FF92D4\r
+:103430000F931F93CF93DF93CDB7DEB7A6970FB649\r
+:10344000F894DEBF0FBECDBF87E40E943F0F8823F4\r
+:1034500009F40EC20E942B0F0E94F16E64307105B8\r
+:10346000D9F144F46230710521F15CF577FF17C0A2\r
+:103470000C94B7276A35710509F4A6C134F46C3190\r
+:10348000710511F00C94B7276AC06B35710509F40A\r
+:103490009FC16C35710511F00C94B7279EC18091C6\r
+:1034A000E602811106C00E9462130E94AA140C94C5\r
+:1034B00047288091E602811104C00E94E51381E053\r
+:1034C00007C08091E602811107C00E94E51380E0E9\r
+:1034D0000E9435150C94472880E50E943F0F8823F1\r
+:1034E00039F00E942B0F0E94F66E6B017C0103C025\r
+:1034F000C12CD12C760183E50E943F0F882361F017\r
+:103500000E942B0F20E030E04AE754E40E945771FC\r
+:103510000E94F66E6B017C010E94AC570E94FD2A4E\r
+:103520004B015C018C0C9D1CAE1CBF1C0E94FD2A33\r
+:10353000609386057093870580938805909389052D\r
+:1035400005C00E94FD5981E00E940F160E94FD2ACD\r
+:10355000681579058A059B05A0F30C94B72780911F\r
+:103560002C0190912D01A0912E01B0912F018093FB\r
+:103570008A0590938B05A0938C05B0938D0580915F\r
+:103580001A0190911B01909307038093060384E630\r
+:1035900090E090931B0180931A010E94FD2A609392\r
+:1035A000860570938705809388059093890581E04F\r
+:1035B0000E9446518091F5029091F602A091F70287\r
+:1035C000B091F80280938E0590938F05A09390059B\r
+:1035D000B09391058091F9029091FA02A091FB02BB\r
+:1035E000B091FC028093920590939305A09394056B\r
+:1035F000B09395058091FD029091FE02A091FF028B\r
+:10360000B09100038093960590939705A093980539\r
+:10361000B09399058091010390910203A091030357\r
+:10362000B091040380939A0590939B05A0939C0509\r
+:10363000B0939D0510922C0110922D0110922E0135\r
+:1036400010922F0188E50E943F0F882311F090E02F\r
+:103650000AC089E50E943F0F8111F9CF8AE50E94D7\r
+:103660003F0F91E098279093300191110C94BA2765\r
+:1036700088E50E943F0F81110C94BA278091300198\r
+:1036800081110C94C02789E50E943F0F81110C9491\r
+:10369000C0278091300181110C94C6278AE50E94D1\r
+:1036A0003F0F81110C94C62788E50E943F0F8823A5\r
+:1036B000D1F00E94340F672B682B692BA1F00E9478\r
+:1036C0002B0F2091E9023091EA024091EB025091D8\r
+:1036D000EC020E94A96D6093F5027093F60280934C\r
+:1036E000F7029093F80289E50E943F0F8823D1F0FA\r
+:1036F0000E94340F672B682B692BA1F00E942B0FBF\r
+:103700002091ED023091EE024091EF025091F002D3\r
+:103710000E94A96D6093F9027093FA028093FB02F4\r
+:103720009093FC028AE50E943F0F8823D1F00E940B\r
+:10373000340F672B682B692BA1F00E942B0F20916F\r
+:10374000F1023091F2024091F3025091F4020E9492\r
+:10375000A96D6093FD027093FE028093FF02909327\r
+:10376000000321E033E04DEF52E069EF72E085EFB6\r
+:1037700092E00E94F74F80E00E94465180918A05B6\r
+:1037800090918B05A0918C05B0918D0580932C01B3\r
+:1037900090932D01A0932E01B0932F0180910603E9\r
+:1037A0009091070390931B0180931A010E94FD2AB8\r
+:1037B00060938605709387058093880590938905AB\r
+:1037C0000E943F510C94B72710929E050C94B72786\r
+:1037D00081E080939E050C94B72785E40E943F0FFB\r
+:1037E000811102C00E94AC5750E4C52E52E0D52E84\r
+:1037F00069EEE62E62E0F62E05EF12E0B12CF6013E\r
+:1038000081916F010E943F0F882339F1F3E0BF12CD\r
+:103810000CC00E942B0FF801608371838283938315\r
+:1038200081E093E00E94935018C00E942B0FF70193\r
+:1038300020813181428153810E94A96DF80160830A\r
+:1038400071838283938321E033E04DEF52E069EF8F\r
+:1038500072E085EF92E00E94F74FB394F4E0EF0E30\r
+:10386000F11C0C5F1F4F24E0B212C9CF0C94B72794\r
+:103870008DE40E943F0F882311F40C940A270E94C4\r
+:103880002B0F0E94F16E6237710509F45FC70CF0CF\r
+:103890006FC06C31710509F46DC19CF5663171051D\r
+:1038A00009F425C1ACF46231710509F45EC634F443\r
+:1038B0006131710511F00C94B727E2C064317105D4\r
+:1038C00009F4E4C06531710511F00C94B72709C102\r
+:1038D0006931710509F434C154F46731710509F493\r
+:1038E0000CC16831710511F00C94B72719C16A3108\r
+:1038F000710509F42BC16B31710511F00C94B727D8\r
+:1039000033C16435710509F430C6CCF46A327105EF\r
+:1039100009F43BC254F46E31710509F460C16F3192\r
+:10392000710511F00C94B72798C16235710509F43F\r
+:1039300015C66335710511F00C94B72712C66836A9\r
+:10394000710509F45FC254F46535710509F448C680\r
+:103950006C35710511F00C94B72758C6693671059E\r
+:1039600009F4C6C26D36710511F00C94B72792C3E5\r
+:103970006E3C710511F40C94BB25D4F56C387105BF\r
+:1039800009F4A2C2C4F46737710511F40C948824B9\r
+:1039900034F46337710511F00C94B727C9C6683742\r
+:1039A000710511F40C9482246937710511F00C949F\r
+:1039B000B7270C9484246B3C710511F40C942125D9\r
+:1039C0005CF46E3B710509F42DC5693C710511F07D\r
+:1039D0000C94B7270C94E7246C3C710511F40C94FB\r
+:1039E0003D256D3C710511F00C94B7270C945D25B5\r
+:1039F000603931E0730711F40C94E62604F56D325A\r
+:103A000091E0790711F40C94F82564F46C3D71058C\r
+:103A100011F40C94D7256D3D710511F00C94B72766\r
+:103A20000C94E9256E32F1E07F0711F40C94D32653\r
+:103A30006F32714011F00C94B7270C94D726663F73\r
+:103A400031E0730711F40C94EE266CF4643F91E0BE\r
+:103A5000790711F40C94E926653F714011F00C943C\r
+:103A6000B7270C94EC26673FF1E07F0711F40C9424\r
+:103A7000F226673E734011F00C94B7270C94F5269C\r
+:103A80005E985E9815985E980C94B727E3EFF2E085\r
+:103A900007C09091C00095FFFCCF8093C60031967F\r
+:103AA00084918111F6CF8091C00085FFFCCF8AE020\r
+:103AB0008093C6008CE093E00E94C664E3E0F3E0EC\r
+:103AC00007C09091C00095FFFCCF8093C60031964F\r
+:103AD00084918111F6CF8091C00085FFFCCF0C94BA\r
+:103AE000B4278CE093E00E9408650C94B7278CE023\r
+:103AF00093E00E94CE650C94B72700917405109155\r
+:103B000075050C5F1F4F6AE270E0C8010E946F7577\r
+:103B1000009719F0FC013197108241E0B80159C0BB\r
+:103B20008CE093E00E94D2650E94FD2A609382059A\r
+:103B30007093830580938405909385050C94B72733\r
+:103B40008CE093E00E94D9650C94B72780910E0316\r
+:103B5000882311F40C94B72783E50E943F0F81114D\r
+:103B60000C94CC270C94B7278CE093E00E94096951\r
+:103B70000C94B72780917405909175056AE270E006\r
+:103B800004960E946F758C010097E1F020916E05FC\r
+:103B900030916F0540E6429FC001439F900D112474\r
+:103BA0006EE470E086519C4F0E946F7560E270E099\r
+:103BB0000E946F7501969093750580937405F801C6\r
+:103BC0003197108260917405709175056C5F7F4F1D\r
+:103BD00040E08CE093E00E94DE650C94B727809172\r
+:103BE0000E03882311F40C94B7278CE093E00E9415\r
+:103BF000C06A80917405909175056AE270E0049640\r
+:103C00000E946F758C010097E1F020916E05309154\r
+:103C10006F0540E6429FC001439F900D11246EE462\r
+:103C200070E086519C4F0E946F7560E270E00E94C8\r
+:103C30006F7501969093750580937405F80131971F\r
+:103C4000108260917405709175056C5F7F4F8CE0F8\r
+:103C500093E00E94CD670C94B7270E94FD2A6093E1\r
+:103C60007E0570937F058093800590938105009178\r
+:103C70008205109183052091840530918505601B94\r
+:103C8000710B820B930B28EE33E040E050E00E9472\r
+:103C90005472CA01B9012CE330E040E050E00E94C8\r
+:103CA00054727F936F933F932F9380E991E09F939A\r
+:103CB0008F93CE0101969F938F930E94B875E7EC86\r
+:103CC000F0E084910FB6F894DEBF0FBECDBFE7ECF5\r
+:103CD000F0E008C09091C00095FFFCCF8093C60033\r
+:103CE000319684918111F6CFFE01319606C09091F4\r
+:103CF000C00095FFFCCF8093C60081918111F7CF62\r
+:103D00008091C00085FFFCCF8AE08093C60080913F\r
+:103D1000260D882311F40C94B7271092260D60917C\r
+:103D2000E802E62FF0E0EE0FFF1FE050F24F808137\r
+:103D300091810E9431592091390130913A0140918D\r
+:103D40003B0150913C010E948570181614F00C94B0\r
+:103D5000B7276091E802062F10E080E090E00E9413\r
+:103D60006158F801EE0FFF1FE050F24F91838083FE\r
+:103D7000F801EE0FFF1FEE0FFF1FEA50F24F108207\r
+:103D80001182128213820C94B72783E50E943F0FA1\r
+:103D9000882311F40C94B7270E942B0F0E94F16E18\r
+:103DA0007B0180E50E943F0F882311F40C94B72714\r
+:103DB000F7FE02C00C94B727FFEFEF16F10409F0ED\r
+:103DC00014F40C94DD270C94B727819191918017FE\r
+:103DD000910711F40C94B72722E0E83BF207A9F70A\r
+:103DE00017FF02C00C94B72761E0802F0E94252C9A\r
+:103DF0006E2D802F0E94412CB701802F0E949A2B9C\r
+:103E00000C94B7278091E80280939F0584E50E9477\r
+:103E10003F0F882379F10E942B0F0E94F66E60936A\r
+:103E20009F05662339F1E7ECF0E007C09091C000F0\r
+:103E300095FFFCCF8093C600319684918111F6CF17\r
+:103E4000EDEBF1E006C09091C00095FFFCCF8093B0\r
+:103E5000C60081918111F7CF40E050E060919F054D\r
+:103E600081EC95E00E94A82D8091C00085FFFCCFD9\r
+:103E70000C94B42783E50E943F0F882309F1B0908A\r
+:103E80009F050E942B0F6B017C010B2D10E00E94FF\r
+:103E9000F16EDC01CB016B2D0E946158F801EE0F31\r
+:103EA000FF1FE050F24F91838083F801EE0FFF1F58\r
+:103EB000EE0FFF1FEA50F24FC082D182E282F382FE\r
+:103EC0000E94DE5C0C94B72783E50E943F0F882395\r
+:103ED00011F40C94B7270E942B0F0E94F16ECB01B6\r
+:103EE0000E94E5589093FF0D8093FE0D0C94B72728\r
+:103EF0008091E80280939F0584E50E943F0F88230C\r
+:103F000079F10E942B0F0E94F66E60939F05662345\r
+:103F100039F1E7ECF0E007C09091C00095FFFCCFCD\r
+:103F20008093C600319684918111F6CFE4EDF1E0E3\r
+:103F300006C09091C00095FFFCCF8093C600819190\r
+:103F40008111F7CF40E050E060919F0581EC95E052\r
+:103F50000E94A82D8091C00085FFFCCF0C94B4274F\r
+:103F6000E1E1F3E007C09091C00095FFFCCF8093A2\r
+:103F7000C600319684918111F6CF60919F05E62F9E\r
+:103F8000F0E0EE0FFF1FE450F24F808191810E941C\r
+:103F90003159AB01BC0121E030E081EC95E00E9499\r
+:103FA000B92EE7E1F3E007C09091C00095FFFCCF88\r
+:103FB0008093C600319684918111F6CF60919F0560\r
+:103FC000E62FF0E0EE0FFF1FE050F24F808191816D\r
+:103FD0000E943159AB01BC0121E030E081EC95E059\r
+:103FE0000E94B92EEAE1F3E007C09091C00095FF6E\r
+:103FF000FCCF8093C600319684918111F6CF8091D9\r
+:10400000FA0D9091FB0D0E94EB5BAB01BC0121E02E\r
+:1040100030E081EC95E00E94B92EEEE1F3E007C0BC\r
+:104020009091C00095FFFCCF8093C600319684919B\r
+:104030008111F6CF8091FE0D9091FF0D0E94EB5BF8\r
+:10404000AB01BC0121E030E081EC95E00E94B92E8B\r
+:10405000E1E2F3E007C09091C00095FFFCCF8093B0\r
+:10406000C600319684918111F6CF80919F0590E032\r
+:104070000E945B584AE050E0BC0181EC95E00E9450\r
+:10408000C02D8091C00085FFFCCF8AE08093C600E0\r
+:104090000C9447288091E80280939F0584E50E9454\r
+:1040A0003F0F882371F10E942B0F0E94F66E6093E0\r
+:1040B0009F05662331F1E7ECF0E007C09091C00066\r
+:1040C00095FFFCCF8093C600319684918111F6CF85\r
+:1040D000EBEEF1E006C09091C00095FFFCCF80931D\r
+:1040E000C60081918111F7CF40E050E060919F05BB\r
+:1040F00081EC95E00E94A82D8091C00085FFFCCF47\r
+:1041000033C71092260D83E50E943F0F882309F1E3\r
+:10411000B0909F050E942B0F6B017C010B2D10E0CE\r
+:104120000E94F16EDC01CB016B2D0E946158F801F9\r
+:10413000EE0FFF1FE050F24F91838083F801EE0FE6\r
+:10414000FF1FEE0FFF1FEA50F24FC082D182E282C2\r
+:10415000F38283E50E943F0F882351F00E942B0FCA\r
+:104160006093390170933A0180933B0190933C0135\r
+:1041700082E40E943F0F882351F00E942B0F60932E\r
+:104180003D0170933E0180933F019093400186E48E\r
+:104190000E943F0F882369F00E942B0F6093350126\r
+:1041A00070933601809337019093380181E08093BA\r
+:1041B000260D0E94DE5C0E94FD2A4B015C0180916D\r
+:1041C0009F0590E0880F991FFC01E050F24F20817D\r
+:1041D000318138A32F8FFC01E450F24F808191810F\r
+:1041E0009EA38DA3CC24CA94DC2C7601E5E2F3E0F7\r
+:1041F0002490E8E2F3E03490ECE2F3E0049148EE3E\r
+:10420000442E43E0542E612C712C1AE0F9C6822D05\r
+:10421000E5E2F3E008C09091C00095FFFCCF8093E9\r
+:10422000C600319684918111F6CF60919F05E62FEB\r
+:10423000F0E0EE0FFF1FE450F24F808191810E9469\r
+:104240003159AB01BC0121E030E081EC95E00E94E6\r
+:10425000B92E832DE8E2F3E008C09091C00095FFED\r
+:10426000FCCF8093C600319684918111F6CF609186\r
+:104270009F054AE050E070E081EC95E00E94C02D7F\r
+:10428000802FECE2F3E008C09091C00095FFFCCFD6\r
+:104290008093C600319684918111F6CFF7FE03C05A\r
+:1042A000E2E0F2E027C00E94FD2A4B015C01C70159\r
+:1042B000B601605F784D8F4F9F4F681979098A0961\r
+:1042C0009B09A30192010E94547249015A012AE0FC\r
+:1042D00030E0B501A40181EC95E00E949C2D809115\r
+:1042E000C00085FFFCCF0DC09091C00095FFFCCFB2\r
+:1042F0008093C60081918111F7CF8091C00085FF26\r
+:10430000FCCF1093C6000E94FD2A4B015C010E9465\r
+:10431000FD5981E00E940F169FEFC916D906E906E4\r
+:10432000F906A9F560919F05EF8DF8A12DA13EA199\r
+:104330002E173F070CF07FC6E62FF0E0EE0FFF1FB1\r
+:10434000E450F24F808191810E94315969A37AA390\r
+:104350008BA39CA360919F05E62FF0E0EE0FFF1F5B\r
+:10436000E050F24F808191810E94315920E030E08D\r
+:1043700040E85FE30E94A86D9B01AC0169A17AA1AE\r
+:104380008BA19CA10E94857087FF20C639C0F7FCD5\r
+:1043900037C060919F05E62FF0E0EE0FFF1FE4505D\r
+:1043A000F24F808191810E94315969A37AA38BA336\r
+:1043B0009CA360919F05E62FF0E0EE0FFF1FE050F9\r
+:1043C000F24F808191810E9431599B01AC0169A11A\r
+:1043D0007AA18BA19CA10E94A86D0E94F16EAB01F5\r
+:1043E000BC0177FF07C070956095509541955F4F70\r
+:1043F0006F4F7F4F44305105610571050CF0E6C5E4\r
+:104400003FEFC316D306E306F30609F4F9C5F7FC3C\r
+:10441000E2C50E94FD2A6C197D096031774208F4DB\r
+:10442000EFC5D9C583E50E943F0F882359F00E944C\r
+:104430002B0F0E94F16ECB010E94E5589093FF0D67\r
+:104440008093FE0D0E94FD2A6B017C01E0E3F3E006\r
+:10445000A490E3E3F3E0B490E7E3F3E014910AE01F\r
+:1044600070C00E94FD2A6C197D098E099F09693E62\r
+:1044700073408105910508F45FC06091E802A62FA2\r
+:10448000B0E0AA0FBB1FA450B24F8D919C910E9427\r
+:104490003159AB01BC018A2DE0E3F3E008C09091F3\r
+:1044A000C00095FFFCCF8093C600319684918111A6\r
+:1044B000F6CF22E030E081EC95E00E94B92E8B2D02\r
+:1044C000E3E3F3E008C09091C00095FFFCCF809338\r
+:1044D000C600319684918111F6CF6091E8024AE0DE\r
+:1044E00050E070E081EC95E00E94C02D812FE7E361\r
+:1044F000F3E008C09091C00095FFFCCF8093C60008\r
+:10450000319684918111F6CF8091FA0D9091FB0D37\r
+:104510000E94EB5BAB01BC0121E030E081EC95E057\r
+:104520000E94B92E8091C00085FFFCCF0093C60089\r
+:104530000E94FD2A6B017C010E94FD5981E00E94CE\r
+:104540000F162091FE0D3091FF0D8091FA0D909184\r
+:10455000FB0D821793070CF484CF47C510920B0311\r
+:1045600006C581E080930B0302C583E50E943F0FDF\r
+:10457000882399F00E942B0F20E030E04AE754E4B2\r
+:104580000E9457710E94F66E609331017093320160\r
+:104590008093330190933401EAC488E50E943F0F71\r
+:1045A000811141C589E50E943F0F81113CC58AE513\r
+:1045B0000E943F0F811137C585E40E943F0F811192\r
+:1045C00032C52BC55E9A89E50E943F0F81115E9A24\r
+:1045D0008AE50E943F0F882309F4C9C4159AC7C40D\r
+:1045E00083E50E943F0F0E942B0F20E030E04AE756\r
+:1045F00054E40E9457710E94F66E6093A005709378\r
+:10460000A1058093A2059093A305B1C420E432E0F4\r
+:104610003EA32DA353E7252E5DE0352E83E89DE0D4\r
+:1046200098A38F8F67E3862E6DE0962E10E0EDA1A4\r
+:10463000FEA18191FEA3EDA30E943F0F882309F400\r
+:1046400066C0133009F05CC00E942B0F6B017C0127\r
+:1046500020E030E040EA51E40E94826E87FF4AC0C9\r
+:10466000A7019601F10160817181828193810E948D\r
+:10467000896E762EA72EB82E092F762F272F3A2D4A\r
+:104680004B2D502F60914B0D70914C0D80914D0D25\r
+:1046900090914E0D0E94577160934B0D70934C0D8D\r
+:1046A00080934D0D90934E0D272D3A2D4B2D502F6D\r
+:1046B000EF8DF8A160817181828193810E94577191\r
+:1046C000EF8DF8A16083718382839383F40160810D\r
+:1046D0007181828193810E94226F272D3A2D4B2D6B\r
+:1046E000502F0E9457710E94F66EF401608371830F\r
+:1046F00082839383F101C082D182E282F38207C078\r
+:104700000E942B0FF10160837183828393831F5F6B\r
+:10471000F4E02F0E311C2F8D38A12C5F3F4F38A3B2\r
+:104720002F8F34E0830E911C143009F080CF1FC40A\r
+:10473000EBE3F3E007C09091C00095FFFCCF8093BE\r
+:10474000C600319684918111F6CF11C4E6EDF3E0F5\r
+:1047500007C09091C00095FFFCCF8093C6003196B2\r
+:1047600084918111F6CF4091F5025091F60260914B\r
+:10477000F7027091F80222E030E081EC95E00E94AF\r
+:10478000B92EE9EDF3E007C09091C00095FFFCCF92\r
+:104790008093C600319684918111F6CF4091F90241\r
+:1047A0005091FA026091FB027091FC0222E030E02D\r
+:1047B00081EC95E00E94B92EECEDF3E007C09091FA\r
+:1047C000C00095FFFCCF8093C60031968491811183\r
+:1047D000F6CF4091FD025091FE026091FF02709170\r
+:1047E000000322E030E081EC95E00E94B92EEFED6D\r
+:1047F000F3E007C09091C00095FFFCCF8093C60006\r
+:10480000319684918111F6CF4091010350910203BA\r
+:10481000609103037091040322E030E081EC95E0A5\r
+:104820000E94B92EE2EEF3E007C09091C00095FF20\r
+:10483000FCCF8093C600319684918111F6CF0E94FF\r
+:1048400006580E94246F2091730D3091740D409191\r
+:10485000750D5091760D0E94896EAB01BC0122E06E\r
+:1048600030E081EC95E00E94B92EECEEF3E007C059\r
+:104870009091C00095FFFCCF8093C6003196849143\r
+:104880008111F6CF81E00E9406580E94246F20918A\r
+:10489000770D3091780D4091790D50917A0D0E94ED\r
+:1048A000896EAB01BC0122E030E081EC95E00E9412\r
+:1048B000B92EEFEEF3E007C09091C00095FFFCCF5A\r
+:1048C0008093C600319684918111F6CF82E00E94D8\r
+:1048D00006580E94246F20917B0D30917C0D4091F1\r
+:1048E0007D0D50917E0D0E94896EAB01BC0122E0CE\r
+:1048F00030E081EC95E00E94B92E8091C00085FFE8\r
+:10490000FCCF32C380E001C081E00E9446512FC33A\r
+:10491000E2EFF3E007C09091C00095FFFCCF8093D9\r
+:10492000C600319684918111F6CF329B03C0E7EB2C\r
+:10493000F1E009C0EAEBF1E006C09091C00095FFFC\r
+:10494000FCCF8093C60081918111F7CFE9EFF3E0AE\r
+:1049500007C09091C00095FFFCCF8093C6003196B0\r
+:1049600084918111F6CF339B03C0E7EBF1E009C0DE\r
+:10497000EAEBF1E006C09091C00095FFFCCF809378\r
+:10498000C60081918111F7CFE0E0F4E007C090917B\r
+:10499000C00095FFFCCF8093C600319684918111B1\r
+:1049A000F6CF349B03C0E7EBF1E009C0EAEBF1E09E\r
+:1049B00006C09091C00095FFFCCF8093C600819106\r
+:1049C0008111F7CF8091C00085FFFCCFCDC240E4BC\r
+:1049D000E42E42E0F42E00E010E0F70181917F0127\r
+:1049E0000E943F0F882339F10E942B0F23E6C22E2D\r
+:1049F0002DE0D22EC00ED11E0E94F66EF60160830D\r
+:104A00007183828393830E942B0F37E3C32E3DE093\r
+:104A1000D32EC00ED11EF801ED58F24F2081318106\r
+:104A2000428153810E9457710E94F66EF6016083A5\r
+:104A30007183828393830C5F1F4F0031110569F6E8\r
+:104A400096C200E412E093E8E92E9DE0F92EF80109\r
+:104A500081918F010E943F0F882339F00E942B0F14\r
+:104A6000F7016083718382839383F4E0EF0EF11C7E\r
+:104A700022E00434120759F77AC283E50E943F0FFF\r
+:104A8000882351F00E942B0F60935B0D70935C0D97\r
+:104A900080935D0D90935E0D84E50E943F0F882307\r
+:104AA00009F465C20E942B0F6093570D7093580D47\r
+:104AB0008093590D90935A0D5AC283E50E943F0F7F\r
+:104AC000882351F00E942B0F60935F0D7093600D4F\r
+:104AD0008093610D9093620D84E50E943F0F8823BF\r
+:104AE00051F00E942B0F6093470D7093480D8093F7\r
+:104AF000490D90934A0D82E40E943F0F882361F094\r
+:104B00000E942B0F0E94F66E6093930D7093940D8C\r
+:104B10008093950D9093960D88E50E943F0F882312\r
+:104B200051F00E942B0F6093530D7093540D80939E\r
+:104B3000550D9093560D8AE50E943F0F882351F042\r
+:104B40000E942B0F60934F0D7093500D8093510D69\r
+:104B50009093520D85E40E943F0F882309F407C209\r
+:104B60000E942B0F60934B0D70934C0D80934D0D55\r
+:104B700090934E0DFCC100E412E089EEE82E82E035\r
+:104B8000F82EF80181918F010E943F0F882339F0A0\r
+:104B90000E942B0FF7016083718382839383F4E07B\r
+:104BA000EF0EF11C22E00334120759F7E0C183E550\r
+:104BB0000E943F0F882309F4DAC10E942B0F0E9444\r
+:104BC000F16E70931B0160931A0181E080930503DD\r
+:104BD000CEC183E50E943F0F882309F4C8C10E941B\r
+:104BE0002B0F0E94F16E7093190160931801BFC1E1\r
+:104BF00080E50E943F0F882351F00E942B0F6093A5\r
+:104C0000520170935301809354019093550189E4AC\r
+:104C10000E943F0F882381F00E942B0F2DEB37E37A\r
+:104C200046E05EE30E94577160934E0170934F011E\r
+:104C3000809350019093510184E40E943F0F882398\r
+:104C400081F00E942B0F2DEB37E346E05EE30E94DC\r
+:104C5000896E60934A0170934B0180934C0190934D\r
+:104C60004D0183E40E943F0F882351F00E942B0FD7\r
+:104C700060934601709347018093480190934901E6\r
+:104C80000E944458E4E0F2E006C09091C00095FF15\r
+:104C9000FCCF8093C60081918111F7CFE7E0F2E06D\r
+:104CA00006C09091C00095FFFCCF8093C600819113\r
+:104CB0008111F7CF409152015091530160915401FD\r
+:104CC0007091550122E030E081EC95E00E94B92E10\r
+:104CD000EBE0F2E006C09091C00095FFFCCF80931E\r
+:104CE000C60081918111F7CF2DEB37E346E05EE3FB\r
+:104CF00060914E0170914F0180915001909151014E\r
+:104D00000E94896EAB01BC0122E030E081EC95E0AD\r
+:104D10000E94B92EEFE0F2E006C09091C00095FF2E\r
+:104D2000FCCF8093C60081918111F7CF2DEB37E343\r
+:104D300046E05EE360914A0170914B0180914C0125\r
+:104D400090914D010E945771AB01BC0122E030E00F\r
+:104D500081EC95E00E94B92EE3E1F2E006C090916B\r
+:104D6000C00095FFFCCF8093C60081918111F7CFE1\r
+:104D70002DEB37E346E05EE3609146017091470119\r
+:104D800080914801909149010E945771AB01BC018B\r
+:104D900022E030E081EC95E00E94B92E8091C000C5\r
+:104DA00085FFFCCFE1C081E00E94AD50E0C083E50B\r
+:104DB0000E943F0F882319F00E942B0F04C060E06F\r
+:104DC00070E086E193E40E94015DD1C00E94AC577F\r
+:104DD000CEC00E941717CBC080E001C081E00E94C6\r
+:104DE0003918C5C00E94C10BC2C01092E602809162\r
+:104DF000A4059091A505A091A605B091A705809363\r
+:104E00007A0590937B05A0937C05B0937D050E9465\r
+:104E1000820FADC084E50E943F0F882309F463C070\r
+:104E20000E942B0F0E94F66E60939F05662391F1FE\r
+:104E3000E7ECF0E007C09091C00095FFFCCF8093B5\r
+:104E4000C600319684918111F6CFE7E1F2E006C009\r
+:104E50009091C00095FFFCCF8093C6008191811195\r
+:104E6000F7CF40E050E060919F0581EC95E00E9413\r
+:104E7000A82DE9E1F2E006C09091C00095FFFCCFBB\r
+:104E80008093C60081918111F7CF8091C00085FF8A\r
+:104E9000FCCF6AC01092E802E7ECF0E007C0909106\r
+:104EA000C00095FFFCCF8093C6003196849181119C\r
+:104EB000F6CFEAE2F2E006C09091C00095FFFCCF89\r
+:104EC0008093C60081918111F7CF6091E8024AE09A\r
+:104ED00050E070E081EC95E00E94C02D8091C00010\r
+:104EE00085FFFCCF41C0E7ECF0E007C09091C00027\r
+:104EF00095FFFCCF8093C600319684918111F6CF47\r
+:104F0000E7E0F4E007C09091C00095FFFCCF8093EC\r
+:104F1000C600319684918111F6CF80916E059091F3\r
+:104F20006F0520E6289FF001299FF00D1124E6511E\r
+:104F3000FC4F06C09091C00095FFFCCF8093C60047\r
+:104F400081918111F7CFE9E1F4E007C09091C000B1\r
+:104F500095FFFCCF8093C600319684918111F6CFE6\r
+:104F60008091C00085FFFCCF8AE08093C6000E943C\r
+:104F70005A0F8DC080E090E00E94140A0C943E1BF2\r
+:104F800081E090E00E94140A0C94491B82E090E0BA\r
+:104F90000E94140A0C94541B0E94340FAB016093BE\r
+:104FA000DC035093DD038093DE039093DF03BC01A9\r
+:104FB00089EB93E00E94D131DACF0E942B0F0E943F\r
+:104FC000F16E8B01E4E4F2E00C94E51E0E94FD2AF0\r
+:104FD0006B017C0115CA0E94FD2A609382057093C3\r
+:104FE000830580938405909385050E94FD2A609334\r
+:104FF0008605709387058093880590938905B7CFC0\r
+:105000000E94FD2A681979098A099B09693E734043\r
+:105010008105910508F0FBC87AC90E94AC575E9AD9\r
+:105020000E941A58A4CF0E94AC5788E50E943F0FF7\r
+:105030008111C8CAC8CAE62FF0E0EE0FFF1FE45086\r
+:10504000F24F808191810E94315969A37AA38BA389\r
+:105050009CA360919F05E62FF0E0EE0FFF1FE0504C\r
+:10506000F24F808191810E94315920E030E040E888\r
+:105070005FE30E94A96D9B01AC0169A17AA18BA19C\r
+:105080009CA10E94826E18160CF0A0CFB9C9A696FA\r
+:105090000FB6F894DEBF0FBECDBFDF91CF911F9149\r
+:1050A0000F91FF90EF90DF90CF90BF90AF909F90C7\r
+:1050B0008F907F906F905F904F903F902F900895CA\r
+:1050C000CF93DF938091E6039091E703039714F465\r
+:1050D0000E94AE0F60E08CE093E00E94CC6980916A\r
+:1050E000E6039091E703892B09F460C080910C03DB\r
+:1050F000882309F443C080916E0590916F0520E6E6\r
+:10510000289FE001299FD00D1124C651DC4F6CE38C\r
+:1051100072E0CE010E949E75892BB9F4BE018CE02D\r
+:1051200093E00E945E69E7EAF1E007C09091C00059\r
+:1051300095FFFCCF8093C600319684918111F6CF04\r
+:105140008091C00085FFFCCF15C08CE093E00E94E9\r
+:10515000C06AEAEAF1E007C09091C00095FFFCCF79\r
+:105160008093C600319684918111F6CF8091C00062\r
+:1051700085FFFCCF8AE08093C60002C00E940A1A15\r
+:105180008091E6039091E70301979093E703809362\r
+:10519000E60380916E0590916F05019664E070E0E2\r
+:1051A0000E94407290936F0580936E050E94FD5996\r
+:1051B00081E00E940F16DF91CF910C94B050CF92F6\r
+:1051C000DF92EF92FF920F931F93CF93DF9300D064\r
+:1051D00000D0CDB7DEB740E052EC61E070E081EC8A\r
+:1051E00095E00E94AA2CEAEDF0E007C09091C00083\r
+:1051F00095FFFCCF8093C600319684918111F6CF44\r
+:105200008091C00085FFFCCF8AE08093C60087ECC8\r
+:1052100090E0FC0107C03091C00035FFFCCF209327\r
+:10522000C600319624912111F6CF24B720FF14C077\r
+:10523000E0EEF0E007C04091C00045FFFCCF3093A6\r
+:10524000C600319634913111F6CF3091C00035FF50\r
+:10525000FCCF3AE03093C60021FF14C0E8EEF0E046\r
+:1052600007C04091C00045FFFCCF3093C600319687\r
+:1052700034913111F6CF3091C00035FFFCCF3AE0C8\r
+:105280003093C60022FF14C0E8EFF0E007C0409161\r
+:10529000C00045FFFCCF3093C600319634913111E8\r
+:1052A000F6CF3091C00035FFFCCF3AE03093C60016\r
+:1052B00023FF14C0E9E0F1E007C04091C00045FFC2\r
+:1052C000FCCF3093C600319634913111F6CF309136\r
+:1052D000C00035FFFCCF3AE03093C60025FF14C074\r
+:1052E000E9E1F1E007C03091C00035FFFCCF209329\r
+:1052F000C600319624912111F6CF2091C00025FFE0\r
+:10530000FCCF2AE02093C60014BEE9E2F1E007C01A\r
+:105310003091C00035FFFCCF2093C6003196249118\r
+:105320002111F6CFE1E3F1E007C03091C00035FF75\r
+:10533000FCCF2093C600319624912111F6CF209105\r
+:10534000C00025FFFCCF2AE02093C600FC01249179\r
+:10535000E7ECF0E008C03091C00035FFFCCF2093AF\r
+:10536000C600319624912111F6CFEBE3F1E007C09E\r
+:105370003091C00035FFFCCF2093C60031962491B8\r
+:105380002111F6CFEBE4F1E007C03091C00035FF0A\r
+:10539000FCCF2093C600319624912111F6CFE6E58B\r
+:1053A000F1E007C03091C00035FFFCCF2093C6006C\r
+:1053B000319624912111F6CFE2E6F1E007C0309159\r
+:1053C000C00035FFFCCF2093C600319624912111F7\r
+:1053D000F6CF2091C00025FFFCCF2AE02093C60025\r
+:1053E000FC018491E7ECF0E008C09091C00095FFCB\r
+:1053F000FCCF8093C600319684918111F6CFE6E60A\r
+:10540000F1E007C09091C00095FFFCCF8093C600EB\r
+:10541000319684918111F6CF0E949C0E4AE050E0B3\r
+:10542000BC0181EC95E00E94C02DE5E7F1E007C0EA\r
+:105430009091C00095FFFCCF8093C6003196849177\r
+:105440008111F6CF4AE050E060ED74E081EC95E028\r
+:105450000E94C02D8091C00085FFFCCF8AE0809320\r
+:10546000C60010926A0510926B0510926C0510929E\r
+:105470006D0580E00E94391873E6C72E7DE0D72EB7\r
+:10548000E3E7EE2EEDE0FE2E07E31DE0F60161916D\r
+:105490007191819191916F01F70121913191419128\r
+:1054A00051917F0129833A834B835C830E94226F51\r
+:1054B00029813A814B815C810E9457710E94F66E6E\r
+:1054C000F80161937193819391938F01F3E7CF1664\r
+:1054D000FDE0DF06D9F60E94825C0E94CB430F906C\r
+:1054E0000F900F900F90DF91CF911F910F91FF9030\r
+:1054F000EF90DF90CF900C9460570E945F2B0E943A\r
+:10550000DF280E946028FDCF24E832E0FC013183CF\r
+:1055100020832581222319F002960C94B832089535\r
+:10552000CF93DF93EC0185559F4F0E94842ACE01D3\r
+:1055300085599F4F0E94842ACE01825B9F4F0E9413\r
+:10554000842ACE01C1960E94842ACE014296DF9120\r
+:10555000CF910C94842A8CE093E00C94EA628CE066\r
+:1055600093E00C94902A1F920F920FB60F92112481\r
+:105570002F933F938F939F93AF93BF938091B805E1\r
+:105580009091B905A091BA05B091BB053091C005C5\r
+:10559000232F2D5F2D3720F40196A11DB11D05C0CD\r
+:1055A000232F2A570296A11DB11D2093C005809379\r
+:1055B000B8059093B905A093BA05B093BB05809147\r
+:1055C000BC059091BD05A091BE05B091BF050196A7\r
+:1055D000A11DB11D8093BC059093BD05A093BE0590\r
+:1055E000B093BF05BF91AF919F918F913F912F9144\r
+:1055F0000F900FBE0F901F9018950F931F938FB7AA\r
+:10560000F8940091B8051091B9052091BA05309130\r
+:10561000BB058FBFB801C9011F910F9108950F936A\r
+:105620001F939FB7F8940091BC051091BD05209180\r
+:10563000BE053091BF0586B5A89B06C08F3F21F0FF\r
+:105640000F5F1F4F2F4F3F4F9FBF322F212F102F24\r
+:105650000027080F111D211D311D42E0000F111FF1\r
+:10566000221F331F4A95D1F7B801C9011F910F912D\r
+:105670000895CF92DF92EF92FF92CF93DF936B0169\r
+:105680007C010E940F2BEB010EC00E940F2B6C1BA4\r
+:105690007D0B683E734038F081E0C81AD108E108FC\r
+:1056A000F108C851DC4FC114D104E104F10469F7D9\r
+:1056B000DF91CF91FF90EF90DF90CF900895789495\r
+:1056C00084B5826084BD84B5816084BD85B5826007\r
+:1056D00085BD85B5816085BDEEE6F0E080818160A5\r
+:1056E0008083E1E8F0E010828081826080838081A5\r
+:1056F00081608083E0E8F0E0808181608083E1EB7D\r
+:10570000F0E0808184608083E0EBF0E08081816064\r
+:105710008083EAE7F0E0808184608083808182601A\r
+:105720008083808181608083808180688083109203\r
+:10573000C10008951F93CF93DF93182FEB0161E011\r
+:105740000E94252C209711F460E004C0CF3FD105C2\r
+:1057500039F461E0812FDF91CF911F910C94412C9E\r
+:10576000E12FF0E0E05FFC4EE491E330B9F028F483\r
+:10577000E13051F0E230B1F50CC0E63019F1E7301C\r
+:1057800049F1E43079F514C084B5806884BDC7BDA3\r
+:105790002EC084B5806284BDC8BD29C080918000C0\r
+:1057A000806880938000D0938900C09388001FC0D8\r
+:1057B00080918000806280938000D0938B00C093A2\r
+:1057C0008A0015C08091B00080688093B000C093BB\r
+:1057D000B3000DC08091B00080628093B000C09390\r
+:1057E000B40005C0C038D1050CF0B3CFADCFDF9108\r
+:1057F000CF911F910895833069F028F48130A1F092\r
+:10580000823011F514C08630B1F08730C1F0843099\r
+:10581000D9F404C0809180008F7703C0809180000C\r
+:105820008F7D80938000089584B58F7702C084B502\r
+:105830008F7D84BD08958091B0008F7703C08091E3\r
+:10584000B0008F7D8093B000089590E0FC01E05D92\r
+:10585000FC4E2491FC01E05BFC4EE491EE2381F0D0\r
+:10586000F0E0EB58FC4EA491B0E09FB7F8948C9117\r
+:10587000611103C02095822301C0822B8C939FBFAE\r
+:1058800008950F931F93CF93DF931F92CDB7DEB789\r
+:10589000A82FB0E0FD01E05FFC4E8491FD01E05DCA\r
+:1058A000FC4E1491FD01E05BFC4E04910023B9F025\r
+:1058B000882321F069830E94FB2B6981E02FF0E0AF\r
+:1058C000E059FC4EA491B0E09FB7F8948C9161111F\r
+:1058D00003C01095812301C0812B8C939FBF0F9033\r
+:1058E000DF91CF911F910F9108951F920F920FB6E4\r
+:1058F0000F9211242F933F934F935F936F938F9346\r
+:105900009F93EF93FF936091C600209142063091E0\r
+:105910004306C90101968F7799274091440650911B\r
+:1059200045068417950741F0F901EE53FA4F60835D\r
+:105930009093430680934206FF91EF919F918F9140\r
+:105940006F915F914F913F912F910F900FBE0F90EC\r
+:105950001F9018959A01AB01211581EE380741057A\r
+:105960005105A9F42AC03093C5002093C40080914A\r
+:10597000C10080618093C1008091C10088608093E4\r
+:10598000C1008091C10080688093C100089582E0C9\r
+:105990008093C00060E079E08DE390E00E94767231\r
+:1059A0002150310941095109CA01B90122E030E011\r
+:1059B00040E050E00E947672D6CF1092C00020E105\r
+:1059C00030E0D1CF80914406909145062091420667\r
+:1059D000309143062817390769F0FC01EE53FA4F5E\r
+:1059E000208101968F7799279093450680934406EE\r
+:1059F00030E002C02FEF3FEFC901089580914406C7\r
+:105A000090914506909343068093420608956F92C5\r
+:105A10007F928F929F92AF92BF92CF92DF92EF923E\r
+:105A2000FF920F931F93CF93DF93CDB7DEB7A0976D\r
+:105A30000FB6F894DEBF0FBECDBF8C0141155105E6\r
+:105A400061057105E1F420E030E040E350E060E002\r
+:105A500070E0A0960FB6F894DEBF0FBECDBFDF9109\r
+:105A6000CF911F910F91FF90EF90DF90CF90BF905B\r
+:105A7000AF909F908F907F906F900C94AF2D662485\r
+:105A80006394712C6C0E7D1EC12CD12C7601822E5C\r
+:105A9000912CA12CB12CCB01BA01A50194010E943B\r
+:105AA0005472FA01D3016D933D01BFEFCB1ADB0AAB\r
+:105AB000EB0AFB0AA901BF014115510561057105FA\r
+:105AC00051F781E0C81AD108E108F1081AC0E1E0F5\r
+:105AD000F0E0EC0FFD1FEC0DFD1D8081482F8A309A\r
+:105AE00010F4405D01C0495C50E060E070E020E0EF\r
+:105AF00030E0C8010E94AF2DA1E0CA1AD108E10828\r
+:105B0000F108BFEFCB16DB06EB06FB0601F7A0960C\r
+:105B10000FB6F894DEBF0FBECDBFDF91CF911F91BE\r
+:105B20000F91FF90EF90DF90CF90BF90AF909F903C\r
+:105B30008F907F906F9008952115310539F48091F1\r
+:105B4000C00085FFFCCF4093C60008950C94072D3C\r
+:105B50009A01462F50E060E070E00C949C2D2115D6\r
+:105B6000310539F48091C00085FFFCCF4093C60019\r
+:105B700008952A30310511F40C94EC2D0C94072D66\r
+:105B80009A01AB01662757FD6095762F0C94AF2DD7\r
+:105B9000CF93DF93EC0120E030E04DE050E060E097\r
+:105BA00070E00E94AF2D20E030E04AE050E060E07D\r
+:105BB00070E0CE01DF91CF910C94AF2DCF93DF93A6\r
+:105BC000EC019A01AB0160E070E00E949C2DCE01D7\r
+:105BD000DF91CF910C94C82DCF92DF92EF92FF927C\r
+:105BE000CF93DF93EC016A017B0177FF10C020E0C7\r
+:105BF00030E04DE250E060E070E00E94AF2DF094A4\r
+:105C0000E094D094C094C11CD11CE11CF11C2AE08A\r
+:105C1000B701A601CE01DF91CF91FF90EF90DF9009\r
+:105C2000CF900C94072D8F929F92AF92BF92CF92FC\r
+:105C3000DF92EF92FF920F931F93CF93DF938C012C\r
+:105C40004A015B01C22F20E030E0A901C501B40187\r
+:105C50000E94826E87FF0DC020E030E04DE250E0F0\r
+:105C600060E070E0C8010E94AF2DB7FAB094B7F8B9\r
+:105C7000B094D0E060E070E080E09FE307C020E0F7\r
+:105C800030E040E251E40E94896EDF5FDC13F7CF21\r
+:105C9000262F372F482F592FC501B4010E94A96D17\r
+:105CA000D62EE72EF82ED92F0E94F66E962EA72E0E\r
+:105CB000B82EC92E0E94226F9B01AC016D2D7E2D46\r
+:105CC0008F2D9D2F0E94A86DD62FF72EE82ED92E4E\r
+:105CD0002AE0492D5A2D6B2D7C2DC8010E94072DDD\r
+:105CE000CC23C1F1EEE7F2E006C09091C00095FF31\r
+:105CF000FCCF8093C60081918111F7CF2BC020E0AB\r
+:105D000030E040E251E46D2F7F2D8E2D9D2D0E94BD\r
+:105D100057714B015C01792D9B2D0E94F16E6B0137\r
+:105D2000EE24D7FCE094FE2CB701A601C8010E9426\r
+:105D3000EC2DC701B6010E94246F9B01AC01682DB8\r
+:105D4000792D8A2D9B2D0E94A86DD62FF72EE82E37\r
+:105D5000D92EC150C111D3CFDF91CF911F910F9197\r
+:105D6000FF90EF90DF90CF90BF90AF909F908F907B\r
+:105D700008950C94132ECF93DF93EC019C012C5FBC\r
+:105D80003F4F41E050E060E070E0898D9A8D0E94C5\r
+:105D900084398823A1F04D895E896F89788D452BE0\r
+:105DA000462B472B71F44C815D816E817F814D8B39\r
+:105DB0005E8B6F8B788F89818068898302C080E0D9\r
+:105DC00001C081E0DF91CF910895CF92DF92EF92F1\r
+:105DD000FF921F93CF93DF93EC0149895A896B8916\r
+:105DE0007C89403E5F4F6F41710508F06DC00E9495\r
+:105DF000BB2E882309F468C00E948F37882309F4DA\r
+:105E000063C0E98DFA8DCC80DD80EE80FF8032E0CA\r
+:105E1000C31AD108E108F108058404C0CC0CDD1CCC\r
+:105E2000EE1CFF1C0A94D2F746855785608971895C\r
+:105E3000C40ED51EE61EF71E81E080934C06C0926C\r
+:105E40004F08D0925008E0925108F092520880E03A\r
+:105E500092E0EFE4F6E0DF019C011D92215030401A\r
+:105E6000E1F711E011C0B701A601410F511D611DFD\r
+:105E7000711D2FE436E080914D0690914E060E94F0\r
+:105E80005E6D882309F11F5FE98DFA8D84811817F3\r
+:105E900050F3C12C82E0D82EE12CF12C058404C0F3\r
+:105EA000CC0CDD1CEE1CFF1C0A94D2F749895A89E0\r
+:105EB0006B897C894C0D5D1D6E1D7F1D498B5A8B36\r
+:105EC0006B8B7C8B81E001C080E0DF91CF911F91D3\r
+:105ED000FF90EF90DF90CF900895CF93DF93EC0188\r
+:105EE00041E0611101C040E06C857D858E859F8514\r
+:105EF0000E94CA37882341F0288980E2289F9001B8\r
+:105F00001124215B394F02C020E030E0C901DF914C\r
+:105F1000CF910895CF93FB0120E030E231932F5FC2\r
+:105F20002B30E1F7DC0130E0C7E01CC0CA3051F192\r
+:105F300038E0CAE017C0E21729F1FC010196E491AC\r
+:105F4000E111F9CFC317F0F02132E0F02F37D0F490\r
+:105F5000FB01E30FF11D822F81568A3108F4205294\r
+:105F600020833F5FCD012D91211109C0FA0180836B\r
+:105F7000918381E0FB019081903259F403C02F326C\r
+:105F800019F4F4CF80E005C02E3281F28CEB96E05C\r
+:105F9000D4CFCF9108950F931F93CF93DF93EC014C\r
+:105FA0008B018B81882311F080E044C0FB0187893D\r
+:105FB000803131F528C083E08B83F801428D538D09\r
+:105FC000648D758D4D8B5E8B6F8B788F9E012F5EF0\r
+:105FD0003F4FC8010E948438882331F31A8F098FFC\r
+:105FE00081E089831C821D821E821F821886198689\r
+:105FF0001A861B861C861D861E861F86188A1AC056\r
+:10600000803291F6D8CF82E08B831D8A1E8A1F8A48\r
+:10601000188EFB01408D518D60E070E095E0440FDB\r
+:10602000551F661F771F9A95D1F7498B5A8B6B8B3B\r
+:106030007C8BD4CFDF91CF911F910F9108952F9238\r
+:106040003F924F925F926F927F928F929F92AF9208\r
+:10605000BF92CF92DF92EF92FF920F931F93CF9355\r
+:10606000DF9300D000D0CDB7DEB79C838B831A01BD\r
+:10607000DC0113968C91811103C02FEF3FEFEEC02E\r
+:10608000EB81FC81818180FFF8CFBCC01601201A12\r
+:10609000310A21015B018B819C8104969A8389835B\r
+:1060A000ABC0AB81BC8118960D911D912D913C9197\r
+:1060B0001B97D901C8019170AA27BB273C01EB812E\r
+:1060C000FC8143816801790159E0F694E794D79403\r
+:1060D000C7945A95D1F7818D928D423061F4DC01DD\r
+:1060E0005A966D917D918D919C915D976C0D7D1D62\r
+:1060F0008E1D9F1D4BC0FC0194809A949C206114BE\r
+:10610000710401F591101EC0012B022B032B59F4D1\r
+:10611000EB81FC8185899689A789B08D84839583DD\r
+:10612000A683B7830FC0AB81BC8114964D915D915E\r
+:106130006D917C91179729813A810E9403388823B9\r
+:1061400009F49BCFAB81BC815996ED91FC915A9794\r
+:1061500014966D917D918D919C9117976250710964\r
+:1061600081099109058404C0660F771F881F991F54\r
+:106170000A94D2F70685178520893189600F711F2F\r
+:10618000821F931F690D711D811D911D20E032E05A\r
+:106190002619370972012415350508F47901E1142F\r
+:1061A000B2E0FB0609F054C000914F08109150086E\r
+:1061B00020915108309152086017710782079307A8\r
+:1061C000D9F546C0C301815B994FA701BC01C50148\r
+:1061D0000E945B75AE0CBF1CEB81FC8100851185B4\r
+:1061E000228533850E0D1F1D211D311D008711874E\r
+:1061F000228733874E185F084114510409F051CFAC\r
+:1062000091012CC0EB81FC81C188D288E388F4889D\r
+:1062100000851185228533852A01612C712C460168\r
+:106220005701801A910AA20AB30A481459046A0451\r
+:106230007B0408F02BCF2DCF9501AB01BC018091E1\r
+:106240004D0690914E060E943B6C8111C3CF15CF35\r
+:1062500040E00E94CA378111B5CF0FCFC9010F901E\r
+:106260000F900F900F90DF91CF911F910F91FF90A2\r
+:10627000EF90DF90CF90BF90AF909F908F907F90E6\r
+:106280006F905F904F903F902F900895CF93DF9342\r
+:106290001F92CDB7DEB741E050E0BE016F5F7F4F88\r
+:1062A0000E941F30019719F4298130E002C02FEFBE\r
+:1062B0003FEFC9010F90DF91CF9108950F931F9386\r
+:1062C000CF93DF938C01EB01FC018381823028F1B5\r
+:1062D00040855185628573854F71552766277727DD\r
+:1062E000452B462B472BC9F440E250E0BE01C801C4\r
+:1062F0000E941F308032910521F0892B71F480E0DB\r
+:106300000DC088818823D9F3853E71F38E3261F305\r
+:106310008B8583FDE9CF80E201C08FEFDF91CF91C4\r
+:106320001F910F910895CF92DF92EF92FF92CF933A\r
+:10633000DF93EC018B81823018F420E030E029C03B\r
+:10634000C884D984EA84FB8475E0F694E794D794F2\r
+:10635000C7947A95D1F78FE0C822DD24EE24FF247C\r
+:10636000CE010E94463197FDE8CF488559856A8560\r
+:106370007B85415E5F4F6F4F7F4F488759876A87A4\r
+:106380007B87960165E0220F331F6A95E1F7215B59\r
+:10639000394FC901DF91CF91FF90EF90DF90CF90FF\r
+:1063A00008958F929F92AF92BF92CF92DF92EF9219\r
+:1063B000FF920F931F93CF93DF93EC014A015B0190\r
+:1063C0002B81222309F475C089899A89AB89BC89FC\r
+:1063D00084179507A607B70708F46BC06CC0811433\r
+:1063E0009104A104B10449F41C821D821E821F8203\r
+:1063F000188619861A861B865AC0088519852A852B\r
+:106400003B85E98DFA8D858590E00996B901A80153\r
+:106410004150510961097109082E04C076956795AC\r
+:10642000579547950A94D2F7CC24CA94DC2C760170\r
+:10643000C80CD91CEA1CFB1C04C0F694E794D79442\r
+:10644000C7948A95D2F7C416D506E606F70620F05B\r
+:10645000012B022B032B49F44D895E896F89788DBE\r
+:106460004C835D836E837F8316C0C41AD50AE60A07\r
+:10647000F70A11C04C815D816E817F819801898D01\r
+:106480009A8D0E94033891E0C91AD108E108F108F9\r
+:10649000811104C014C08E010C5F1F4FC114D104C0\r
+:1064A000E104F10439F788869986AA86BB8681E0E3\r
+:1064B00006C080E004C0223009F091CFF4CFDF9114\r
+:1064C000CF911F910F91FF90EF90DF90CF90BF90F1\r
+:1064D000AF909F908F9008950F931F93CF93DF936A\r
+:1064E000EC018B818823E1F1898187FF33C061E072\r
+:1064F000CE010E946D2F8C01009791F1FC018081EB\r
+:10650000853E71F18B81823040F449895A896B89CB\r
+:106510007C89448F558F668F778F4D895E896F890F\r
+:10652000788DF801538F428F758B648BE09146060E\r
+:10653000F0914706309759F0B8016A5E7F4FC80165\r
+:1065400048960995F801808D918D938B828B898176\r
+:106550008F778983DF91CF911F910F910C948F37A3\r
+:1065600081E0888380E0DF91CF911F910F910895A2\r
+:10657000CF93DF93EC010E946C321B82DF91CF91AD\r
+:106580000895FC01238121110C94B83208956F9273\r
+:106590007F928F929F92AF92BF92CF92DF92EF92B3\r
+:1065A000FF920F931F93CF93DF9300D000D0CDB70E\r
+:1065B000DEB73C016A017B01FC018381813009F077\r
+:1065C00083C0818181FF80C081C0452B462B472B32\r
+:1065D00009F478C0F30180849184A284B384B70164\r
+:1065E000A601C3010E94D131811101C06DC0F30128\r
+:1065F000818D928DC114D104E104F10479F44589AF\r
+:1066000056896789708D0E94953A882309F45CC089\r
+:10661000F301158A168A178A108E3AC0F301448155\r
+:106620005581668177819E012F5F3F4F0E9403381D\r
+:10663000882309F449C049815A816B817C81F30127\r
+:10664000818D928DFC012789203139F4483FFFEF7D\r
+:106650005F0761057105E0F407C0483F2FEF52075F\r
+:1066600062072FE07207A0F40E94953A882361F137\r
+:10667000F30144815581668177810FEF1FEF2FEF82\r
+:106680003FE0818D928D0E94EC38811101C01CC0C9\r
+:10669000F301C18AD28AE38AF48A81818068818386\r
+:1066A000C3010E946C32882379F0B701A6018C14D3\r
+:1066B0009D04AE04BF0410F4B501A401C3010E94FF\r
+:1066C000D13110C081E00EC080E00CC0F3014189DF\r
+:1066D0005289638974894C155D056E057F0508F044\r
+:1066E00074CFF2CF0F900F900F900F90DF91CF915A\r
+:1066F0001F910F91FF90EF90DF90CF90BF90AF90E0\r
+:106700009F908F907F906F900895CF93DF93EC01CF\r
+:1067100040E050E0BA010E94C732882371F061E086\r
+:10672000CE010E946D2F009741F025EEFC012083E1\r
+:106730001B82DF91CF910C948F3780E0DF91CF9156\r
+:106740000895FF920F931F93CF93DF93EC01F42EE4\r
+:1067500080E2689FF0011124E15BF94F838581712C\r
+:1067600021F0842F827109F05CC000914F081091D4\r
+:10677000500820915108309152080C871D872E87B0\r
+:106780003F87688B4489558960E070E0BA015527DE\r
+:106790004427028D138D20E030E0402B512B622BDB\r
+:1067A000732B4D8B5E8B6F8B788F8385887151F443\r
+:1067B000048D158D268D378D098B1A8B2B8B3C8B79\r
+:1067C00081E00CC0803169F59E012F5E3F4F898DBD\r
+:1067D0009A8D0E948438882321F184E08B838F2D49\r
+:1067E0008F7089831C821D821E821F8218861986E3\r
+:1067F0001A861B86F4FE18C040E050E0BA01CE01B4\r
+:106800000E94C732811110C012C049895A896B8910\r
+:106810007C89CE01DF91CF911F910F91FF900C9455\r
+:10682000D1311B8280E003C0F5FCEFCF81E0DF9126\r
+:10683000CF911F910F91FF9008956F927F928F9249\r
+:106840009F92AF92BF92CF92DF92EF92FF920F93FF\r
+:106850001F93CF93DF935C01EB014A01722E898D68\r
+:106860009A8DF501928F818F40E050E0BA01CE0100\r
+:106870000E94D131612C33C045E03695279517959C\r
+:1068800007954A95D1F70F70CE010E949331DC0134\r
+:10689000009709F495C08C91882311F0853EB1F4DE\r
+:1068A000611010C0C0904F08D0905008E09051087F\r
+:1068B000F0905208F501C486D586E686F786008BEF\r
+:1068C000662463948C9181110AC075C04BE050E03E\r
+:1068D000BD01C4010E944E75009709F46FC0088580\r
+:1068E00019852A853B85C988DA88EB88FC880C15D0\r
+:1068F0001D052E053F0508F4BFCF5DC071FE60C0C9\r
+:10690000662051F0F501008961E0C5010E946D2FFC\r
+:10691000EC01009771F454C08B81823009F450C0AF\r
+:10692000CE010E94E52E882309F44AC0CFE4D6E0C8\r
+:1069300000E080E2FE0111928A95E9F78BE0F40114\r
+:10694000DE0101900D928A95E1F7E0914606F09103\r
+:106950004706309739F0BE01625F7F4FCE01409607\r
+:10696000099508C081E298E2998B888B80E098E0D5\r
+:106970009F878E87888999899B8B8A8B998F888F2F\r
+:106980008E859F859F8B8E8B0E948F378823C1F0C9\r
+:10699000472D602FC501DF91CF911F910F91FF907F\r
+:1069A000EF90DF90CF90BF90AF909F908F907F90AF\r
+:1069B0006F900C94A13376FCA1CF02C077FEE8CF94\r
+:1069C00080E0DF91CF911F910F91FF90EF90DF90CA\r
+:1069D000CF90BF90AF909F908F907F906F900895D1\r
+:1069E0005F926F927F928F929F92AF92BF92CF925F\r
+:1069F000DF92EF92FF920F931F93CF93DF93CDB768\r
+:106A0000DEB7C354D1090FB6F894DEBF0FBECDBFB9\r
+:106A10005C016B0124965FAF4EAF2497522E1C8E03\r
+:106A20001F8E19821C826115710511F410E075C06A\r
+:106A3000FC0183818111FACF2496EEADFFAD24973E\r
+:106A400080818F3239F0760117C031962496FFAFDE\r
+:106A5000EEAF24972496EEADFFAD249780818F3260\r
+:106A6000A1F3F60183818250823060F4ECCFEE24F2\r
+:106A7000E394F12CEC0EFD1E8E01045E1F4F4801C5\r
+:106A800038010CC0F601618D728DCE0101960E9415\r
+:106A9000CB2F8111ECCFCACF78018301AE014E5BC1\r
+:106AA0005F4FBE01695C7F4F24968EAD9FAD2497EA\r
+:106AB0000E948A2F811106C0B9CF31962496FFAF6C\r
+:106AC000EEAF24972496EEADFFAD249780818F32F0\r
+:106AD000A1F38823C9F021E0AE01495C5F4FB70103\r
+:106AE000C8010E941D34882309F4A0CFEC14FD04D2\r
+:106AF00019F0C7010E94B8320815190569F68E0110\r
+:106B00000F5F1F4F7301CACF252DAE01495C5F4F48\r
+:106B1000B701C5010E941D34182FCE0101960E94B5\r
+:106B2000C132CE014C960E94C132812FCD5BDF4F26\r
+:106B30000FB6F894DEBF0FBECDBFDF91CF911F918E\r
+:106B40000F91FF90EF90DF90CF90BF90AF909F900C\r
+:106B50008F907F906F905F9008951F93CF93DF93F6\r
+:106B6000CDB7DEB76B970FB6F894DEBF0FBECDBFC3\r
+:106B7000AB0119821C8222E0BC01CE0101960E9469\r
+:106B8000F034882331F0CE0101960E948533182F0E\r
+:106B900001C010E0CE0101960E94C132812F6B9698\r
+:106BA0000FB6F894DEBF0FBECDBFDF91CF911F911E\r
+:106BB00008952F923F924F925F927F928F929F9271\r
+:106BC000AF92BF92CF92DF92EF92FF920F931F93FB\r
+:106BD000CF93DF9300D000D000D0CDB7DEB71C013B\r
+:106BE000162F072F5E834D83DC0113968C9113972C\r
+:106BF000813009F054C111968C9181FF50C155C16B\r
+:106C0000F101418952896389748980859185A285C2\r
+:106C1000B38584179507A607B70729F4AD80BE8012\r
+:106C2000812E902E07C1C1010E94D1318111F6CF72\r
+:106C300036C1D10159968D919C915A97FC0174806F\r
+:106C40007A94B701A601E9E076956795579547953F\r
+:106C5000EA95D1F77422F1E0DF22EE24FF24260129\r
+:106C600071104AC0C114D10409F046C014964D9168\r
+:106C70005D916D917C9117974115510561057105E5\r
+:106C800061F455960D911D912D913C9158970115E8\r
+:106C900011052105310549F522C09E012F5F3F4FA7\r
+:106CA0000E940338882309F4FAC009811A812B81D4\r
+:106CB0003C81D1015996ED91FC915A978789803199\r
+:106CC00039F4083FBFEF1B072105310540F40DC023\r
+:106CD000083FEFEF1E072E07EFE03E0730F0C1013F\r
+:106CE0000E94BB2E811108C0DAC0D10114960D9309\r
+:106CF0001D932D933C93179780E092E0841995099A\r
+:106D000085018A159B0508F48C01D1015996ED91F6\r
+:106D1000FC915A971496CD90DD90ED90FC901797CA\r
+:106D2000B2E0CB1AD108E108F108058404C0CC0C0C\r
+:106D3000DD1CEE1CFF1C0A94D2F786859785A0897E\r
+:106D4000B189C80ED91EEA1EFB1EC70CD11CE11C5E\r
+:106D5000F11C0115E2E01E0731F580914F0890917A\r
+:106D60005008A0915108B09152088C159D05AE05B0\r
+:106D7000BF0569F410924C068FEF9FEFDC01809302\r
+:106D80004F0890935008A0935108B0935208940173\r
+:106D9000B701A60180914D0690914E060E945E6D4E\r
+:106DA000811137C07CC04114510419F5D1011896E6\r
+:106DB0004D915D916D917C911B9751968D919D9117\r
+:106DC0000D90BC91A02D481759076A077B0788F0E2\r
+:106DD0000E948F37882309F462C081E080934C06BB\r
+:106DE000C0924F08D0925008E0925108F092520899\r
+:106DF00008C041E0C701B6010E94CA37882309F4E0\r
+:106E00004EC09201215B394FA801B401C9010E9413\r
+:106E10005B75F101C084D184E284F384C00ED11E7D\r
+:106E2000E11CF11CC086D186E286F386800E911E9D\r
+:106E3000A01AB10AF101C084D184E284F384A114C0\r
+:106E4000B10409F0F6CE01891289238934890C1521\r
+:106E50001D052E053F0530F4D101C18AD28AE38A8F\r
+:106E6000F48A0BC08091460690914706892B59F011\r
+:106E7000ED81FE81EF2B39F0D10111968C911197A4\r
+:106E8000806811968C93F101818183FD03C02D816F\r
+:106E90003E810EC0C1010E946C328111F8CF81E0A9\r
+:106EA000D1018C932FEF3FEF03C082FDA9CEB6CE68\r
+:106EB000C90126960FB6F894DEBF0FBECDBFDF9195\r
+:106EC000CF911F910F91FF90EF90DF90CF90BF90E7\r
+:106ED000AF909F908F907F905F904F903F902F90BA\r
+:106EE0000895DB010D900020E9F7AD0141505109F3\r
+:106EF000461B570B02960C94D935CF93DF931F9204\r
+:106F0000CDB7DEB7698341E050E0BE016F5F7F4FD0\r
+:106F100002960E94D9350F90DF91CF91089580910C\r
+:106F20004C068823A9F140914F0850915008609178\r
+:106F30005108709152082FE436E080914D069091EF\r
+:106F40004E060E945E6D811102C080E0089540915E\r
+:106F500048065091490660914A0670914B064115CA\r
+:106F600051056105710591F02FE436E080914D06E1\r
+:106F700090914E060E945E6D882339F31092480668\r
+:106F80001092490610924A0610924B0610924C0637\r
+:106F900081E00895CF92DF92EF92FF92CF936B0141\r
+:106FA0007C01C42F80914F0890915008A091510806\r
+:106FB000B09152088C159D05AE05BF05D1F00E9419\r
+:106FC0008F37811102C080E018C02FE436E0B7018E\r
+:106FD000A60180914D0690914E060E943B6C88233D\r
+:106FE00091F3C0924F08D0925008E0925108F0926D\r
+:106FF000520881E0C11180934C06CF91FF90EF9031\r
+:10700000DF90CF900895AF92BF92CF92DF92EF9230\r
+:10701000FF920F931F93CF93DF935C016A017B0173\r
+:10702000E901FC0141855285638574854F5F5F4F9F\r
+:107030006F4F7F4F4C155D056E057F0510F480E0A6\r
+:1070400058C0FC018789803129F499278F2D7E2D26\r
+:107050006D2D0CC0803299F7C701B60127E09695D7\r
+:107060008795779567952A95D1F7F5010389148956\r
+:1070700025893689600F711F821F931F00914F0869\r
+:1070800010915008209151083091520860177107F3\r
+:107090008207930729F4F50187898031A1F406C09E\r
+:1070A00040E00E94CA378111F6CFC9CFDD24EE241B\r
+:1070B000FF24F601EE0FFF1FE15BF94F8081918104\r
+:1070C000A0E0B0E011C0E894C7F8DD24EE24FF246E\r
+:1070D000F601EE0FFF1FEE0FFF1FE15BF94F8081FE\r
+:1070E0009181A281B381BF7088839983AA83BB8376\r
+:1070F00081E0DF91CF911F910F91FF90EF90DF9092\r
+:10710000CF90BF90AF9008956F927F928F929F9291\r
+:10711000AF92BF92CF92DF92EF92FF920F931F93A5\r
+:10712000CF93DF9300D000D0CDB7DEB78C01498379\r
+:107130005A836B837C833901C12CD12C7601812C3D\r
+:1071400042E0942EA12CB12C49815A816B817C8123\r
+:107150009E012F5F3F4FC8010E940338882341F1F1\r
+:10716000D501C401F801058404C0880F991FAA1F26\r
+:10717000BB1F0A94D2F7C80ED91EEA1EFB1E498116\r
+:107180005A816B817C818789803131F4483F5F4F20\r
+:107190006105710530F4D8CF483F5F4F6F4F7F4096\r
+:1071A00098F2F301C082D182E282F38281E001C0D1\r
+:1071B00080E00F900F900F900F90DF91CF911F9173\r
+:1071C0000F91FF90EF90DF90CF90BF90AF909F9086\r
+:1071D0008F907F906F9008956F927F928F929F9281\r
+:1071E000AF92BF92CF92DF92EF92FF920F931F93D5\r
+:1071F000CF93DF9300D0CDB7DEB73C014A015B01EE\r
+:10720000423051056105710508F46CC0F3014185F8\r
+:107210005285638574854F5F5F4F6F4F7F4F481571\r
+:1072200059056A057B0508F45DC08789803129F41A\r
+:10723000FF24EB2CDA2CC92C0DC0803209F052C08F\r
+:107240007501640177E0F694E794D794C7947A9532\r
+:10725000D1F7F30183899489A589B689C80ED91E0F\r
+:10726000EA1EFB1E41E0C701B60129833A830E9452\r
+:10727000CA3729813A818823A9F1F30187898031AE\r
+:1072800059F49924AA24BB24F401EE0FFF1FE15BFB\r
+:10729000F94F1183008310C0E89487F89924AA2439\r
+:1072A000BB24F401EE0FFF1FEE0FFF1FE15BF94F50\r
+:1072B0000083118322833383F3018289823080F03B\r
+:1072C00085819681A781B0858C0D9D1DAE1DBF1D4A\r
+:1072D0008093480690934906A0934A06B0934B06C4\r
+:1072E00081E001C080E00F900F90DF91CF911F915E\r
+:1072F0000F91FF90EF90DF90CF90BF90AF909F9055\r
+:107300008F907F906F9008952F923F924F925F924F\r
+:107310006F927F928F929F92AF92BF92CF92DF92A5\r
+:10732000EF92FF920F931F93CF93DF93CDB7DEB70A\r
+:107330002F970FB6F894DEBF0FBECDBF1C014C8750\r
+:107340005D876E877F873B872A87DC0119964D9082\r
+:107350005D906D907C901C97BFEF4B1A5B0A6B0A97\r
+:107360007B0AF90180809180A280B380811491040E\r
+:10737000A104B10431F0FFEF8F1A9F0AAF0ABF0AD0\r
+:1073800010C0DC018D909D90AD90BC90B1E0BD83AC\r
+:107390002C853D854E855F852130310541055105A0\r
+:1073A00009F01D82750164011E821F8218861986EC\r
+:1073B000F10181859285A385B4852E813F81488521\r
+:1073C0005985281739074A075B0708F052C04C1443\r
+:1073D0005D046E047F0450F4B2E0CB2ED12CE12C7E\r
+:1073E000F12C12E0812E912CA12CB12C9E012F5F4B\r
+:1073F0003F4FB701A601C1010E9403388823C9F19C\r
+:1074000049815A816B817C81D701C6010196A11DFA\r
+:10741000B11D452B462B472B19F04C015D010DC0CA\r
+:1074200088199909AA09BB092C853D854E855F8578\r
+:1074300082179307A407B50789F08E819F81A885DD\r
+:10744000B9850196A11DB11D8E839F83A887B98739\r
+:107450009FEFC91AD90AE90AF90AAACF0FEF1FEF5D\r
+:107460002FEF3FE0A601B701C1010E94EC38811166\r
+:1074700015C080E041C044244A94542C32014C0C85\r
+:107480005D1C6E1C7F1C97018601B301A201C10126\r
+:107490000E94EC38882369F3730162018C149D0407\r
+:1074A000AE04BF0440F3AA85BB854D915D916D91FB\r
+:1074B0007C91411551056105710551F4EA85FB8503\r
+:1074C00080829182A282B382FD81F11109C013C032\r
+:1074D00095018401C1010E94EC388111EFCFC9CF21\r
+:1074E0002FEF821A920AA20AB20AD1018D929D92BE\r
+:1074F000AD92BC92139781E02F960FB6F894DEBF41\r
+:107500000FBECDBFDF91CF911F910F91FF90EF90F4\r
+:10751000DF90CF90BF90AF909F908F907F906F90B3\r
+:107520005F904F903F902F900895AF92BF92CF926F\r
+:10753000DF92EF92FF920F931F93CF93DF9300D0D0\r
+:1075400000D0CDB7DEB75C016A017B0142E050E0BC\r
+:1075500060E070E0FC0140835183628373839E018D\r
+:107560002F5F3F4FB701A601C5010E94033881116B\r
+:1075700002C080E023C000E010E09801B701A6013E\r
+:10758000C5010E94EC388823A1F3C980DA80EB8022\r
+:10759000FC80F5018789803141F4F8EFCF16FFEFC9\r
+:1075A000DF06E104F10448F4DACF88EFC8168FEF64\r
+:1075B000D806E8068FE0F80690F281E00F900F9071\r
+:1075C0000F900F90DF91CF911F910F91FF90EF904F\r
+:1075D000DF90CF90BF90AF9008958F929F92AF921F\r
+:1075E000BF92CF92DF92EF92FF920F931F93CF93B0\r
+:1075F000DF93EC01142F70934E0660934D061F8AA3\r
+:1076000052E0C52ED12CE12CF12CC882D982EA821D\r
+:10761000FB8210924C061092480610924906109276\r
+:107620004A0610924B06CC24CA94DC2C7601C092F8\r
+:107630004F08D0925008E0925108F092520844232B\r
+:1076400059F1453008F0DDC040E060E070E0CB016A\r
+:107650000E94CA37882309F4D4C020E1129FF001A8\r
+:107660001124E350F84F80818F7709F0CAC0C48499\r
+:10767000D584E684F78434E6C316D104E104F1042A\r
+:1076800008F4BFC0C084D184E284F384C114D1045F\r
+:10769000E104F10421F4B5C0C12CD12C760140E005\r
+:1076A000C701B6010E94CA37882309F4AAC0809195\r
+:1076B0005A0690915B068115924009F0A2C0209174\r
+:1076C0005F06222309F49DC080915D0690915E06BD\r
+:1076D000009709F496C040915C06442309F491C0D8\r
+:1076E0002A8B4C831D8650E061E070E006C02E2F8F\r
+:1076F0002F5F2D87E83008F084C0ED859B010E2EAA\r
+:1077000002C0220F331F0A94E2F74217530779F79A\r
+:1077100020916506309166062115310519F040E08B\r
+:1077200050E008C0209173063091740640917506B0\r
+:10773000509176062D833E834F835887460157012B\r
+:10774000880E991EA11CB11C8B8A9C8AAD8ABE8AA8\r
+:107750000091600610916106198F088FA0915F0655\r
+:10776000B0E00E941D72680D791D8A1D9B1D6A8FF5\r
+:107770007B8F8C8F9D8F980105E0220F331F0A9518\r
+:10778000E1F721503E4F232F33272695DC01CB0113\r
+:10779000820F931FA11DB11D8E879F87A88BB98B68\r
+:1077A00080906206909063068114910419F0A12CD8\r
+:1077B000B12C08C080906F0690907006A090710662\r
+:1077C000B0907206A7019601281B390B4A0B5B0B80\r
+:1077D000DA01C901880D991DAA1DBB1D04C0B6950B\r
+:1077E000A79597958795EA95D2F789879A87AB876A\r
+:1077F000BC87853F3FE09307A105B10520F48CE0ED\r
+:107800008F8B80E016C0853F9F4FA105B10510F416\r
+:1078100080E10DC040917B0650917C0660917D0611\r
+:1078200070917E064A8F5B8F6C8F7D8F80E28F8B8D\r
+:1078300081E0DF91CF911F910F91FF90EF90DF904A\r
+:10784000CF90BF90AF909F908F9008952F923F92CE\r
+:107850004F925F926F927F928F929F92AF92BF9260\r
+:10786000CF92DF92EF92FF920F931F93CF93DF930C\r
+:10787000CDB7DEB7CA54D1090FB6F894DEBF0FBE3C\r
+:10788000CDBF1C017E8B6D8B3A012BA30EA3E8A606\r
+:10789000AEAABFAAC8AED9AE34E0239F800111249E\r
+:1078A000400F511F5BAB4AABDA018D909D90AD90BC\r
+:1078B000BC90FC01E00FF11F208131814281538196\r
+:1078C000C501B4010E94A96D6AAF7BAF8CAF9DAFBB\r
+:1078D000EEA1B4E0EB9F7001112493012E0D3F1D2A\r
+:1078E0003DAB2CABD9014D905D906D907C90F1013A\r
+:1078F000EE0DFF1D2081318142815381C301B20110\r
+:107900000E94A96D6EAF7FAF21968FAF219722960F\r
+:107910009FAF2297E8A5B4E0EB9FC0011124F101CD\r
+:10792000E80FF91F20813181428153812B8F3C8FD9\r
+:107930004D8F5E8FED89FE89E80FF91F608171819F\r
+:10794000828193810E94A86D462F572F682F792F2F\r
+:10795000498B5A8B6B8B7C8BAD89BE891C968D9124\r
+:107960009D910D90BC91A02D8AA79BA7ACA7BDA708\r
+:10797000D1011C962D913D914D915C911F972F8FB8\r
+:1079800038A349A35AA3B7FAB094B7F8B09477FADA\r
+:10799000709477F87094ED89FE89E00FF11F7AAD4D\r
+:1079A0006BAD9CAD8DAD272F362F492F582F6081A1\r
+:1079B0007181828193810E94A86D6F8B788F898FEE\r
+:1079C0009DA3ED89FE89EE0DFF1D7EAD6FAD219665\r
+:1079D0009FAD219722968FAD2297272F362F492FC3\r
+:1079E000582F60817181828193810E94A86D162F2A\r
+:1079F000072F382E292E7F89688D998D8DA1272FED\r
+:107A0000362F492F582FC501B4010E9457716B01C1\r
+:107A10007C01B801C101272F362F492F582FC301F0\r
+:107A2000B2010E9457719B01AC01C701B6010E94CF\r
+:107A3000A96D6B017C01B801C101272F362F492F99\r
+:107A4000582FC501B4010E9457716EA77FA788AB5C\r
+:107A500099AB7F89688D998D8DA1272F362F492F5E\r
+:107A6000582FC301B2010E9457719B01AC016EA552\r
+:107A70007FA588A999A90E94A86DA70196010E94D7\r
+:107A80001C6E362E272E182F092F20E030E0A9017A\r
+:107A9000D101F8016B2F7A2F8F2F9E2F0E94826EBB\r
+:107AA00087FF10C02BED3FE049EC50E4D101F80115\r
+:107AB0006B2F7A2F8F2F9E2F0E94A96D362E272E87\r
+:107AC000182F092FA4968FADA497882381F02BED52\r
+:107AD0003FE049EC50E4D101F8016B2F722D8F2F5C\r
+:107AE000902F0E94A86D362E272E182F092FC98897\r
+:107AF000DA88EB88FC88E894F7F8A3962CAD3DADC6\r
+:107B00004EAD5FADA397D101F8016B2F722D8F2F72\r
+:107B1000902F0E945771A70196010E9492706B01ED\r
+:107B20007C012FE632E143E85AE30E94826E87FD32\r
+:107B300016C2C701B6010E945F6F0E94F66E7A8F6F\r
+:107B4000698FFB01EF2B21F421E030E03A8F298F80\r
+:107B5000498D5A8DBA0180E090E00E94226F6B013E\r
+:107B60007C019B01AC01D101F8016B2F722D8F2F8D\r
+:107B7000902F0E94896E6F8B788F1C01A701960150\r
+:107B800069897A898B899C890E94896E24966FAF56\r
+:107B9000249725967FAF259726968FAF2697279611\r
+:107BA0009FAF27972F8D38A149A15AA16AA57BA520\r
+:107BB0008CA59DA50E94A86DA70196010E94896EC3\r
+:107BC00028966FAF289729967FAF29972A968FAF6F\r
+:107BD0002A972B969FAF2B9720E030E040E05FE3A1\r
+:107BE000BF89A88D6B2F7A2F822D932D0E945771FC\r
+:107BF000BF89A88D2B2F3A2F422D532D0E945771EC\r
+:107C00009B01AC0160E070E080E89FE30E94A86DFA\r
+:107C10006DA37AA78EA723969FAF2397CE010196D7\r
+:107C2000FC01A8A554E0A59FE00DF11D11242B8DAA\r
+:107C30003C8D4D8D5E8D20833183428353832F8D08\r
+:107C400038A149A15AA12D873E874F87588B198AA1\r
+:107C500041E050E05C8F4B8F81E090E08C0F9D1FE6\r
+:107C6000BBA1A4E0BA9F800D911D112499A788A7FC\r
+:107C700021E030E02C0F3D1F5EA144E0549F200D19\r
+:107C8000311D11243FA32EA3FCA3EBA347C1898977\r
+:107C900089310CF050C07F89688D272F362F422DF7\r
+:107CA000532DC501B4010E9457716B017C017DA168\r
+:107CB0006AA59EA523968FAD2397272F362F492F90\r
+:107CC000582FC301B2010E9457719B01AC01C7013B\r
+:107CD000B6010E94A96D7B018C017DA16AA59EA5BC\r
+:107CE00023968FAD2397272F362F492F582FC50165\r
+:107CF000B4010E9457715B016C017F89688D272F49\r
+:107D0000362F422D532DC301B2010E9457719B01A2\r
+:107D1000AC016A2D7B2D8C2D9D2D0E94A86D862E89\r
+:107D2000972EA82EB92E99899F5F998B4E2C5F2C88\r
+:107D3000602E712E72C0AB8DBC8DBD0180E090E0D5\r
+:107D40000E94226FBF89A88D2B2F3A2F422D532DD1\r
+:107D50000E9457717B018C017F2D802F912F0E94F3\r
+:107D6000866E2B013C016E2D7F2D802F912F0E945E\r
+:107D7000BA71698B7A8B8B8B9C8BEAA9FBA980816A\r
+:107D80009181A281B3819C01AD0150582F8F38A3FE\r
+:107D900049A35AA3ACA9BDA9CD90DD90ED90FC906C\r
+:107DA000A30192016F8D78A189A19AA10E945771B8\r
+:107DB0004B015C0129893A894B895C89C701B6016D\r
+:107DC0000E9457719B01AC01C501B4010E94A96DCD\r
+:107DD000862E972EA82EB92E29893A894B895C893F\r
+:107DE0006F8D78A189A19AA10E945771698B7A8BB6\r
+:107DF0008B8B9C8BA3019201C701B6010E94577126\r
+:107E00009B01AC0169897A898B899C890E94A86D44\r
+:107E1000462E572E682E792E198AA5019401BAADE7\r
+:107E2000ABADFCADEDAD6B2F7A2F8F2F9E2F0E9447\r
+:107E3000A96DE8A5F9A56083718382839383A3016B\r
+:107E40009201BEADAFAD2196FFAD21972296EFAD69\r
+:107E500022976B2F7A2F8F2F9E2F0E94A96DAEA194\r
+:107E6000BFA16D937D938D939C93139724967FADC3\r
+:107E7000249725966FAD259726969FAD2697279632\r
+:107E80008FAD2797272F362F492F582FEBA1FCA115\r
+:107E900060817181828193810E94A96DABA1BCA197\r
+:107EA0006D937D938D939C93139728967FAD289720\r
+:107EB00029966FAD29972A969FAD2A972B968FAD5D\r
+:107EC0002B97272F362F492F582F6D857E858F852D\r
+:107ED00098890E94A96D6D877E878F87988BCE01C8\r
+:107EE00001960E94181494E6C92ED12CCC0EDD1EEA\r
+:107EF000EEA8FFA808AD19AD9E01235F3F4FAE016C\r
+:107F0000475F5F4FBE016B5F7F4FCE0101960E94BE\r
+:107F10006845EB8DFC8D3196FC8FEB8F2B8D3C8D66\r
+:107F2000498D5A8D2417350708F4B1CE2D893E8925\r
+:107F3000245F3F4F4D895E89485F5F4F6D897E8921\r
+:107F40006C5F7F4F84E6C82ED12CCC0EDD1EEEA8D0\r
+:107F5000FFA808AD19AD8D899E890E946845C65B52\r
+:107F6000DF4F0FB6F894DEBF0FBECDBFDF91CF91CC\r
+:107F70001F910F91FF90EF90DF90CF90BF90AF9047\r
+:107F80009F908F907F906F905F904F903F902F9039\r
+:107F900008952F923F924F925F926F927F928F92AD\r
+:107FA0009F92AF92BF92CF92DF92EF92FF920F9388\r
+:107FB0001F93CF93DF93CDB7DEB768970FB6F894D2\r
+:107FC000DEBF0FBECDBF1C014A015B016801790114\r
+:107FD000DC01D8966D917D918D919C91DB970E94EB\r
+:107FE000226F69837A838B839C83A50194010E940D\r
+:107FF00057710E946F6E0E94F66E6D837E838F8331\r
+:108000009887A701960169817A818B819C810E9462\r
+:1080100057710E946F6E0E94F66E69877A878B8710\r
+:108020009C872D813E814F81588528373105410538\r
+:10803000510540F488E790E0A0E0B0E08D839E8396\r
+:10804000AF83B88729853A854B855C85283731050C\r
+:108050004105510540F488E790E0A0E0B0E0898751\r
+:108060009A87AB87BC87F101EC5BFF4F4080518062\r
+:1080700062807380C301B2010E94246F6B017C0196\r
+:10808000D101DC966D917D918D919C91DF970E943D\r
+:10809000226F6D877E878F87988B20E030E0A90163\r
+:1080A000C701B6010E94826E882339F129813A8185\r
+:1080B0004B815C81CA01B9010E9457714B015C017F\r
+:1080C0002D853E854F855889CA01B9010E94577197\r
+:1080D0009B01AC01C501B4010E94A86D4B015C017C\r
+:1080E000A7019601C701B6010E94A96D9B01AC01D1\r
+:1080F000C501B4010E94896E04C060E070E080E0B8\r
+:1081000090E00E946F6E0E94F16E4B015C01662749\r
+:108110007727CB0164197509860997090E94246F96\r
+:108120002B013C01F101E05CFF4F60817181828194\r
+:1081300093810E94226F698B7A8B8B8B9C8B20E0C2\r
+:1081400030E0A901C301B2010E94826E882379F157\r
+:1081500029893A894B895C89CA01B9010E94577102\r
+:108160006D8B7E8B8F8B988F29813A814B815C81BF\r
+:10817000CA01B9010E9457719B01AC016D897E89CA\r
+:108180008F89988D0E94A86D69837A838B839C83E5\r
+:10819000A3019201C301B2010E94A96D9B01AC0130\r
+:1081A00069817A818B819C810E94896E04C060E024\r
+:1081B00070E080E090E00E945F6FD10150962D91B9\r
+:1081C0003D914D915C91539729833A834B835C8316\r
+:1081D00029013A01481859086A087B080E94F16E83\r
+:1081E000461A570A680A790A77FE6CC020E030E028\r
+:1081F000A901C701B6010E94826E882309F445C017\r
+:10820000A7019601C701B6010E94A96D2B013C018F\r
+:1082100069817A818B819C810E94226F9B01AC01D4\r
+:10822000C301B2010E9457712B013C012D853E858F\r
+:108230004F855889CA01B9010E9457719B01AC0151\r
+:10824000C301B2010E94A86D2B013C0129893A8922\r
+:108250004B895C89CA01B9010E9457719B01AC012D\r
+:10826000C301B2010E94A96D4B015C0120E030E026\r
+:1082700040E850E4C701B6010E9457719B01AC0170\r
+:10828000C501B4010E94896E04C060E070E080E026\r
+:1082900090E00E946F6E0E94F16E4B015C01B7FE90\r
+:1082A00003C0812C912C540129813A814B815C813E\r
+:1082B00082169306A406B50610F049015A01412C16\r
+:1082C000512C32018FB7F894F101E45BFF4F90819C\r
+:1082D000911125C0D10154968D929D92AD92BC9280\r
+:1082E0005797480C591C6A1C7B1CF101408E518E1B\r
+:1082F000628E738E2D813E814F815885DC962D9341\r
+:108300003D934D935C93DF97A05CBF4F29853A85E1\r
+:108310004B855C852D933D934D935C9313978FBF55\r
+:1083200068960FB6F894DEBF0FBECDBFDF91CF9138\r
+:108330001F910F91FF90EF90DF90CF90BF90AF9083\r
+:108340009F908F907F906F905F904F903F902F9075\r
+:108350000895AF92BF92CF92DF92EF92FF920F9368\r
+:108360001F93CF93DF935B017A016115710509F4C7\r
+:1083700075C04115510509F471C0FB0102A513A593\r
+:10838000D4A5C5A5CE01202F312F492F582F66A186\r
+:1083900077A180A591A50E94826E882309F45EC012\r
+:1083A000F50187A981114BC0F701C6A0D7A0E0A4B1\r
+:1083B000F1A4A7019601DE01602F712F8B2F9A2F58\r
+:1083C0000E9485701816DCF5A7019601C701B60159\r
+:1083D0000E9457716B017C01F50182A993A9A4A9A0\r
+:1083E000B5A9BC01CD0190589B01AC010E94A96DBB\r
+:1083F000F50126A537A540A951A90E9457719B01F7\r
+:10840000AC01C701B6010E94A86D0E94C4716B0146\r
+:108410007C01762F272F3D2D4E2D5F2DFE01602FE5\r
+:10842000712F8F2F9E2F0E94826E87FF03C06801DD\r
+:10843000ED2EFC2E5C2D4D2D3E2D2F2D03C0502FEB\r
+:10844000412F9E01852F942FA32FB22FF50186A3D4\r
+:1084500097A3A0A7B1A781E0F50186ABDF91CF91EB\r
+:108460001F910F91FF90EF90DF90CF90BF90AF9052\r
+:108470000895CF92DF92EF92FF920F931F93CF93C5\r
+:10848000DF931F92CDB7DEB7809155088FB7F89470\r
+:10849000F09054088FBF8091550890E08F19910992\r
+:1084A0008F709927049704F12091550823502F705D\r
+:1084B00040E050E000E010E08DE4E82E13C0211110\r
+:1084C00001C020E12150E29E6001112486E598E080\r
+:1084D000C80ED91EB801C60129830E94A941A8016E\r
+:1084E000860129812F11EBCF0F90DF91CF911F9142\r
+:1084F0000F91FF90EF90DF90CF9008952F923F92D1\r
+:108500004F925F926F927F928F929F92AF92BF92A3\r
+:10851000CF92DF92EF92FF920F931F93CF93DF934F\r
+:10852000EC011B01009709F464C08FA9811161C09F\r
+:108530008EA09FA0A8A4B9A4FB01C6A0D7A0E0A4C8\r
+:10854000F1A42C2D3D2D4E2D5F2DC501B4010E94AF\r
+:10855000826E87FF4EC0A5019401C501B4010E943F\r
+:1085600057712B013C018AA89BA8ACA8BDA8C501E6\r
+:10857000B40190589B01AC010E94A96D2EA53FA5A6\r
+:1085800048A959A90E9457719B01AC01C301B201CE\r
+:108590000E94A86D0E94C4718B01D82FC92F762F1D\r
+:1085A000CE01272F312F492F582F6C2D7D2D8E2D49\r
+:1085B0009F2D0E94826E87FF03C08601DE2DCF2D86\r
+:1085C000CE01202F312F492F5C2F6C2D7D2D8E2D2C\r
+:1085D0009F2D0E94826E882361F09E01802F912F33\r
+:1085E000A32FBC2FF10186A397A3A0A7B1A781E079\r
+:1085F00086ABDF91CF911F910F91FF90EF90DF90AD\r
+:10860000CF90BF90AF909F908F907F906F905F9032\r
+:108610004F903F902F900895EF92FF920F931F93EA\r
+:10862000CF93DF93F0905408C0E0D0E080E090E07A\r
+:108630002DE4E22E11C0EF9C800111240A5A174F3D\r
+:10864000A801BE010E947E428F2D8F5F803109F408\r
+:1086500080E0F82ECE01E80120915508F212EBCF10\r
+:1086600040E050E0BE01DF91CF911F910F91FF904C\r
+:10867000EF900C947E424F925F926F927F928F9216\r
+:108680009F92AF92BF92CF92DF92EF92FF920F93A1\r
+:108690001F93CF93DF9350905408C0E0D0E03DE4A7\r
+:1086A000432E37C0489E3001499E700C112486E548\r
+:1086B00098E0680E791E209731F18EA9811104C0CF\r
+:1086C000F30186A98823F9F0CAA0DBA0ECA0FDA0E5\r
+:1086D000A7019601F30166A177A180A591A50E944B\r
+:1086E000896E4B015C01A70196016EA17FA188A54F\r
+:1086F00099A50E94896EAB01BC0195018401CE0150\r
+:108700000E94C93F1EAA5394F0E15F1201C0512C90\r
+:10871000E301852D992787FD90952091550830E03C\r
+:108720008217930709F0BECF2097F1F0CAA0DBA013\r
+:10873000ECA0FDA0A70196016DEC7CEC8CE49DE320\r
+:108740000E94896E4B015C01A70196016EA17FA179\r
+:1087500088A599A50E94896EAB01BC019501840191\r
+:10876000CE010E94C93F1EAADF91CF911F910F91A8\r
+:10877000FF90EF90DF90CF90BF90AF909F908F9041\r
+:108780007F906F905F904F9008950E9439420E94B1\r
+:108790000C430C943B43109255081092540880E10E\r
+:1087A000E7E2FDE0DF011D928A95E9F71092970D4F\r
+:1087B0001092980D1092990D10929A0D10929B0D97\r
+:1087C00010929C0D10929D0D10929E0D10929F0D77\r
+:1087D0001092A00D1092A10D1092A20D1092A30D57\r
+:1087E0001092A40D1092A50D1092A60D1092A70D37\r
+:1087F0001092A80D1092A90D1092AA0D08954F92F3\r
+:108800005F926F927F928F929F92AF92BF92CF9220\r
+:10881000DF92EF92FF920F931F93CF93DF9380919C\r
+:10882000260D882309F41FC160E08091000E90910D\r
+:10883000010E0E9431590091390110913A01D091F5\r
+:108840003B01C0913C0120E030E040E050E40E9458\r
+:10885000A96DFE01202F312F4F2F5E2F0E94826EB7\r
+:1088600087FD01C1F0905408B12CC12CD12CE12C12\r
+:108870004DE4A42E5CC02F2D30E0A29EF001A39EFB\r
+:10888000F00D1124EA5AF74F80819181A281B381C2\r
+:10889000892B8A2B8B2B81F484819581A681B781CA\r
+:1088A000892B8A2B8B2B41F480859185A285B385FA\r
+:1088B000892B8A2B8B2BC1F1A29E4001A39E900C89\r
+:1088C000112486E598E0880E991EF401648575856B\r
+:1088D000868597850E94246F2B013C01F4016089F5\r
+:1088E0007189828993890E94226F9B01AC01C30127\r
+:1088F000B2010E94896EF40122A133A144A155A1C5\r
+:108900000E9457713B014C012B2D3C2D4D2D5E2DAE\r
+:10891000662D772D882D992D0E948570181624F4C8\r
+:10892000B62CC72CD82CE92CF394FFE0FF228091C1\r
+:108930005508F812A0CF20913501309136014091B1\r
+:108940003701509138016B2D7C2D8D2D9E2D0E946D\r
+:1089500057719B01AC01FE01602F712F8F2F9C2F4F\r
+:108960000E94A96D6B017C01CE01202F312F492F70\r
+:108970005C2F6C2D7D2D8E2D9F2D0E94826E87FD8C\r
+:1089800003C08601DE2DCF2DC0903D01D0903E0169\r
+:10899000E0903F01F09040012C2D3D2D4E2D5F2D9C\r
+:1089A000FE01602F712F8F2F9C2F0E94857018164B\r
+:1089B0001CF06801ED2EFC2E8090AB0D9090AC0D5C\r
+:1089C000A090AD0DB090AE0D2C2D3D2D4E2D5F2DF8\r
+:1089D000C501B4010E9485701816F4F420E037ED4B\r
+:1089E00043EA5CE36C2D7D2D8E2D9F2D0E945771E7\r
+:1089F0008B01D82FC92F28E431EE4AE75FE3C50188\r
+:108A0000B4010E945771FE01202F312F4F2F5E2F8E\r
+:108A10000E94A96D6B017C018C2D9D2DAE2DBF2D6B\r
+:108A20008093AB0D9093AC0DA093AD0DB093AE0DB4\r
+:108A30006C2D7D2D8E2D9F2D0E94F16EDC01CB01C2\r
+:108A400060E00E9461589093010E8093000E8C2D7F\r
+:108A50009D2DAE2DBF2D8093F60D9093F70DA09315\r
+:108A6000F80DB093F90DDF91CF911F910F91FF9009\r
+:108A7000EF90DF90CF90BF90AF909F908F907F90BE\r
+:108A80006F905F904F900895909154088091550891\r
+:108A90009817D9F02091540830E04DE410C0429F5F\r
+:108AA000F0011124EA5AF74F80859185A285B3859C\r
+:108AB000892B8A2B8B2B09F03F5F2F5F2F708091C2\r
+:108AC00055082813ECCF311101C0159A0C94FF43BF\r
+:108AD0002F923F924F925F926F927F928F929F92CE\r
+:108AE000AF92BF92CF92DF92EF92FF920F931F93BC\r
+:108AF000CF93DF93CDB7DEB7C357D1090FB6F89444\r
+:108B0000DEBF0FBECDBF2C013B014A0159013E2CF7\r
+:108B1000F9AA0AAB212EDDAACCAA209155082F5F15\r
+:108B200063962FAF6397203119F463961FAE639756\r
+:108B300063963FAD6397032F112707FD109505C07E\r
+:108B40000E94FD5981E00E940F168091540890E028\r
+:108B500080179107A9F32091730D3091740D409106\r
+:108B6000750D5091760DD2016D917D918D919C91F5\r
+:108B70000E9457710E94277123966FAF2397249606\r
+:108B80007FAF249725968FAF259726969FAF269780\r
+:108B90002091770D3091780D4091790D50917A0D9B\r
+:108BA000F30160817181828193810E9457710E94DB\r
+:108BB000277127966FAF279728967FAF289729961A\r
+:108BC0008FAF29972A969FAF2A9720917B0D3091DE\r
+:108BD0007C0D40917D0D50917E0DD4016D917D9164\r
+:108BE0008D919C910E9457710E9427712B966FAFB7\r
+:108BF0002B972C967FAF2C972D968FAF2D972E9677\r
+:108C00009FAF2E9720917F0D3091800D4091810D67\r
+:108C10005091820DF50160817181828193810E9462\r
+:108C200057710E9427716AAF7BAF8CAF9DAF809167\r
+:108C3000330D9091340DA091350DB091360D2AADC4\r
+:108C40003BAD4CAD5DAD281739074A075B07C1F155\r
+:108C50006091E802E62FF0E0EE0FFF1FE450F24FC4\r
+:108C6000808191810E94315920E030E04AE253E452\r
+:108C70000E94826E87FF24C080915308811120C01A\r
+:108C80000C94CD4F9091C00095FFFCCF8093C6000F\r
+:108C9000319684918111F6CFE2EDF6E007C0909114\r
+:108CA000C00095FFFCCF8093C6003196849181115E\r
+:108CB000F6CF8091C00085FFFCCF8AE08093C6008C\r
+:108CC0008091330D9091340DA091350DB091360DFA\r
+:108CD0002AAD3BAD4CAD5DAD281B390B4A0B5B0B90\r
+:108CE000CA01B90157FF07C09095809570956195AD\r
+:108CF0007F4F8F4F9F4F0E94246F4B015C0120E0FC\r
+:108D000030E848E953E460917F0D7091800D8091C7\r
+:108D1000810D9091820D0E9457719B01AC01C5019C\r
+:108D2000B4010E94857018166CF58AAD9BADACAD90\r
+:108D3000BDAD8093330D9093340DA093350DB0935A\r
+:108D4000360DECECF6E007C09091C00095FFFCCF2B\r
+:108D50008093C600319684918111F6CFECEEF6E057\r
+:108D600007C09091C00095FFFCCF8093C60031965C\r
+:108D700084918111F6CF8091C00085FFFCCF8AE0FD\r
+:108D80008093C60080915508EDE48E9FD001112498\r
+:108D9000AA5AB74FBCA3ABA3FD01E45BFF4F1082FF\r
+:108DA0002091270D3091280D4091290D50912A0DC9\r
+:108DB0002DA33EA34FA358A726964CAC5DAC6EAC3A\r
+:108DC0007FAC2697421A530A640A750A77FE08C0D8\r
+:108DD0007094609450944094411C511C611C711C0F\r
+:108DE000ABA1BCA14D925D926D927C9213972091A4\r
+:108DF0002B0D30912C0D40912D0D50912E0D29A74A\r
+:108E00003AA74BA75CA72A968CAC9DACAEACBFAC86\r
+:108E10002A97821A930AA40AB50AB7FE08C0B0942A\r
+:108E2000A09490948094811C911CA11CB11CABA1B6\r
+:108E3000BCA114968D929D92AD92BC9217972091F1\r
+:108E40002F0D3091300D4091310D5091320D2DA7E5\r
+:108E50003EA74FA758AB2E964CAD5DAD6EAD7FAD26\r
+:108E60002E978DA59EA5AFA5B8A9481B590B6A0BD7\r
+:108E70007B0B77FF07C070956095509541955F4FCC\r
+:108E80006F4F7F4F142F052F6EAB78AFABA1BCA1F6\r
+:108E900018964C93189719965C9319971A966C9399\r
+:108EA0001A971B967C93C090330DD090340DE090B0\r
+:108EB000350DF090360DA0911801B09119012AAD31\r
+:108EC0003BAD4CAD5DAD2C193D094E095F0957FF17\r
+:108ED00007C050954095309521953F4F4F4F5F4FBC\r
+:108EE0000E94287224E630E040E050E00E94767252\r
+:108EF000EBA1FCA12487358746875787812F902FC8\r
+:108F0000AEA9B8AD88159905AA05BB0514F4D5011D\r
+:108F1000C40182179307A407B50714F4DA01C90145\r
+:108F200093018201481659066A067B0614F48C01E7\r
+:108F30009D01ABA1BCA150960D931D932D933C9325\r
+:108F40005397063011052105310510F40C94DD4FBF\r
+:108F5000FD01E85BFF4F8091E70290E0A0E0B0E008\r
+:108F600080839183A283B38326962CAD3DAD4EAD15\r
+:108F70005FAD26978DA19EA1AFA1B8A5281739078F\r
+:108F80004A075B072CF0ABA1BCA190961C9204C0D1\r
+:108F900081E0EBA1FCA180A32A962CAD3DAD4EADA6\r
+:108FA0005FAD2A9789A59AA5ABA5BCA52817390757\r
+:108FB0004A075B0744F4ABA1BCA190968C919097B3\r
+:108FC000826090968C932E962CAD3DAD4EAD5FADEC\r
+:108FD0002E978DA59EA5AFA5B8A9281739074A07D2\r
+:108FE0005B0744F4ABA1BCA190968C9190978460F0\r
+:108FF00090968C932AAD3BAD4CAD5DAD2C153D05E7\r
+:109000004E055F0544F4ABA1BCA190968C9190975E\r
+:10901000886090968C93ECA9FDA98081ABA1BCA13E\r
+:1090200091968C9345284628472809F05E98EBA135\r
+:10903000FCA184819581A681B781892B8A2B8B2BFA\r
+:1090400009F05E98EBA1FCA180859185A285B3858E\r
+:10905000892B8A2B8B2B09F01598EBA1FCA1848519\r
+:109060009585A685B785892B8A2B8B2B09F05E9871\r
+:10907000ABA1BCA11C962D913D914D915C911F9788\r
+:1090800022962CAF3DAF4EAF5FAF2297232B242B00\r
+:10909000252BD9F44090470D5090480D6090490D14\r
+:1090A00070904A0D242D352D462D572DA9A9FAA9CA\r
+:1090B000632D7A2F8F2F922D0E94826E87FD0EC016\r
+:1090C000432C59A86AA8722C09C040905F0D50909B\r
+:1090D000600D6090610D7090620DE4CF26966CADCE\r
+:1090E0007DAD8EAD9FAD26972DA13EA14FA158A578\r
+:1090F000621B730B840B950B0E94246F2091730DE0\r
+:109100003091740D4091750D5091760D0E94896ECD\r
+:109110006DA379AB8AAB9CAB362F272F982F8CA9E8\r
+:10912000432F522F692F782F498B5A8B6B8B7C8B57\r
+:109130002A966CAD7DAD8EAD9FAD2A9729A53AA537\r
+:109140004BA55CA5621B730B840B950B0E94246FCF\r
+:109150002091770D3091780D4091790D50917A0DD5\r
+:109160000E94896E162F072F89A79EAB9801982F12\r
+:109170008EA9432F522F692F782F4D8B5E8B6F8BCB\r
+:10918000788F2E966CAD7DAD8EAD9FAD2E972DA5B3\r
+:109190003EA54FA558A9621B730B840B950B0E942B\r
+:1091A000246F20917B0D30917C0D40917D0D50916D\r
+:1091B0007E0D0E94896E6DA778AF382E292E362F2E\r
+:1091C000272FC101432F522F692F782F498F5A8F94\r
+:1091D0006B8F7C8FA0901801B09019016AAD7BADA8\r
+:1091E0008CAD9DAD6C197D098E099F090E94246F7D\r
+:1091F00020917F0D3091800D4091810D5091820D15\r
+:109200000E94896E6B017C01B501882777FD8095EE\r
+:10921000982F0E94246F9B01AC01C701B6010E94E8\r
+:10922000577120E030E048EC52E40E94896E462FEE\r
+:10923000572F682F792FDB01CA014D8F5E8F6F8FFB\r
+:1092400078A3EBA1FCA180809180A280B380F6E09E\r
+:109250008F169104A104B104E4F4EBA1FCA14481B4\r
+:1092600055816681778146305105610571058CF421\r
+:1092700040855185628573854630510561057105CC\r
+:1092800044F4AC01BD017F7746A757A760AB71AB33\r
+:109290003AC05DA149A93AA92CA9652F742F832F43\r
+:1092A000922F0E9402726B017C01A80139A52EA9A0\r
+:1092B000652F702F832F922F0E9402729B01AC01A9\r
+:1092C000C701B6010E94A96D162F072FF82EE92EAF\r
+:1092D0005DA548AD9101652F742F832F922D0E94BB\r
+:1092E00002729B01AC01D801F7016B2F7A2F8F2FEF\r
+:1092F0009E2F0E94A96D0E94C471EBA1FCA166A7DC\r
+:1093000077A780AB91ABABA1BCA19E962D913D916F\r
+:109310004D915C91D197AA962CAF3DAF4EAF5FAF08\r
+:10932000AA9760E070E080E89FE30E94896E9B014D\r
+:10933000AC01642D752D862D972D0E94577169A75C\r
+:109340007DA789AB9CAB9091550880915408E92F7B\r
+:10935000F0E0E81BF109EF70FF27FBABEAAB762FDB\r
+:109360006DA599A98CA9272F362F492F582F60E07A\r
+:1093700074E284E799E40E94896E0E9427711B01C0\r
+:109380008C012AA93BA9223031050CF44AC069019D\r
+:10939000EE24D7FCE094FE2CC701B6010E94246F96\r
+:1093A00020E030E040E051E40E94826E87FF39C047\r
+:1093B000210138018091930D9091940DA091950D0C\r
+:1093C000B091960D481659066A067B0650F5BC0109\r
+:1093D000CD016419750986099709660F771F881FE3\r
+:1093E000991FA70196010E945472CA01B9010E94F7\r
+:1093F000226F0E942771AB01BC01CB01BA01640D41\r
+:10940000751D861D971D0E94226F9B01AC0160E0B7\r
+:1094100074E284E799E40E94896E69A77DA789AB0D\r
+:109420009CAB79A56DA599A98CA9272F362F492F1B\r
+:10943000582FAA966CAD7DAD8EAD9FADAA970E94B8\r
+:1094400057716E966FAF6E97A2967FAFA2972F96C9\r
+:109450008FAF2F97A6969FAFA697362F272F982FBF\r
+:10946000A6968FADA697432F522F692F782FABA1C9\r
+:10947000BCA192964D935D936D937C939597509676\r
+:109480006D917D918D919C9153970E94226F6B01FC\r
+:109490007C0179A56DA599A98CA9272F362F492F75\r
+:1094A000582FC701B6010E9457710E946F6E0E942B\r
+:1094B000F66E67966CAF7DAF8EAF9FAF6797EBA1EF\r
+:1094C000FCA160AF71AF82AF93AF9E012F5E3F4FA3\r
+:1094D0003FAB2EABAE014F5F5F4F5EA34DA383E862\r
+:1094E0009DE06D969FAF8EAF6D97DE019196BAA30A\r
+:1094F000A9A359AF48AF212C312C00E81FE3EEA9F6\r
+:10950000FFA96191719181919191FFABEEABB9A5EA\r
+:10951000ADA5F9A9ECA92B2F3A2F4F2F5E2F0E9452\r
+:109520005771AB01BC01A8ADB9AD4D935D936D937F\r
+:109530007D93B9AFA8AFDB01CA01BF776B968CAF43\r
+:109540009DAFAEAFBFAF6B976D96AEADBFAD6D9734\r
+:109550004D905D906D907D906D96BFAFAEAF6D9765\r
+:10956000A30192016B966CAD7DAD8EAD9FAD6B97F7\r
+:109570000E9485701816F4F46B962CAD3DAD4EAD7F\r
+:109580005FAD6B97C301B2010E94896E2B01782EEB\r
+:10959000692E762FC301272F352D492F582F622D85\r
+:1095A000732D802F912F0E94826E87FD03C01201C0\r
+:1095B000072D162DEEA9FFA929A13AA1E217F3075D\r
+:1095C00009F09DCF20E030E040E85FE3622D732D8D\r
+:1095D000802F912F0E94826E87FF4FC03E0131E1A4\r
+:1095E000630E711C222D332D402F512FADA1BEA132\r
+:1095F0006D917D918D919C910E945771EDA1FEA17D\r
+:109600006193719381939193FEA3EDA3E615F70502\r
+:1096100049F7222D332D402F512F6E96BFAD6E97F7\r
+:10962000A296AFADA2972F96FFAD2F97A696EFAD5E\r
+:10963000A6976B2F7A2F8F2F9E2F0E945771ABA169\r
+:10964000BCA192966D937D938D939C93959767960D\r
+:109650006CAD7DAD8EAD9FAD67970E94226F222DC0\r
+:10966000332D402F512F0E9457710E94F66EEBA1AF\r
+:10967000FCA160AF71AF82AF93AFAA962CAD3DADA8\r
+:109680004EAD5FADAA97C701B6010E94896E162F35\r
+:10969000072F382E292E81149104A104B10491F5CD\r
+:1096A000EBA1FCA184819581A681B781892B8A2BAE\r
+:1096B0008B2B41F580859185A285B385892B8A2BDB\r
+:1096C0008B2B01F52091570D3091580D4091590D7C\r
+:1096D00050915A0DD801F1016B2F7A2F8F2F9E2FA9\r
+:1096E0000E9457710E946F6EEBA0FCA0F4E4EF0E95\r
+:1096F000F11C0E94F66ED7016D937D938D939C9320\r
+:10970000139700C120915B0D30915C0D40915D0D70\r
+:1097100050915E0DD801F1016B2F702F8F2F922D7C\r
+:109720000E9457710E946F6E0E94F66EEBA1FCA121\r
+:10973000EC5BFF4FFEA3EDA3608371838283938371\r
+:109740004090370D5090380D6090390D70903A0D63\r
+:109750000E94226F69A77AA78BA79CA7C501B401B5\r
+:109760000E94246F9B01AC0169A57AA58BA59CA5DD\r
+:109770000E945771A70196010E94896E4B015C01FE\r
+:10978000C301B2010E94226F9B01AC01C501B4016B\r
+:109790000E948570181634F4EDA1FEA1408251821A\r
+:1097A0006282738240903B0D50903C0D60903D0D65\r
+:1097B00070903E0DADA1BEA16D917D918D919C915A\r
+:1097C0000E94226F4B015C01EBA1FCA164817581B9\r
+:1097D000868197810E94246F9B01AC01C501B40171\r
+:1097E0000E945771A70196010E94896E4B015C018E\r
+:1097F000C301B2010E94226F9B01AC01C501B401FB\r
+:109800000E94857018163CF4ADA1BEA14D925D92E8\r
+:109810006D927C9213974090430D5090440D609050\r
+:10982000450D7090460DEBA1FCA1EC5BFF4FFEA334\r
+:10983000EDA360817181828193810E94226F4B012F\r
+:109840005C0122966CAD7DAD8EAD9FAD22970E94DE\r
+:10985000246F9B01AC01C501B4010E945771A7019F\r
+:1098600096010E94896E4B015C01C301B2010E9406\r
+:10987000226F9B01AC01C501B4010E9485701816CE\r
+:109880003CF4ADA1BEA14D925D926D927C92139776\r
+:1098900040903F0D5090400D6090410D7090420DF2\r
+:1098A000EDA1FEA160817181828193810E94226F6E\r
+:1098B0004B015C01ABA1BCA118966D917D918D917E\r
+:1098C0009C911B970E94246F9B01AC01C501B401C0\r
+:1098D0000E945771A70196010E94896E6B017C015D\r
+:1098E000C301B2010E94226F9B01AC01C701B60106\r
+:1098F0000E948570181634F4EDA1FEA140825182B9\r
+:1099000062827382EBA1FCA1EC5BFF4F60817181ED\r
+:10991000828193810E94226F6B017C01B801C10199\r
+:10992000272F302F492F522DC701B6010E94896E73\r
+:10993000462F572F682F792FE0964CAF5DAF6EAF53\r
+:109940007FAFE097ABA1BCA1D2964D935D936D9391\r
+:109950007C93D5972DEB37E346E051E4C701B60180\r
+:109960000E9457710E94F16EEBA1FCA1648F758F6C\r
+:10997000868F978F4090530D5090540D6090550DE9\r
+:109980007090560D20E030E040E05FE3C301B2018B\r
+:109990000E9457716DA379A78DA799AB29853A8548\r
+:1099A0004B855C85A1962CAF3DAF4EAF5FAFA197C5\r
+:1099B00080904F0D9090500DA090510DB090520D91\r
+:1099C00020E030E040E05FE3C501B4010E94577140\r
+:1099D000162F072FF82EE92EA1966CAD7DAD8EAD1A\r
+:1099E0009FADA1979F77D801F7012B2F3A2F4F2FCB\r
+:1099F0005E2F0E9485701816B4F4B801C701272F96\r
+:109A0000362F492F582FBDA1A9A5FDA5E9A96B2F78\r
+:109A10007A2F8F2F9E2F0E94826E87FD04C01DA378\r
+:109A200009A7FDA6E9AA8D859E85AF85B889A5966B\r
+:109A30008CAF9DAFAEAFBFAFA597C0904B0DD09090\r
+:109A40004C0DE0904D0DF0904E0D20E030E040E0E8\r
+:109A50005FE3C701B6010E945771162F072F382EFA\r
+:109A6000292EA5966CAD7DAD8EAD9FADA5979F7748\r
+:109A7000D801F1012B2F3A2F4F2F5E2F0E948570B6\r
+:109A80001816B4F4B801C101272F362F492F582FCB\r
+:109A9000BDA1A9A5FDA5E9A96B2F7A2F8F2F9E2F18\r
+:109AA0000E94826E87FD04C01DA309A73DA629AAB6\r
+:109AB000ABA1BCA19296BC91BCABEBA1FCA1F3A164\r
+:109AC000FEABABA1BCA19496BC91B8AFEBA1FCA13D\r
+:109AD000F5A1FEAF7CA96EA9272F362F4B2F5F2F44\r
+:109AE000BDA1A9A5FDA5E9A96B2F7A2F8F2F9E2FC8\r
+:109AF0000E94826E87FD08C02CA92DA33EA939A71C\r
+:109B000048AD4DA75EAD59AB8AA99BA902970CF44D\r
+:109B100043C19091A70D9AABA091A80D6496AFAFE9\r
+:109B20006497B091A90D6896BFAF6897E091AA0DB0\r
+:109B30006C96EFAF6C9727E137EB41ED58E36896F1\r
+:109B4000FFAD6897692F7A2F8F2F9E2F0E94857007\r
+:109B500018160CF021C12091970D3091980D40916D\r
+:109B6000990D50919A0D69817A818B819C810E9417\r
+:109B7000A86D162F072F382E292E20919B0D30917E\r
+:109B80009C0D40919D0D50919E0D6D817E818F8128\r
+:109B900098850E94A86D2F966FAF2F97A6967FAFDE\r
+:109BA000A697AB968FAFAB97AC969FAFAC97B8012B\r
+:109BB000C101272F362F492F582FD801F1016B2FC4\r
+:109BC0007A2F8F2F9E2F0E945771E4966CAF7DAF36\r
+:109BD0008EAF9FAFE4972F967FAD2F97A6966FAD70\r
+:109BE000A697AB969FADAB97AC968FADAC97272F52\r
+:109BF000362F492F582FDB01FC016B2F7A2F8F2F27\r
+:109C00009E2F0E9457719B01AC01E4966CAD7DAD17\r
+:109C10008EAD9FADE4970E94A96D0E94C4716296BB\r
+:109C20006CAF7DAF8EAF9FAF6297A30192010E9490\r
+:109C3000857018166CF462962CAD3DAD4EAD5FADDF\r
+:109C40006297C301B2010E94896E8B011C0106C09C\r
+:109C500000E010E050E8252E6FE3362E20919F0D96\r
+:109C60003091A00D4091A10D5091A20DA1966CAD27\r
+:109C70007DAD8EAD9FADA1970E94A86D2B013C01DB\r
+:109C8000E89477F8A5019401C301B2010E948570A0\r
+:109C90001816D4F4A3019201C501B4010E94896E83\r
+:109CA0004B01B82EA92E762FC501272F392D492F0C\r
+:109CB000582F602F712F822D932D0E94826E87FD69\r
+:109CC00003C084012B2C3A2C2091A30D3091A40DBC\r
+:109CD0004091A50D5091A60DA5966CAD7DAD8EADB4\r
+:109CE0009FADA5970E94A86D4B015C01E894B7F861\r
+:109CF000A7019601C501B4010E9485701816D4F41D\r
+:109D0000A5019401C701B6010E94896E6B01F82E6E\r
+:109D1000E92E762FC701272F3D2D492F582F602F71\r
+:109D2000712F822D932D0E94826E87FD03C08601C4\r
+:109D30002F2C3E2C202F312F422D532DBCA9AEA904\r
+:109D4000F8ADEEAD6B2F7A2F8F2F9E2F0E9457719B\r
+:109D50004B015C01762F272F392D4A2D5B2DBAA997\r
+:109D60006496AFAD64976896FFAD68976C96EFAD5B\r
+:109D70006C976B2F7A2F8F2F9E2F0E94826E87FFFA\r
+:109D80000FC08AA864969FAC64976896AFAC68973A\r
+:109D90006C96BFAC6C9704C08DA099A4ADA4B9A873\r
+:109DA000482D592D6A2D7B2DABA1BCA19A964D93C0\r
+:109DB0005D936D937C939D97E0966CAD7DAD8EAD7C\r
+:109DC0009FADE09790589B01AC010E94A96DAA96A7\r
+:109DD0002CAD3DAD4EAD5FADAA970E9457719B0172\r
+:109DE000AC016BE077ED83E29BE30E94A86D0E94DB\r
+:109DF000C4718B01F82EE92E762FC701272F312F42\r
+:109E0000492F582F682D792D8A2D9B2D0E94826E07\r
+:109E100087FD03C04801AF2CBE2C482D592D6A2D5B\r
+:109E20007B2DEBA1FCA146A357A360A771A7C70197\r
+:109E3000202F312F492F5E2DBCA9AEA9F8ADEEAD74\r
+:109E40006B2F7A2F8F2F9E2F0E94826E181634F060\r
+:109E500081E0ABA1BCA1D7968C9303C0EBA1FCA180\r
+:109E600017AA81E0ABA1BCA1D6968C9380E1FE013C\r
+:109E70003196A7E9BDE001900D928A95E1F73CA9E2\r
+:109E80002EA998AD8EAD432F522F692F782F409376\r
+:109E9000A70D5093A80D6093A90D7093AA0D232FC1\r
+:109EA000352F492F582FBDA1A9A5FDA5E9A96B2FD5\r
+:109EB0007A2F8F2F9E2F0E94896E6B017C017CA9C7\r
+:109EC0006EA998AD8EAD272F362F492F582F682DAC\r
+:109ED000792D8A2D9B2D0E94896EAB01BC019701C3\r
+:109EE00086018BA19CA10E94C93F6396BFAD639779\r
+:109EF000B093550826962CAD3DAD4EAD5FAD26977F\r
+:109F00002093270D3093280D4093290D50932A0D4F\r
+:109F10002A968CAD9DADAEADBFAD2A9780932B0D2B\r
+:109F200090932C0DA0932D0DB0932E0D2E962CAD4D\r
+:109F30003DAD4EAD5FAD2E9720932F0D3093300D7C\r
+:109F40004093310D5093320D8AAD9BADACADBDAD9C\r
+:109F50008093330D9093340DA093350DB093360D4F\r
+:109F60000E94C543CD58DF4F0FB6F894DEBF0FBE39\r
+:109F7000CDBFDF91CF911F910F91FF90EF90DF90B8\r
+:109F8000CF90BF90AF909F908F907F906F905F9099\r
+:109F90004F903F902F900C9449512AAD3BAD4CAD62\r
+:109FA0005DAD2093330D3093340D4093350D5093B8\r
+:109FB000360DECECF6E00C944946CD58DF4F0FB669\r
+:109FC000F894DEBF0FBECDBFDF91CF911F910F91EF\r
+:109FD000FF90EF90DF90CF90BF90AF909F908F90C9\r
+:109FE0007F906F905F904F903F902F900895EF92E9\r
+:109FF000FF920F931F93CF93DF937B018A01E901B7\r
+:10A000002091730D3091740D4091750D5091760D26\r
+:10A01000FC0160817181828193810E9457710E944D\r
+:10A020002771E7E2FDE0608371838283938320914F\r
+:10A03000770D3091780D4091790D50917A0DF7019F\r
+:10A0400060817181828193810E9457710E94277182\r
+:10A05000EBE2FDE0608371838283938320917B0D2B\r
+:10A0600030917C0D40917D0D50917E0DF801608105\r
+:10A070007181828193810E9457710E942771EFE262\r
+:10A08000FDE0608371838283938320917F0D309103\r
+:10A09000800D4091810D5091820D688179818A8176\r
+:10A0A0009B810E9457710E942771E3E3FDE060836A\r
+:10A0B0007183828393839F014FE25DE06BE27DE0D9\r
+:10A0C00087E29DE00E94B9571092A70D1092A80D4B\r
+:10A0D0001092A90D1092AA0D1092970D1092980D42\r
+:10A0E0001092990D10929A0D10929B0D10929C0D4A\r
+:10A0F00010929D0D10929E0D10929F0D1092A00D2A\r
+:10A100001092A10D1092A20D1092A30D1092A40D09\r
+:10A110001092A50D1092A60DDF91CF911F910F9176\r
+:10A12000FF90EF90089520917F0D3091800D409128\r
+:10A13000810D5091820DFC0160817181828193813A\r
+:10A140000E9457710E942771E3E3FDE060837183F1\r
+:10A1500082839383CF010C94F5578093530808951D\r
+:10A160008091C10D811188C08091C00D811184C082\r
+:10A170008091BF0D811180C008959091C00095FF1E\r
+:10A18000FCCF8093C600319684918111F6CFE0E137\r
+:10A19000F7E007C09091C00095FFFCCF8093C60008\r
+:10A1A000319684918111F6CF8091C10D8823D1F031\r
+:10A1B0006091C20D7091C30D8091C40D9091C50D39\r
+:10A1C0000E94246F2091730D3091740D4091750D94\r
+:10A1D0005091760D0E94896EAB01BC018FE197E032\r
+:10A1E0000E948B0B8091C00D8823D1F06091C60D29\r
+:10A1F0007091C70D8091C80D9091C90D0E94246F78\r
+:10A200002091770D3091780D4091790D50917A0D14\r
+:10A210000E94896EAB01BC0183E297E00E948B0B28\r
+:10A220008091BF0D8823D1F06091CA0D7091CB0D44\r
+:10A230008091CC0D9091CD0D0E94246F20917B0DCB\r
+:10A2400030917C0D40917D0D50917E0D0E94896E64\r
+:10A25000AB01BC0187E297E00E948B0B8091C000AC\r
+:10A2600085FFFCCF8AE08093C6001092C10D10924A\r
+:10A27000C00D1092BF0D0895EAE0F7E085CF10926F\r
+:10A28000C10D1092C00D1092BF0D0895809345012D\r
+:10A290000895EFE6F0E080818260808308951F9248\r
+:10A2A0000F920FB60F9211240F931F932F933F938A\r
+:10A2B0004F935F936F937F938F939F93AF93BF93CE\r
+:10A2C000EF93FF938091D20D9091D30D892B09F0DC\r
+:10A2D0007DC19091550880915408981771F0E091D4\r
+:10A2E00054088DE4E89FF0011124EA5AF74FDF018A\r
+:10A2F000A45BBF4F81E08C9302C0E0E0F0E0F093FC\r
+:10A30000D30DE093D20D309709F45AC1DF01A45B5D\r
+:10A31000BF4F81E08C931092D40D1092D50D109206\r
+:10A32000D60D1092D70D60AD71AD61349CE97907FF\r
+:10A3300028F461329EE4790748F002C060E47CE9C9\r
+:10A34000769567957695679584E007C0613197E2C9\r
+:10A35000790730F07695679582E08093D80D07C035\r
+:10A360008093D80D6032710510F460E270E06052A5\r
+:10A370007109611588E07807D0F0872F9927880F39\r
+:10A38000991F880F991F855D984FFC013296459162\r
+:10A390005491AA27659F9001649F210D3A1F06944E\r
+:10A3A0002A1F3A1F1124FC01859194911DC09B0125\r
+:10A3B000369527952C7F255D344FF9018591949131\r
+:10A3C0002E5F3F4FF90145915491FB01E770FF2744\r
+:10A3D0004E9F90014F9F300D5E9F300D112413E072\r
+:10A3E000369527951A95E1F7821B930B84369105D4\r
+:10A3F000A0F4EAE8F2E006C09091C00095FFFCCF1F\r
+:10A400008093C60081918111F7CF4AE050E081EC42\r
+:10A4100095E00E94DE2D84E690E09093DA0D809323\r
+:10A42000D90DE091D20DF091D30D64AD75AD70935F\r
+:10A43000DC0D6093DB0D61349CE9790728F461320F\r
+:10A440008EE4780748F002C060E47CE97695679571\r
+:10A450007695679584E007C0613197E2790730F01F\r
+:10A460007695679582E08093D80D08C081E080934F\r
+:10A47000D80D6032710510F460E270E0605271092D\r
+:10A48000611588E07807E0F0872F9927880F991FDA\r
+:10A49000880F991F855D984FFC0132962591349164\r
+:10A4A000AA27639FA001629F410D5A1F06944A1F6D\r
+:10A4B0005A1F1124FC0125913491241B350B1EC019\r
+:10A4C000CB01969587958C7F855D944FFC012591F6\r
+:10A4D00034910296FC0145915491FB01E770FF27EE\r
+:10A4E0004E9FC0014F9F900D5E9F900D112443E041\r
+:10A4F000969587954A95E1F7281B390B2436310547\r
+:10A50000A0F4EAE8F2E006C09091C00095FFFCCF0D\r
+:10A510008093C60081918111F7CF4AE050E081EC31\r
+:10A5200095E00E94DE2D24E630E0C901A0E0B0E015\r
+:10A530008093DD0D9093DE0DA093DF0DB093E00DC1\r
+:10A540003093890020938800E091D20DF091D30DD3\r
+:10A5500080899189A289B389B695A79597958795A2\r
+:10A56000B095A095909581959F4FAF4FBF4F809329\r
+:10A57000E10D9093E20DA093E30DB093E40D809371\r
+:10A58000E50D9093E60DA093E70DB093E80D809351\r
+:10A59000E90D9093EA0DA093EB0DB093EC0D809331\r
+:10A5A000ED0D9093EE0DA093EF0DB093F00D109282\r
+:10A5B000CE0D1092CF0D1092D00D1092D10D06C07D\r
+:10A5C00080ED97E09093890080938800E091D20D10\r
+:10A5D000F091D30D309709F460C480A18093F10D00\r
+:10A5E00080FF38C0459A8FEF8093410180914501EB\r
+:10A5F0008823A1F1329942C480E029C080819181F1\r
+:10A60000A281B381181619061A061B06FCF4809164\r
+:10A61000AF0D9091B00DA091B10DB091B20D80939E\r
+:10A62000C20D9093C30DA093C40DB093C50D81E0EE\r
+:10A630008093C10D80899189A289B3898093CE0DC1\r
+:10A640009093CF0DA093D00DB093D10D81E0809366\r
+:10A65000F20D04C0459881E0809341018091F10D95\r
+:10A6600081FF3CC047988FEF809342018091450164\r
+:10A670008823C1F1339907C480E02DC0E091D20D49\r
+:10A68000F091D30D84819581A681B78118161906A2\r
+:10A690001A061B06FCF48091B30D9091B40DA091A5\r
+:10A6A000B50DB091B60D8093C60D9093C70DA093D4\r
+:10A6B000C80DB093C90D81E08093C00D8089918948\r
+:10A6C000A289B3898093CE0D9093CF0DA093D00D26\r
+:10A6D000B093D10D81E08093F30D04C0479A81E0DF\r
+:10A6E000809342018091F10D82FF3CC02A988FEF48\r
+:10A6F00080934301809145018823C1F13499C8C3F7\r
+:10A7000080E02DC0E091D20DF091D30D8085918530\r
+:10A71000A285B385181619061A061B06FCF480914B\r
+:10A72000B70D9091B80DA091B90DB091BA0D80936D\r
+:10A73000CA0D9093CB0DA093CC0DB093CD0D81E0BD\r
+:10A740008093BF0D80899189A289B3898093CE0DB2\r
+:10A750009093CF0DA093D00DB093D10D81E0809355\r
+:10A76000F40D04C02A9A81E0809343018091F10D99\r
+:10A7700083FF03C0289A8FEF02C0289881E080935E\r
+:10A78000440120E030E081C18091C00087FF19C002\r
+:10A79000E091C6004091420650914306CA010196DD\r
+:10A7A0008F77992760914406709145068617970721\r
+:10A7B00041F0DA01AE53BA4FEC9390934306809385\r
+:10A7C0004206E091D20DF091D30D8091E10D909170\r
+:10A7D000E20DA091E30DB091E40D408151816281C1\r
+:10A7E0007381840F951FA61FB71F8093E10D90936F\r
+:10A7F000E20DA093E30DB093E40D181619061A06A6\r
+:10A800001B0644F55F9A4089518962897389841BCC\r
+:10A81000950BA60BB70B8093E10D9093E20DA093DF\r
+:10A82000E30DB093E40D409141018091AF0D909103\r
+:10A83000B00DA091B10DB091B20D840F911DA11D6D\r
+:10A84000B11D8093AF0D9093B00DA093B10DB09357\r
+:10A85000B20D5F98E091D20DF091D30D8091E50D8E\r
+:10A860009091E60DA091E70DB091E80D44815581DE\r
+:10A8700066817781840F951FA61FB71F8093E50D12\r
+:10A880009093E60DA093E70DB093E80D1816190606\r
+:10A890001A061B0644F5469A4089518962897389D4\r
+:10A8A000841B950BA60BB70B8093E50D9093E60DDB\r
+:10A8B000A093E70DB093E80D409142018091B30D54\r
+:10A8C0009091B40DA091B50DB091B60D840F911D6E\r
+:10A8D000A11DB11D8093B30D9093B40DA093B50D40\r
+:10A8E000B093B60D4698E091D20DF091D30D8091C2\r
+:10A8F000E90D9091EA0DA091EB0DB091EC0D408522\r
+:10A90000518562857385840F951FA61FB71F80939D\r
+:10A91000E90D9093EA0DA093EB0DB093EC0D181692\r
+:10A9200019061A061B0644F52B9A4089518962893B\r
+:10A930007389841B950BA60BB70B8093E90D90933D\r
+:10A94000EA0DA093EB0DB093EC0D40914301809183\r
+:10A95000B70D9091B80DA091B90DB091BA0D840FBB\r
+:10A96000911DA11DB11D8093B70D9093B80DA093BB\r
+:10A97000B90DB093BA0D2B98E091D20DF091D30D93\r
+:10A980008091ED0D9091EE0DA091EF0DB091F00D35\r
+:10A990004485558566857785840F951FA61FB71F4B\r
+:10A9A0008093ED0D9093EE0DA093EF0DB093F00D0D\r
+:10A9B000181619061A061B0644F5299A408951896A\r
+:10A9C00062897389841B950BA60BB70B8093ED0DE1\r
+:10A9D0009093EE0DA093EF0DB093F00D40914401D4\r
+:10A9E0008091BB0D9091BC0DA091BD0DB091BE0D9D\r
+:10A9F000840F911DA11DB11D8093BB0D9093BC0DC3\r
+:10AA0000A093BD0DB093BE0D29988091CE0D90916D\r
+:10AA1000CF0DA091D00DB091D10D0196A11DB11D0A\r
+:10AA20008093CE0D9093CF0DA093D00DB093D10D08\r
+:10AA30004091CE0D5091CF0D6091D00D7091D10D00\r
+:10AA40002F5F3F4FE091D20DF091D30D8089918916\r
+:10AA5000A289B389481759076A077B07B0F040916C\r
+:10AA6000CE0D5091CF0D6091D00D7091D10DE09130\r
+:10AA7000D20DF091D30D84899589A689B789841761\r
+:10AA80009507A607B70748F4E0C08091D80D90E07D\r
+:10AA9000281739070CF478CEE2CF4091DD0D5091A4\r
+:10AAA000DE0D6091DF0D7091E00D048D158D268D0A\r
+:10AAB000378DAA27419FB12D529FC001629F900DF3\r
+:10AAC000619F800D911D429FB00D811D9A1F519F66\r
+:10AAD000B00D811D9A1F609FB00D811D9A1F509F60\r
+:10AAE000B10D8A1F9A1FB6958A1F9A1F112444AD73\r
+:10AAF00055AD480F591F5093DC0D4093DB0D80ADD1\r
+:10AB000091ADA2ADB3AD60E070E084179507A607E4\r
+:10AB1000B70720F49093DC0D8093DB0D6091DB0D83\r
+:10AB20007091DC0D61349CE9790728F461328EE480\r
+:10AB3000780748F002C060E47CE9769567957695E1\r
+:10AB4000679584E007C0613197E2790730F0769528\r
+:10AB5000679582E08093D80D08C081E08093D80D7E\r
+:10AB60006032710510F460E270E0605271096115A5\r
+:10AB700088E07807E0F0872F9927880F991F880FC2\r
+:10AB8000991F855D984FFC01329625913491AA2733\r
+:10AB9000639FA001629F410D5A1F06944A1F5A1FCE\r
+:10ABA0001124FC0125913491241B350B1EC0CB01CF\r
+:10ABB000969587958C7F855D944FFC012591349106\r
+:10ABC0000296FC0145915491FB01E770FF274E9FCF\r
+:10ABD000C0014F9F900D5E9F900D1124F3E096955C\r
+:10ABE0008795FA95E1F7281B390B24363105A0F437\r
+:10ABF000EAE8F2E006C09091C00095FFFCCF809398\r
+:10AC0000C60081918111F7CF4AE050E081EC95E0D8\r
+:10AC10000E94DE2D24E630E03093890020938800E6\r
+:10AC20008091DD0D9091DE0DA091DF0DB091E00DD2\r
+:10AC3000820F931FA11DB11D8093DD0D9093DE0D3A\r
+:10AC4000A093DF0DB093E00DF3C04091CE0D509175\r
+:10AC5000CF0D6091D00D7091D10D808D918DA28D11\r
+:10AC6000B38D84179507A607B70708F0D9C04091A0\r
+:10AC7000D40D5091D50D6091D60D7091D70D048DE6\r
+:10AC8000158D268D378DAA27419FB12D529FC0016A\r
+:10AC9000629F900D619F800D911D429FB00D811D9F\r
+:10ACA0009A1F519FB00D811D9A1F609FB00D811D8D\r
+:10ACB0009A1F509FB10D8A1F9A1FB6958A1F9A1F1F\r
+:10ACC00011242091DB0D3091DC0DE05CFF4F281743\r
+:10ACD000390718F42081318102C0281B390B80818B\r
+:10ACE0009181A281B381A90160E070E04817590702\r
+:10ACF0006A077B0708F49C0121349CE9390728F492\r
+:10AD000021328EE4380748F002C020E43CE9369551\r
+:10AD100027953695279584E007C0213197E23907BA\r
+:10AD200030F03695279582E08093D80D08C081E0F9\r
+:10AD30008093D80D2032310510F420E230E0B901C3\r
+:10AD400060527109611588E07807E0F0872F992734\r
+:10AD5000880F991F880F991F855D984FFC013296C7\r
+:10AD600045915491AA27659F9001649F210D3A1F38\r
+:10AD700006942A1F3A1F1124FC0145915491421B4D\r
+:10AD8000530B1EC0CB01969587958C7F855D944FA4\r
+:10AD9000FC01459154910296FC0125913491FB01EF\r
+:10ADA000E770FF272E9FC0012F9F900D3E9F900DB3\r
+:10ADB000112433E0969587953A95E1F7481B590B96\r
+:10ADC00044365105A0F4EAE8F2E006C09091C000D4\r
+:10ADD00095FFFCCF8093C60081918111F7CF4AE0A7\r
+:10ADE00050E081EC95E00E94DE2D44E650E0509367\r
+:10ADF0008900409388008091D40D9091D50DA09149\r
+:10AE0000D60DB091D70D840F951FA11DB11D809354\r
+:10AE1000D40D9093D50DA093D60DB093D70D08C047\r
+:10AE20008091D90D9091DA0D9093890080938800DC\r
+:10AE30004091CE0D5091CF0D6091D00D7091D10DFC\r
+:10AE4000E091D20DF091D30D80899189A289B389C7\r
+:10AE5000481759076A077B0700F11092D30D10922B\r
+:10AE6000D20D90915508809154089817B1F08091B7\r
+:10AE700054088F5F8F70809354080FC08091F20D3B\r
+:10AE80008111BCCBE3CB8091F30D8111F7CB22CCA8\r
+:10AE90008091F40D811136CC61CCFF91EF91BF917F\r
+:10AEA000AF919F918F917F916F915F914F913F9162\r
+:10AEB0002F911F910F910F900FBE0F901F9018951B\r
+:10AEC0003D9A3F9A229A209A569A5E9A569A5E9A8C\r
+:10AED0000D9A159A569A5E9A3A98429A3B98439AD6\r
+:10AEE0003C98449A579A5F985E9A3E9A46985E9A22\r
+:10AEF000239A2B98159A219A29985E9AA1E8B0E096\r
+:10AF00008C918F7E8C938C9188608C93E0E8F0E03C\r
+:10AF100080818D7F808380818E7F808380818F730D\r
+:10AF2000808380818F7C80838C91887F82608C93EA\r
+:10AF300080E090E4909389008093880010928500CF\r
+:10AF400010928400EFE6F0E080818260808381E0EF\r
+:10AF50008093450178940895909155088091540804\r
+:10AF6000981731F00E94FD5981E00E940F16F4CF2E\r
+:10AF70000895CF93DF93EFB7F894EC01888199811E\r
+:10AF8000AA81BB818093AF0D9093B00DA093B10DBA\r
+:10AF9000B093B20DEB0188819981AA81BB81809326\r
+:10AFA000B30D9093B40DA093B50DB093B60DEA0117\r
+:10AFB00088819981AA81BB818093B70D9093B80D48\r
+:10AFC000A093B90DB093BA0DE90188819981AA8146\r
+:10AFD000BB818093BB0D9093BC0DA093BD0DB0932E\r
+:10AFE000BE0DEFBFDF91CF9108952FB7F894FC010C\r
+:10AFF00080819181A281B3818093BB0D9093BC0D20\r
+:10B00000A093BD0DB093BE0D2FBF08950F931F9356\r
+:10B010009FB7F89424E0829FF0011124E155F24F8C\r
+:10B0200000811181228133819FBFB801C9011F9125\r
+:10B030000F9108950E94AC575E9A5E9A159A5E9A97\r
+:10B04000089580916F008D7F80936F0006C080917E\r
+:10B0500054088F5F8F7080935408909155088091A9\r
+:10B06000540898130AC01092D30D1092D20D8091FB\r
+:10B070006F00826080936F000895909155088091D1\r
+:10B080005408981751F3E3CF20914E0130914F01AE\r
+:10B09000409150015091510160E070E08FE793E4DE\r
+:10B0A0000E94896E6093030E7093040E8093050EC8\r
+:10B0B0009093060E08958E5F914FFC01808190E081\r
+:10B0C00008950F931F93CF93DF93EC01662309F448\r
+:10B0D00061C0E2E1F0E107C09091C00095FFFCCFB4\r
+:10B0E0008093C600319684918111F6CF4AE050E0FA\r
+:10B0F00070E081EC95E00E94C02DE9E1F0E107C02D\r
+:10B100009091C00095FFFCCF8093C600319684914A\r
+:10B110008111F6CF8091C00085FFFCCF8AE080933B\r
+:10B12000C6000E94E61536C0C901880F991F880F16\r
+:10B13000991FFC01E95CFF4E659174916C177D07C6\r
+:10B140003CF521503109220F331F220F331FF90123\r
+:10B15000EB5CFF4E05911491295C3F4EF90145913E\r
+:10B160005491FC01EB5CFF4E25913491C41BD50B2F\r
+:10B17000201B310BC29FC001C39F900DD29F900D29\r
+:10B180001124641B750B0E944072600F711F13C065\r
+:10B190004F5F01C041E0242F30E04D3309F0C4CFB0\r
+:10B1A0002D33310531F4E5E2F1E185919491BC0153\r
+:10B1B00002C060E070E020EF3FE3261B370BC901BF\r
+:10B1C000DF91CF911F910F910895CF93DF9321E0ED\r
+:10B1D00030E0D901AA0FBB1FAA0FBB1FFD01E95C1C\r
+:10B1E000FF4E659174916817790744F52150310934\r
+:10B1F000220F331F220F331FF901EB5CFF4EC59165\r
+:10B20000D491295C3F4EF90145915491FD01EB5CCD\r
+:10B21000FF4E25913491FC01E41BF50B2C1B3D0BDB\r
+:10B22000E29FC001E39F900DF29F900D1124641BDB\r
+:10B23000750B0E9440726C0F7D1F0BC02F5F3F4F3C\r
+:10B240002D33310509F0C5CFE5E2F1E18591949107\r
+:10B25000BC0120EF3FE3261B370BC901DF91CF91E3\r
+:10B2600008952F923F924F925F926F927F928F92AA\r
+:10B270009F92AF92BF92CF92DF92EF92FF920F9385\r
+:10B280001F93CF93DF938C01662351F1E2E1F0E14C\r
+:10B2900007C09091C00095FFFCCF8093C600319607\r
+:10B2A00084918111F6CF4AE050E070E081EC95E0A6\r
+:10B2B0000E94C02DE9E2F1E107C09091C00095FF26\r
+:10B2C000FCCF8093C600319684918111F6CF809196\r
+:10B2D000C00085FFFCCF8AE08093C6000E94E6157F\r
+:10B2E000C0EFDFE3C01BD10B41E05EC09C01220F29\r
+:10B2F000331F220F331FF901EB5CFF4E65907490F2\r
+:10B30000C615D7050CF04FC00197880F991F880FFD\r
+:10B31000991FFC01E95CFF4E259034908B5C9F4E99\r
+:10B32000FC0105911491F901E95CFF4E45905490A0\r
+:10B33000B101882777FD8095982F0E94246F6B01BB\r
+:10B340007C01BE01601B710B882777FD8095982FCB\r
+:10B350000E94246F4B015C01B201621973098827B6\r
+:10B3600077FD8095982F0E94246F9B01AC01C50149\r
+:10B37000B4010E9457714B015C01B301601B710B5A\r
+:10B38000882777FD8095982F0E94246F9B01AC0140\r
+:10B39000C501B4010E94896E9B01AC01C701B601D1\r
+:10B3A0000E94A96D17C04F5F842F90E04D3309F0C4\r
+:10B3B0009DCFCD9759F4E7E2F1E16591749188272B\r
+:10B3C00077FD8095982F0E94246F04C060E070E0A4\r
+:10B3D00080E090E0DF91CF911F910F91FF90EF906F\r
+:10B3E000DF90CF90BF90AF909F908F907F906F90A5\r
+:10B3F0005F904F903F902F9008952F923F924F92E1\r
+:10B400005F926F927F928F929F92AF92BF92CF92F4\r
+:10B41000DF92EF92FF920F931F93CF93DF9300D0B1\r
+:10B42000CDB7DEB78091F50D882309F4BFC18FB782\r
+:10B43000F8941092F50D8FBF60E08091FC0D909113\r
+:10B44000FD0D0E9431596B017C019B01AC016091A3\r
+:10B45000F60D7091F70D8091F80D9091F90D0E9405\r
+:10B46000A86DB62EA72E182F092FA5019801852F9C\r
+:10B47000942FA32FB22F8093070E9093080EA093C2\r
+:10B48000090EB0930A0E20E030E040E251E4D5010D\r
+:10B49000F8016B2F7A2F8F2F9E2F0E948570181620\r
+:10B4A0000CF41DC120E030E040E251ECD501F80180\r
+:10B4B0006B2F7A2F8F2F9E2F0E94826E87FD0BC1DC\r
+:10B4C00080910B0E8111FCC02091520130915301EB\r
+:10B4D0004091540150915501D501F8016B2F7A2DFF\r
+:10B4E0008F2F902F0E945771762E672E89839A8313\r
+:10B4F000A301382F292F852F942FA32FB22F8093AC\r
+:10B50000100E9093110EA093120EB093130E209173\r
+:10B510000C0E30910D0E40910E0E50910F0ED50174\r
+:10B52000F8016B2F7A2D8F2F902F0E94A96D8B0120\r
+:10B530002C018090140E9090150EA090160EB090D5\r
+:10B54000170E282D392D4A2D5B2D602F712F842D3C\r
+:10B55000952D0E94826E87FF9DC084012501802F5A\r
+:10B56000912FA42DB52D80930C0E90930D0EA093CA\r
+:10B570000E0EB0930F0E20914E0130914F0140916D\r
+:10B58000500150915101602F712F842D952D0E94F3\r
+:10B5900057714B015C01862F992DAA2DBB2D8093ED\r
+:10B5A000180E9093190EA0931A0EB0931B0E2091B3\r
+:10B5B0001C0E30911D0E40911E0E50911F0EC701A2\r
+:10B5C000B6010E94A86D20914A0130914B01409133\r
+:10B5D0004C0150914D010E94577120ED3CEC4CE420\r
+:10B5E0005DE30E9457711B012C0123E333E343E722\r
+:10B5F0005FE36091200E7091210E8091220E909158\r
+:10B60000230E0E9457719B01AC01C201B1010E943F\r
+:10B61000A96D8B012C01862F912FA42DB52D809320\r
+:10B62000200E9093210EA093220EB093230EC09271\r
+:10B630001C0ED0921D0EE0921E0EF0921F0E282DB1\r
+:10B64000392D4A2D5B2DD301F980EA806B2F762DA1\r
+:10B650008F2D9E2D0E94A96D202F312F442D552D09\r
+:10B660000E94A86D8B017C0120E030E0A901712FC0\r
+:10B670009F2D0E94826E87FD3CC020E030E04FE7A6\r
+:10B6800053E4602F712F8E2D9F2D0E948570181608\r
+:10B69000A4F528C08090030E9090040EA090050E93\r
+:10B6A000B090060E282D392D4A2D5B2D602F712F5D\r
+:10B6B000842D952D0E94857018160CF050CF4DCF1B\r
+:10B6C00010920C0E10920D0E10920E0E10920F0E84\r
+:10B6D00010920B0EF9CE81E080930B0E0AC081E030\r
+:10B6E00080930B0E00E010E08FE7E82E93E4F92E34\r
+:10B6F00004C000E010E0E12CF12C2091FC0D309111\r
+:10B70000FD0D4091240E5091250E421753071CF059\r
+:10B710001092020E11C0409156015091570124170A\r
+:10B720003507B4F7602F712F8E2D9F2D0E94F16E7B\r
+:10B73000759567956093020E0E94FD2A0091260E72\r
+:10B740001091270E2091280E3091290E601B710B4D\r
+:10B75000820B930B683873418105910538F10E9483\r
+:10B76000FD2A6093260E7093270E8093280E9093E7\r
+:10B77000290E8091FA0D9091FB0D20912A0E3091A7\r
+:10B780002B0E281739078CF420915801309159015C\r
+:10B790008217930754F42091FE0D3091FF0D82170C\r
+:10B7A00093070CF002C05A9A01C05A980F900F905C\r
+:10B7B000DF91CF911F910F91FF90EF90DF90CF908D\r
+:10B7C000BF90AF909F908F907F906F905F904F90C1\r
+:10B7D0003F902F9008952F923F924F925F926F92D9\r
+:10B7E0007F928F929F92AF92BF92CF92DF92EF9211\r
+:10B7F000FF920F931F93CF93DF93C0EFDFE3C81B3C\r
+:10B80000D90B81E090E09C01220F331F220F331FE0\r
+:10B81000F901EB5CFF4E65907490C615D7050CF0EE\r
+:10B820004FC00197880F991F880F991FFC01E95C91\r
+:10B83000FF4E259034908B5C9F4EFC010591149136\r
+:10B84000F901E95CFF4E45905490B101882777FDDE\r
+:10B850008095982F0E94246F6B017C01BE01601BB4\r
+:10B86000710B882777FD8095982F0E94246F4B01DC\r
+:10B870005C01B20162197309882777FD8095982FC2\r
+:10B880000E94246F9B01AC01C501B4010E94577155\r
+:10B890004B015C01B301601B710B882777FD80951C\r
+:10B8A000982F0E94246F9B01AC01C501B4010E9436\r
+:10B8B000896E9B01AC01C701B6010E94A96D0FC042\r
+:10B8C00001968D33910509F09ECFE7E2F1E1659194\r
+:10B8D0007491882777FD8095982F0E94246FDF91BF\r
+:10B8E000CF911F910F91FF90EF90DF90CF90BF907D\r
+:10B8F000AF909F908F907F906F905F904F903F9010\r
+:10B900002F9008951092140E1092150E1092160E8C\r
+:10B910001092170E20914E0130914F01409150012D\r
+:10B920005091510160E070E08FE793E40E94896ECE\r
+:10B930006093030E7093040E8093050E9093060E91\r
+:10B94000559A529A87ED80937A00EEE7F0E01082E4\r
+:10B9500080818068808380818064808380E888BD66\r
+:10B96000EEE6F0E08081846080836AEF70E080E042\r
+:10B9700090E00E94392B60E085E090E00E946158E1\r
+:10B980009093250E8093240E60E083E191E00E9465\r
+:10B990006158909357018093560185E090E00E9492\r
+:10B9A000E55890932B0E80932A0E86E990E00E9432\r
+:10B9B000E55890935901809358010895089560E0E7\r
+:10B9C00080E090E00E9461581092F60D1092F70D01\r
+:10B9D0001092F80D1092F90D80E090E00E94E55869\r
+:10B9E0009093FF0D8093FE0D1092010E1092000EA9\r
+:10B9F0001092020E5D981092FF0D1092FE0D5A9853\r
+:10BA000008952F923F924F925F926F927F928F9202\r
+:10BA10009F92AF92BF92CF92DF92EF92FF920F93DD\r
+:10BA20001F93CF93DF93CDB7DEB7E1970FB6F894AE\r
+:10BA3000DEBF0FBECDBF688B798B8A8B9B8B0E943C\r
+:10BA4000FD2A6C8B7D8B8E8B9F8BEEE9F2E006C01E\r
+:10BA50009091C00095FFFCCF8093C6008191811129\r
+:10BA6000F7CF8091C00085FFFCCF8AE08093C600AD\r
+:10BA70000E94DF5C8FE78093020E4C885D886E88A1\r
+:10BA80007F884C865D866E867F86AFE7CA2ED12C76\r
+:10BA9000E12CF12CBFE78B2E912CA12CB12C8EA682\r
+:10BAA0009FA6A8AAB9AA11E01B861A86EBE2FFE0BE\r
+:10BAB00094909FA2E3E3FFE0A490A8A6E8E3FFE050\r
+:10BAC000B490B9A6EFE3FFE084908AA6E6E4FFE035\r
+:10BAD00094909BA6ECE4FFE0A490ACA68091F50DB9\r
+:10BAE000882309F4BBC28FB7F8941092F50D8FBF6D\r
+:10BAF00060E08091FC0D9091FD0D0E9431596E83A4\r
+:10BB00007F8388879987762F6F81982F8985272F44\r
+:10BB1000362F492F582FB88DA98DEA8DFB8D6B2FAD\r
+:10BB20007A2FCF010E948570181644F0BE80B88E1F\r
+:10BB30008F80898E98849A8EA984AB8E7E816F8146\r
+:10BB400098858985272F362F492F582FECA1602FF4\r
+:10BB5000722D832D9E2F0E94826E87FD05C00E815F\r
+:10BB60002F803884B984BCA2112309F43FC378899B\r
+:10BB700069899A898B89272F362F492F582FBE81A3\r
+:10BB8000AF81E885F9856B2F7A2FCF010E948570F0\r
+:10BB900018160CF063C20E94FD2A64197509860903\r
+:10BBA0009709693873418105910508F457C28EA53C\r
+:10BBB0009FA5A8A9B9A98C199D09AE09BF09B5957A\r
+:10BBC000A795979587958093020E0E94FD2A6C8712\r
+:10BBD0007D878E879F874B015C0184189508A60896\r
+:10BBE000B70888A299A2AAA2BBA29888988EA98811\r
+:10BBF000A98EBA88BA8E8B888B8EF8C20E94FD2AD5\r
+:10BC00008C849D84AE84BF84681979098A099B0954\r
+:10BC1000693873418105910508F41FC20E94FD2A0D\r
+:10BC20002B013C018A849B84892809F4FCC1DC0136\r
+:10BC3000CB018C849D84AE84BF8488199909AA099C\r
+:10BC4000BB0988A099A0AAA0BBA0880E991EAA1E15\r
+:10BC5000BB1E28A139A14AA15BA1281B390B4A0BA5\r
+:10BC60005B0BCA01B901A70196010E943072A501C0\r
+:10BC700094010E9476728EA59FA5A8A9B9A9280F44\r
+:10BC8000391F4A1F5B1F243131054105510524F13D\r
+:10BC90002EA73FA748AB59AB2C3E310541055105B6\r
+:10BCA00044F02BEE30E040E050E02EA73FA748AB39\r
+:10BCB00059AB8EA59FA5A8A9B9A980389105A10562\r
+:10BCC000B105C4F0EEEFCE2ED12CE12CF12CC81A28\r
+:10BCD000D90AEA0AFB0A12C024E130E040E050E051\r
+:10BCE0002EA73FA748AB59AB74E1C72ED12CE12C4E\r
+:10BCF000F12C04C0CEA4DFA4E8A8F9A88FA1EBE240\r
+:10BD0000FFE008C09091C00095FFFCCF8093C60073\r
+:10BD1000319684918111F6CF2AE030E04EA55FA5DF\r
+:10BD200068A979A981EC95E00E94AF2D88A5E3E38D\r
+:10BD3000FFE008C09091C00095FFFCCF8093C60043\r
+:10BD4000319684918111F6CF2AE030E0B701A60147\r
+:10BD500081EC95E00E94AF2D89A5E8E3FFE008C0E3\r
+:10BD60009091C00095FFFCCF8093C60031968491DE\r
+:10BD70008111F6CF22E030E08CA1402F522D632DAF\r
+:10BD8000782F81EC95E00E94B92E8AA5EFE3FFE0C1\r
+:10BD900008C09091C00095FFFCCF8093C6003196FB\r
+:10BDA00084918111F6CF22E030E0F88DE98D9A8DF3\r
+:10BDB0008B8D4F2F5E2F692F782F81EC95E00E949D\r
+:10BDC000B92E8091C00085FFFCCF8AE08093C60029\r
+:10BDD0004A855B85433051050CF425C1C701B60186\r
+:10BDE0000E94246F20E030E040E850E40E94577148\r
+:10BDF0006C8F7D8F8E8F9F8F8CA1202F322D432DA6\r
+:10BE0000582FB88DA98DEA8D1B8D6B2F7A2F8E2F11\r
+:10BE1000912F0E94A86D20ED3FE049E450E40E947C\r
+:10BE2000577120E030E040E05FE30E9457719B01D2\r
+:10BE3000AC016C8D7D8D8E8D9F8D0E94896E362E0E\r
+:10BE4000072F182F9C8FC501B4010E94246F20E09A\r
+:10BE500030E04AE754E40E94896EA62E972E282EE1\r
+:10BE6000892E8BA5E6E4FFE008C09091C00095FF05\r
+:10BE7000FCCF8093C600319684918111F6CF22E0E9\r
+:10BE800030E08C8D432D502F612F782F81EC95E081\r
+:10BE90000E94B92E8CA5ECE4FFE008C09091C00090\r
+:10BEA00095FFFCCF8093C600319684918111F6CF27\r
+:10BEB00022E030E04A2D592D622D782D81EC95E05D\r
+:10BEC0000E94B92E8091C00085FFFCCF8AE080934C\r
+:10BED000C6002AE939E949E15FE3EC8D632D702F53\r
+:10BEE000812F9E2F0E945771362E072F182FB92EA3\r
+:10BEF000762F272F302F412F5B2D632D702F812F11\r
+:10BF00009B2D0E94A96D2A2D392D422D582D0E945E\r
+:10BF1000896E6C8F7CA38DA39EA32A2D392D422D73\r
+:10BF2000582D632D702F812F9B2D0E94577120E07B\r
+:10BF300030E040E05EE30E945771A62E972E282E37\r
+:10BF4000892EE2E5FFE007C09091C00095FFFCCF8D\r
+:10BF50008093C600319684918111F6CF8091C00004\r
+:10BF600085FFFCCF8AE08093C600EFE5FFE007C0C5\r
+:10BF70009091C00095FFFCCF8093C60031968491CC\r
+:10BF80008111F6CF22E030E0432D502F612F7B2D21\r
+:10BF900081EC95E00E94B92E8091C00085FFFCCF16\r
+:10BFA0008AE08093C600E5E6FFE007C09091C000FC\r
+:10BFB00095FFFCCF8093C600319684918111F6CF16\r
+:10BFC00022E030E0FC8DECA19DA18EA14F2F5E2FD1\r
+:10BFD000692F782F81EC95E00E94B92E8091C000E6\r
+:10BFE00085FFFCCF8AE08093C600EBE6FFE007C048\r
+:10BFF0009091C00095FFFCCF8093C600319684914C\r
+:10C000008111F6CF22E030E04A2D592D622D782D96\r
+:10C0100081EC95E00E94B92E8091C00085FFFCCF95\r
+:10C020008AE08093C6008EA59FA5A8A9B9A98C0D0A\r
+:10C030009D1DAE1DBF1DB595A795979587958093BE\r
+:10C04000020E8A859B8501969B878A8708892988A5\r
+:10C050003A888B888CA211E001C010E020E030E02B\r
+:10C0600040EA51E4B889A989EA89FB896B2F7A2FC4\r
+:10C07000CF010E94A96D9B01AC01BE81AF81E88513\r
+:10C08000F9856B2F7A2FCF010E948570181694F4D2\r
+:10C09000E1E7FFE007C09091C00095FFFCCF8093DF\r
+:10C0A000C600319684918111F6CF8091C00085FF42\r
+:10C0B000FCCF98C00E94FD2A8C889D88AE88BF88DE\r
+:10C0C000681979098A099B09613D774081059105C5\r
+:10C0D00008F43FC00E94FD2A6C8B7D8B8E8B9F8B5A\r
+:10C0E000EAE9FFE007C09091C00095FFFCCF809384\r
+:10C0F000C600319684918111F6CF60E08091FC0DED\r
+:10C100009091FD0D0E943159AB01BC0122E030E05D\r
+:10C1100081EC95E00E94B92EE0EAFFE007C0909123\r
+:10C12000C00095FFFCCF8093C600319684918111A9\r
+:10C13000F6CF6091020E4AE050E070E081EC95E0AD\r
+:10C140000E94C02D8091C00085FFFCCF8AE08093C3\r
+:10C15000C6000E94FD2A6A837B838C839D830E9494\r
+:10C16000FD2A8C849D84AE84BF84840C951CA61CFF\r
+:10C17000B71C2A813B814C815D81281939094A0904\r
+:10C180005B09260F371F481F591F21383F44424182\r
+:10C19000510590F0E4EAFFE007C09091C00095FFE0\r
+:10C1A000FCCF8093C600319684918111F6CF8091A7\r
+:10C1B000C00085FFFCCF16C0AA85BB8516970CF47E\r
+:10C1C0008DCCE1ECFFE007C09091C00095FFFCCF63\r
+:10C1D0008093C600319684918111F6CF8091C00082\r
+:10C1E00085FFFCCF8AE08093C60015C078896989F5\r
+:10C1F0009A898B89272F362F492F582FBE81AF81DF\r
+:10C200001885F9856B2F7A2F812F9F2F0E94826EC0\r
+:10C2100087FDF4CC22CFE1960FB6F894DEBF0FBEB7\r
+:10C22000CDBFDF91CF911F910F91FF90EF90DF90E5\r
+:10C23000CF90BF90AF909F908F907F906F905F90C6\r
+:10C240004F903F902F900895CF93DF931F92CDB7DB\r
+:10C25000DEB789830E94DF5C0E94AB166981811181\r
+:10C260002FC0E2E1F0E107C08091C00085FFFCCF64\r
+:10C270009093C600319694919111F6CF4AE050E028\r
+:10C2800070E081EC95E00E94C02D8091C00085FF98\r
+:10C29000FCCF8AE08093C600E6E4F1E107C090910C\r
+:10C2A000C00095FFFCCF8093C60031968491811128\r
+:10C2B000F6CF8091C00085FFFCCF8AE08093C60056\r
+:10C2C0000F90DF91CF910895CF93DF931F92CDB759\r
+:10C2D000DEB789830E94DF5C0E94AB166981811101\r
+:10C2E0002FC0E2E1F0E107C08091C00085FFFCCFE4\r
+:10C2F0009093C600319694919111F6CF4AE050E0A8\r
+:10C3000070E081EC95E00E94C02D8091C00085FF17\r
+:10C31000FCCF8AE08093C600E3E7F1E107C090918B\r
+:10C32000C00095FFFCCF8093C600319684918111A7\r
+:10C33000F6CF8091C00085FFFCCF8AE08093C600D5\r
+:10C340000F90DF91CF9108955A980E94AB168111FA\r
+:10C3500021C0E2E1F0E107C09091C00095FFFCCF61\r
+:10C360008093C600319684918111F6CFE0EAF1E125\r
+:10C3700007C09091C00095FFFCCF8093C600319616\r
+:10C3800084918111F6CF8091C00085FFFCCF8AE0B7\r
+:10C390008093C60008951F920F920FB60F9211243A\r
+:10C3A0002F933F934F935F936F937F938F939F93BD\r
+:10C3B000AF93BF93EF93FF9380915A01811106C011\r
+:10C3C0008091020E80932C0E81115D9A90912C0E1B\r
+:10C3D00080915A01891708F05D988F5F8F7780935D\r
+:10C3E0005A0180912D0E833009F443C020F481302E\r
+:10C3F000D1F0A8F50CC0853009F457C008F453C03B\r
+:10C40000863009F454C0873009F05AC052C01092E7\r
+:10C410007B0087E480937C0080917A008064809325\r
+:10C420007A0081E019C020917800309179008091E4\r
+:10C430002E0E90912F0EA091300EB091310E820FE2\r
+:10C44000931FA11DB11D80932E0E90932F0EA093CC\r
+:10C45000300EB093310E82E080932D0E31C086E411\r
+:10C4600080937C0080917A00806480937A0083E0DE\r
+:10C47000F3CF20917800309179008091320E909125\r
+:10C48000330EA091340EB091350E820F931FA11D73\r
+:10C49000B11D8093320E9093330EA093340EB0935F\r
+:10C4A000350E84E0D9CF85E0D7CF86E0D5CF87E0C1\r
+:10C4B000D3CF10922D0E8091360E8F5F8093360E63\r
+:10C4C0008091360E803108F474C02FEF3FE38091E5\r
+:10C4D0002E0E90912F0EA901481B590BCA015093A3\r
+:10C4E000FD0D4093FC0D4091320E5091330E241BF4\r
+:10C4F000350B3093FB0D2093FA0D21E02093F50DC1\r
+:10C500001092360E10922E0E10922F0E1092300EA8\r
+:10C510001092310E1092370E1092380E1092390E82\r
+:10C5200010923A0E10923B0E10923C0E10923D0E5D\r
+:10C5300010923E0E1092320E1092330E1092340E64\r
+:10C540001092350E209156013091570182179307B2\r
+:10C550004CF01092010E1092000E80E00E942461B7\r
+:10C560000E9470162091FC0D3091FD0D8091240EDB\r
+:10C570009091250E821793074CF01092010E1092A5\r
+:10C58000000E80E00E9464610E9470162091FA0DF6\r
+:10C590003091FB0D8091580190915901281739076E\r
+:10C5A00044F01092FF0D1092FE0D0E94A4610E94B3\r
+:10C5B0007016FF91EF91BF91AF919F918F917F91F5\r
+:10C5C0006F915F914F913F912F910F900FBE0F9000\r
+:10C5D0001F901895CF93DF93FC01148A178A84E883\r
+:10C5E00092E0938B828B13AA16AA92AB81ABDF01E8\r
+:10C5F000A05BBF4F1C9213961C92DF01A25BBF4F42\r
+:10C600008D939C93DF01A359BF4F1C9213961C92EC\r
+:10C61000DF01A559BF4F8D939C93DF01A857BF4FF2\r
+:10C6200029E111962C93119714961C92DF01A357C0\r
+:10C63000BF4F57961C92DF01A355BF4F1C92139614\r
+:10C640001C92DF01A555BF4F8D939C93DF01A8532A\r
+:10C65000BF4F1D921D921D921C921397DF01A05394\r
+:10C66000BF4F1D921D921D921C9213971182128230\r
+:10C670001082EF01C453DF4F188219821A821B8285\r
+:10C68000DF01AC52BF4F81E08C93118A108A0E9467\r
+:10C69000FD2A68577C4E8F4F9F4F688379838A832A\r
+:10C6A0009B83DF91CF910895DB01FC0120E03EE206\r
+:10C6B0004C91403229F0283009F431934C91419348\r
+:10C6C0002F5F11962B30A1F7108208952F923F9281\r
+:10C6D0004F925F926F927F928F929F92AF92BF9292\r
+:10C6E000CF92DF92EF92FF920F931F93CF93DF933E\r
+:10C6F000CDB7DEB7C158D1090FB6F894DEBF0FBE73\r
+:10C70000CDBF8C017B01212C4A0182E0880E911C57\r
+:10C710006801E5EDCE0ED11C5801F6EDAF0EB11C4F\r
+:10C720008EE3482E512C4C0E5D1E82E0480E511CAB\r
+:10C73000EAEDF1E13490F3C09C85292F2871203176\r
+:10C7400009F007C1F6018081813009F402C182300D\r
+:10C7500009F0EFC0FEC061EB72E0CE01855A9F4F39\r
+:10C760000E946475B701CE01855A9F4F0E9464757F\r
+:10C77000BE016B587F4FCE01855A9F4F0E94647552\r
+:10C7800061EB72E0CE01855A9F4F0E94647521963D\r
+:10C790001FAE219724961FAE249784E892E09FAFA6\r
+:10C7A0008EAF21E0AE014B585F4FB401C2010E9431\r
+:10C7B000F034811139C0F6018081811135C0832D9B\r
+:10C7C000EAEDF1E108C09091C00095FFFCCF8093A5\r
+:10C7D000C600319684918111F6CFA3EBB2E006C07A\r
+:10C7E0009091C00095FFFCCF8093C6008D91811180\r
+:10C7F000F7CF8091C00085FFFCCF8AE08093C60010\r
+:10C80000DE01AB58BF4F06C09091C00095FFFCCF32\r
+:10C810008093C6008D918111F7CF8091C00085FF74\r
+:10C82000FCCF8AE08093C6008BE1FE01E05CFF4F05\r
+:10C83000DE01939601900D928A95E1F784E892E0EB\r
+:10C840009AA389A3AE014F5D5F4FBE01655A7F4F2A\r
+:10C85000C8010E946663CE0181960E94842ACE019F\r
+:10C86000CE960E94842A5BC0853E09F458C08E3261\r
+:10C8700009F455C08F3509F452C093FD50C081E0D2\r
+:10C88000203109F080E0F8018787811108C089858F\r
+:10C89000873409F044C08A858E3709F440C03801D6\r
+:10C8A000F3E06F0E711CBE016F5F7F4FC3010E94EA\r
+:10C8B0005463F601808181111EC0D70106C090919A\r
+:10C8C000C00095FFFCCF8093C6008D918111F7CFFA\r
+:10C8D000D30106C09091C00095FFFCCF8093C600A5\r
+:10C8E0008D918111F7CF8091C00085FFFCCF8AE048\r
+:10C8F0008093C60014C0813039F4F50180819181A4\r
+:10C900000196918380830BC0823049F4222D30E060\r
+:10C91000F5018081918128173907F1F02394BE0138\r
+:10C920006F5F7F4FC4010E945E3118160CF404CF74\r
+:10C9300013C0BE016F5F7F4FCE018B589F4F0E9487\r
+:10C9400054636C961FAE6C97F701808181110ACFFA\r
+:10C9500002CF8981811188CFCF57DF4F0FB6F8946E\r
+:10C96000DEBF0FBECDBFDF91CF911F910F91FF9022\r
+:10C97000EF90DF90CF90BF90AF909F908F907F907F\r
+:10C980006F905F904F903F902F900895EF92FF929D\r
+:10C990000F931F93CF93DF93CDB7DEB76D970FB68D\r
+:10C9A000F894DEBF0FBECDBF8C01FC01EB52FF4FF0\r
+:10C9B00010827C0184E1E80EF11C40E050E0BA01F5\r
+:10C9C000C7010E94D1318BE1F701DE01139601907E\r
+:10C9D0000D928A95E1F784E892E09A838983AE010B\r
+:10C9E0004F5F5F4F6DE972E0C8010E946663CE0140\r
+:10C9F00001960E94842A6D960FB6F894DEBF0FBE92\r
+:10CA0000CDBFDF91CF911F910F91FF90EF900895CF\r
+:10CA1000EF92FF920F931F93CF93DF93EC011A8253\r
+:10CA20008F89882321F0CE0144960E94B8327E017E\r
+:10CA300088E8E80EF11C4FE160E0C7010E94686CD5\r
+:10CA400081111FC0EAEDF1E107C09091C00095FF90\r
+:10CA5000FCCF8093C600319684918111F6CFE0EE31\r
+:10CA6000F1E107C09091C00095FFFCCF8093C60014\r
+:10CA7000319684918111F6CF8091C00085FFFCCF63\r
+:10CA800076C08E0103571F4F41E0B701C8010E94D5\r
+:10CA9000ED3A811126C040E0B701C8010E94ED3A8D\r
+:10CAA00081111FC0EDEEF1E107C09091C00095FF2C\r
+:10CAB000FCCF8093C600319684918111F6CFE4EFCC\r
+:10CAC000F1E107C09091C00095FFFCCF8093C600B4\r
+:10CAD000319684918111F6CF8091C00085FFFCCF03\r
+:10CAE00046C0B801CE0144960E94CB2F81111FC0D1\r
+:10CAF000EDEEF1E107C09091C00095FFFCCF80936F\r
+:10CB0000C600319684918111F6CFE7E0F2E107C0CB\r
+:10CB10009091C00095FFFCCF8093C6003196849120\r
+:10CB20008111F6CF8091C00085FFFCCF20C081E04D\r
+:10CB30008A83EAEDF1E107C09091C00095FFFCCF38\r
+:10CB40008093C600319684918111F6CFE7E1F2E13E\r
+:10CB500007C09091C00095FFFCCF8093C60031962E\r
+:10CB600084918111F6CF8091C00085FFFCCF8AE0CF\r
+:10CB70008093C6008BE1FE017496DE01D39601908E\r
+:10CB80000D928A95E1F7CE01429698AB8FA7DF917F\r
+:10CB9000CF911F910F91FF90EF900895FC011182AA\r
+:10CBA00012820895FC012281222311F021E02183C9\r
+:10CBB0000895FC0121812111118208952F923F9245\r
+:10CBC0004F925F926F927F928F929F92AF92BF929D\r
+:10CBD000CF92DF92EF92FF920F931F93CF93DF9349\r
+:10CBE000CDB7DEB7AA970FB6F894DEBF0FBECDBFA4\r
+:10CBF0007C016B01342EFC018281882309F4B4C1CD\r
+:10CC00004701FDEA8F0E911CC4010E94B832F70162\r
+:10CC100011821B821E8284E892E09A838983C70175\r
+:10CC2000429690AB87A7F60180818F3209F08CC0C5\r
+:10CC30006FE270E0C6010E946F758C010F5F1F4F9D\r
+:10CC40009AE0292E44244394512C4C0E5D1E77C04B\r
+:10CC50006FE270E0C8010E946F755C01009709F4F3\r
+:10CC60007BC00817190708F077C03C01601A710AE9\r
+:10CC7000A301B801CE014E960E948F75EEE1F0E05F\r
+:10CC8000EC0FFD1FE60DF71D10828E01025E1F4F97\r
+:10CC9000F80106C09091C00095FFFCCF8093C600BC\r
+:10CCA00081918111F7CF8091C00085FFFCCF209248\r
+:10CCB000C600F70167A570A96115710519F06E5FCF\r
+:10CCC0007F4F02C060E070E021E0AE01425E5F4F46\r
+:10CCD000CE0103960E94F03481112BC0E2E2F2E112\r
+:10CCE00007C09091C00095FFFCCF8093C60031969D\r
+:10CCF00084918111F6CF06C09091C00095FFFCCFC2\r
+:10CD00008093C600F80181918F018111F5CFE6E390\r
+:10CD1000F2E107C09091C00095FFFCCF8093C60060\r
+:10CD2000319684918111F6CF8091C00085FFFCCFB0\r
+:10CD300014C1F70150AA47A685010F5F1F4F0115C7\r
+:10CD4000110509F085CF05C0C701C196F70190AB69\r
+:10CD500087A7BC2CAD2C02C0B02EA12EF70167A571\r
+:10CD600070A9332009F49FC06115710519F06E5F39\r
+:10CD70007F4F02C060E070E021E04B2D5A2DC401CE\r
+:10CD80000E94F034882309F463C0F701E853FF4F91\r
+:10CD9000D701A355BF4F51960D911D912D913C91F7\r
+:10CDA00054970083118322833383E8E3F2E107C0C1\r
+:10CDB0009091C00095FFFCCF8093C600319684917E\r
+:10CDC0008111F6CFEB2DFA2D06C09091C00095FF92\r
+:10CDD000FCCF8093C60081918111F7CFE5E4F2E1A9\r
+:10CDE00007C09091C00095FFFCCF8093C60031969C\r
+:10CDF00084918111F6CFF701E853FF4F40815181B3\r
+:10CE0000628173812AE030E081EC95E00E949C2DE4\r
+:10CE10008091C00085FFFCCF8AE08093C600F701B7\r
+:10CE2000E053FF4F1082118212821382ECE4F2E190\r
+:10CE300007C09091C00095FFFCCF8093C60031964B\r
+:10CE400084918111F6CF8091C00085FFFCCF85C011\r
+:10CE5000EAE5F2E107C09091C00095FFFCCF809316\r
+:10CE6000C600319684918111F6CFEB2DFA2D06C0C4\r
+:10CE70009091C00095FFFCCF8093C60081918111F5\r
+:10CE8000F7CFEEE6F2E107C09091C00095FFFCCF2E\r
+:10CE90008093C600319684918111F6CF8091C000B5\r
+:10CEA00085FFFCCF5AC06115710519F06E5F7F4F89\r
+:10CEB00002C060E070E026E54B2D5A2DC4010E94AF\r
+:10CEC000F03481112BC0E0E7F2E107C09091C0007F\r
+:10CED00095FFFCCF8093C600319684918111F6CFE7\r
+:10CEE000EB2DFA2D06C09091C00095FFFCCF8093EA\r
+:10CEF000C60081918111F7CFE4E8F2E107C090917B\r
+:10CF0000C00095FFFCCF8093C600319684918111BB\r
+:10CF1000F6CF8091C00085FFFCCF1FC081E0F701F4\r
+:10CF20008083E6E8F2E107C09091C00095FFFCCF56\r
+:10CF30008093C600319684918111F6CFF60106C028\r
+:10CF40009091C00095FFFCCF8093C6008191811124\r
+:10CF5000F7CF8091C00085FFFCCF8AE08093C600A8\r
+:10CF6000CE0101960E94842AAA960FB6F894DEBFDD\r
+:10CF70000FBECDBFDF91CF911F910F91FF90EF902A\r
+:10CF8000DF90CF90BF90AF909F908F907F906F90E9\r
+:10CF90005F904F903F902F9008955F926F927F9295\r
+:10CFA0008F929F92AF92BF92CF92DF92EF92FF92B9\r
+:10CFB0000F931F93CF93DF93CDB7DEB7AA970FB62A\r
+:10CFC000F894DEBF0FBECDBF8C016B01FC018281E6\r
+:10CFD000882309F408C1C80183559F4F0E94B832C5\r
+:10CFE000F80111821B821E8284E892E09A83898371\r
+:10CFF000C801429690AB87A7F60180818F3209F075\r
+:10D000008EC06FE270E0C6010E946F7501967C01D0\r
+:10D010004AE0542E66246394712C6C0E7D1E7AC0F7\r
+:10D020006FE270E0C7010E946F755C01009709F420\r
+:10D030007CC0E816F90608F078C04C018E189F08ED\r
+:10D04000A401B701CE014E960E948F75EEE1F0E08B\r
+:10D05000EC0FFD1FE80DF91D10823EE1E32EF12CCF\r
+:10D06000EC0EFD1EF70106C09091C00095FFFCCFAD\r
+:10D070008093C60081918111F7CF8091C00085FF18\r
+:10D08000FCCF5092C600F80167A570A96115710523\r
+:10D0900019F06E5F7F4F02C060E070E021E0AE01EA\r
+:10D0A000425E5F4FCE0103960E94F03481112BC087\r
+:10D0B000E8E9F2E107C09091C00095FFFCCF8093B2\r
+:10D0C000C600319684918111F6CF06C09091C000C0\r
+:10D0D00095FFFCCF8093C600F70181917F018111FC\r
+:10D0E000F5CFECEAF2E107C09091C00095FFFCCFCC\r
+:10D0F0008093C600319684918111F6CF8091C00053\r
+:10D1000085FFFCCF69C0F80170AA67A67501FFEF23\r
+:10D11000EF1AFF0AE114F10409F082CF05C0C8013B\r
+:10D12000C196F80190AB87A77601F80187A590A971\r
+:10D13000009711F0029602C080E090E0B7010E94D3\r
+:10D14000AD35882301F1EEEAF2E107C09091C0000D\r
+:10D1500095FFFCCF8093C600319684918111F6CF64\r
+:10D16000F70106C09091C00095FFFCCF8093C600E8\r
+:10D1700081918111F7CFF801E053FF4F10821182A6\r
+:10D18000128213822CC0ECEBF2E107C09091C00038\r
+:10D1900095FFFCCF8093C600319684918111F6CF24\r
+:10D1A000F70106C09091C00095FFFCCF8093C600A8\r
+:10D1B00081918111F7CFE4EDF2E107C09091C000B9\r
+:10D1C00095FFFCCF8093C600319684918111F6CFF4\r
+:10D1D0008091C00085FFFCCF8AE08093C600CE011D\r
+:10D1E00001960E94842AAA960FB6F894DEBF0FBE5D\r
+:10D1F000CDBFDF91CF911F910F91FF90EF90DF9006\r
+:10D20000CF90BF90AF909F908F907F906F905F90E6\r
+:10D210000895CF93DF93EC018A818823C1F1E6ED75\r
+:10D22000F2E107C09091C00095FFFCCF8093C6004B\r
+:10D23000319684918111F6CFFE01E053FF4F40817A\r
+:10D240005181628173812AE030E081EC95E00E9497\r
+:10D250009C2DE8EEF2E107C09091C00095FFFCCF55\r
+:10D260008093C600319684918111F6CFC853DF4F69\r
+:10D27000488159816A817B812AE030E081EC95E028\r
+:10D280000E949C2D8091C00085FFFCCF11C0EAEE6A\r
+:10D29000F2E107C09091C00095FFFCCF8093C600DB\r
+:10D2A000319684918111F6CF8091C00085FFFCCF2B\r
+:10D2B0008AE08093C600DF91CF910895CF92DF92EC\r
+:10D2C000EF92FF920F931F93CF93DF937C01EB01BB\r
+:10D2D000EC2FFD2FDF010D900020E9F78D010150AB\r
+:10D2E00011090E1B1F0BF701E355FF4F10826EE46F\r
+:10D2F00070E0CE010E946F756C01009729F4F8016F\r
+:10D300003197EC0FFD1F0DC060E270E00E946F7559\r
+:10D31000EC0121966AE270E0C6010E946F75FC0183\r
+:10D3200031978DE081838AE082831382BE01C70139\r
+:10D3300085559F4F0E947137F701E355FF4F80815C\r
+:10D34000882309F1EDEEF1E107C09091C00095FF4F\r
+:10D35000FCCF8093C600319684918111F6CFEAEF1D\r
+:10D36000F2E107C09091C00095FFFCCF8093C6000A\r
+:10D37000319684918111F6CF8091C00085FFFCCF5A\r
+:10D380008AE08093C600DF91CF911F910F91FF90AB\r
+:10D39000EF90DF90CF9008953F924F925F926F92FF\r
+:10D3A0007F928F929F92AF92BF92CF92DF92EF9235\r
+:10D3B000FF920F931F93CF93DF93CDB7DEB7CC557A\r
+:10D3C000D1090FB6F894DEBF0FBECDBF8C01662326\r
+:10D3D00049F0F801EC52FF4F1082F801828181116F\r
+:10D3E00018C030C0FC01EC52FF4F8081882309F443\r
+:10D3F000AEC0F801E453FF4FC080D180E280F380DB\r
+:10D400000E94FD2AC616D706E806F90608F49FC052\r
+:10D41000E0CFF80181898F9380898F9386EC92E029\r
+:10D420009F938F937E01FFE3EF0EF11CFF92EF922B\r
+:10D430000E94B8750F900F900F900F900F900F9063\r
+:10D44000670110C0C8010E940865F801828181113E\r
+:10D45000E0CF7DC0F701808190E00E942E75F7013A\r
+:10D4600081937F01F60101900020E9F73197EC19D3\r
+:10D47000FD09C7018C199D098E179F075CF33801C0\r
+:10D48000F4E16F0E711C40E050E0BA01C3010E944C\r
+:10D49000D131312CEE24E394F12CEC0EFD1E5E0113\r
+:10D4A0008FE3A80EB11C7FEC472E72E0572E4E0181\r
+:10D4B000E1E28E0E911C37C0F601808190E00E945F\r
+:10D4C0002E75F60181936F01F70101900020E9F7B5\r
+:10D4D0003197EE19FF09C6018E199F098E179F0714\r
+:10D4E0005CF38A858E37F9F045E050E0B501C7015D\r
+:10D4F0000E9481750097B9F4BF92AF925F924F92EC\r
+:10D500009F928F920E94B875C4010E94B80E86ED5A\r
+:10D5100092E00E94B80E0F900F900F900F900F9016\r
+:10D520000F9033243394B701C3010E945E31181663\r
+:10D5300014F46701C9CF311004C08FEF9FEFF801D9\r
+:10D5400004C0F801808991890196918B808BC45A1F\r
+:10D55000DF4F0FB6F894DEBF0FBECDBFDF91CF9186\r
+:10D560001F910F91FF90EF90DF90CF90BF90AF9001\r
+:10D570009F908F907F906F905F904F903F90089515\r
+:10D580000F931F93CF93DF938C01EC01C355DF4FB3\r
+:10D59000CE010E946C32CE010E94B832F801108296\r
+:10D5A000DF91CF911F910F910895CF93DF93EC01FD\r
+:10D5B0000E94AC570E94215819828AED92E00E9485\r
+:10D5C000B80E8091260D8823A1F11092260D60914E\r
+:10D5D000E802E62FF0E0EE0FFF1FE050F24F8081EF\r
+:10D5E00091810E9431592091390130913A01409145\r
+:10D5F0003B0150913C010E9485701816D4F4609153\r
+:10D60000E802C62FD0E080E090E00E946158FE0161\r
+:10D61000EE0FFF1FE050F24F91838083FE01EE0F6B\r
+:10D62000FF1FEE0FFF1FEA50F24F1082118212828D\r
+:10D630001382DF91CF910895EF92FF920F931F9382\r
+:10D64000CF93DF937C01EB018A01060F171F09C0FE\r
+:10D650006991D701ED91FC910190F081E02DC70116\r
+:10D660000995C017D107A1F7DF91CF911F910F91B5\r
+:10D67000FF90EF9008958FEF8EBD0DB407FEFDCFA4\r
+:10D680008EB508958EBD0DB407FEFDCF089561E0FF\r
+:10D69000FC0180810C94412CFC014281242F30E05C\r
+:10D6A0003595279520652CBD40FD03C021E046300F\r
+:10D6B00009F420E02DBD60E0FC0180810C94412C38\r
+:10D6C000CF92DF92EF92FF920F931F93CF93DF934E\r
+:10D6D000EC018B017A010E94FD2A6B0109C00E94B6\r
+:10D6E000FD2A6C197D096D32714010F081E108C08E\r
+:10D6F0000E943B6B8B838F3F91F38E3F19F08FE03D\r
+:10D70000898328C0E114F104E1F081E0E81AF1080E\r
+:10D710008FEF8EBDF8012FEF06C00DB407FEFDCFD1\r
+:10D720008EB581932EBDCF01801B910B8E159F0569\r
+:10D73000A0F30DB407FEFDCF8EB5F801EE0DFF1D71\r
+:10D7400080830E943B6B0E943B6BCE010E94476B23\r
+:10D7500081E004C0CE010E94476B80E0DF91CF9151\r
+:10D760001F910F91FF90EF90DF90CF9008950F934E\r
+:10D770001F93CF93DF938B010E94FD2AEB0107C01B\r
+:10D780000E94FD2A6C1B7D0B6017710730F40E940C\r
+:10D790003B6B8F3FA9F781E001C080E0DF91CF9123\r
+:10D7A0001F910F910895CF92DF92FF920F931F93D5\r
+:10D7B000CF93DF9300D000D0CDB7DEB78C01F62E2B\r
+:10D7C00029833A834B835C830E944C6B6CE271E04B\r
+:10D7D000C8010E94B76B8F2D80640E94426B68E184\r
+:10D7E000C62ED12C5C814B813A812981DA01C90195\r
+:10D7F0000C2C04C0B695A795979587950A94D2F7F7\r
+:10D8000029833A834B835C830E94426B68E0C61A8B\r
+:10D81000D10829813A814B815C8188EFC8168FEF4E\r
+:10D82000D80621F7FF2029F098E0F91621F08FEFB4\r
+:10D8300003C085E901C087E80E94426BECE0FE125C\r
+:10D8400002C00E943B6BF12C0E943B6BF80183836A\r
+:10D8500087FF05C0FFEFFF1611F0F394F5CF0F908F\r
+:10D860000F900F900F90DF91CF911F910F91FF902C\r
+:10D87000DF90CF9008950F931F93CF93DF93EC0128\r
+:10D8800089018C81833039F0B9E0440F551F661F40\r
+:10D89000771FBA95D1F79A01AB0161E1CE010E94E1\r
+:10D8A000D36B81110AC040E052E0B801CE01DF9194\r
+:10D8B000CF911F910F910C94606B84E08983CE010E\r
+:10D8C0000E94476B80E0DF91CF911F910F910895E7\r
+:10D8D000BF92CF92DF92EF92FF920F931F93CF935D\r
+:10D8E000DF93EC01B62E1C82198248830E94FD2A28\r
+:10D8F0008B0161E088810E94252CCE010E94476B3C\r
+:10D9000060E086E00E94252C61E085E00E94252CE5\r
+:10D9100061E087E00E94252C61E084E00E94252CD4\r
+:10D9200061E084E00E94412C85E08A8382E58CBD21\r
+:10D930001DBC2AE0F22E8FEF0E94426BFA94D9F7B9\r
+:10D9400009C00E94FD2A601B710B613D774010F0F9\r
+:10D9500081E058C020E030E0A90160E0CE010E94E3\r
+:10D96000D36BF82E8B8381E0F812EBCF2AEA31E0FB\r
+:10D9700040E050E068E0CE010E94D36B82FF02C01D\r
+:10D98000FC820DC094E0F92E0E943B6B8B83FA94CD\r
+:10D99000D9F78A3A11F082E035C082E08C838C811D\r
+:10D9A000823031F4C12CD12CE12C80E4F82E0DC052\r
+:10D9B000C12CD12C760109C00E94FD2A601B710B7D\r
+:10D9C000613D774010F08AE01DC020E030E0A90101\r
+:10D9D00067E3CE010E94D36BA701960169E2CE01F5\r
+:10D9E0000E94D36B8B838111E7CF8C818230C9F485\r
+:10D9F00020E030E0A9016AE3CE010E94D36B8823C6\r
+:10DA000019F088E0898319C00E943B6B807C803CC0\r
+:10DA100011F483E08C830E943B6B0E943B6B0E945D\r
+:10DA20003B6BCE010E94476B86E08B1518F488E1B2\r
+:10DA3000898306C0BA8281E004C0CE010E94476B90\r
+:10DA400080E0DF91CF911F910F91FF90EF90DF90D9\r
+:10DA5000CF90BF900895CF93DF93EC016EBD20E08F\r
+:10DA600030E00DB407FEFDCFFA01E20FF31F808115\r
+:10DA70008EBD0DB407FEFDCF81818EBD2E5F3F4F61\r
+:10DA8000211582E0380769F70DB407FEFDCF8FEF4F\r
+:10DA90000E94426B8FEF0E94426B0E943B6B8B8314\r
+:10DAA0008F71853039F083E18983CE010E94476B05\r
+:10DAB00080E001C081E0DF91CF9108950F931F9323\r
+:10DAC000CF93DF93EC0189018C81833039F0E9E059\r
+:10DAD000440F551F661F771FEA95D1F79A01AB01D6\r
+:10DAE00068E1CE010E94D36B882311F086E029C043\r
+:10DAF000A8016EEFCE010E942B6D8823B9F068E576\r
+:10DB000072E0CE010E94B76B811102C087E119C09B\r
+:10DB100020E030E0A9016DE0CE010E94D36B8111BD\r
+:10DB20000FC00E943B6B81110BC005C0CE010E944B\r
+:10DB3000476B80E008C0CE010E94476B81E003C0C4\r
+:10DB400086E18983F3CFDF91CF911F910F910895E3\r
+:10DB50005058BB27AA270ED076C23FD230F044D20D\r
+:10DB600020F031F49F3F11F41EF40FC20EF4E09543\r
+:10DB7000E7FBDCC1E92F89D280F3BA17620773078C\r
+:10DB80008407950718F071F49EF5B8C20EF4E0957D\r
+:10DB90000B2EBA2FA02D0B01B90190010C01CA0167\r
+:10DBA000A0011124FF27591B99F0593F50F4503E12\r
+:10DBB00068F11A16F040A22F232F342F4427585F04\r
+:10DBC000F3CF469537952795A795F0405395C9F71C\r
+:10DBD0007EF41F16BA0B620B730B840BBAF09150D4\r
+:10DBE000A1F0FF0FBB1F661F771F881FC2F70EC073\r
+:10DBF000BA0F621F731F841F48F487957795679546\r
+:10DC0000B795F7959E3F08F0B3CF9395880F08F02E\r
+:10DC10009927EE0F979587950895DFD158F080E802\r
+:10DC200091E009F49EEFE0D128F040E851E059F48A\r
+:10DC30005EEF09C0AAC162C2E92FE07826D268F37C\r
+:10DC4000092E052AC1F3261737074807590738F068\r
+:10DC50000E2E07F8E02569F0E025E0640AC0EF63C6\r
+:10DC600007F8009407FADB01B9019D01DC01CA0144\r
+:10DC7000AD01EF935DD0E7D10AD05F91552331F02C\r
+:10DC80002BED3FE049E450FD49EC63CF0895DF936D\r
+:10DC9000DD27B92FBF7740E85FE316161706480760\r
+:10DCA0005B0710F4D92F96D29F938F937F936F9336\r
+:10DCB000A9D3ECE7F0E06CD1C6D12F913F914F9101\r
+:10DCC0005F9101D3DD2349F09058A2EA2AED3FE0AD\r
+:10DCD00049EC5FE3D0785D274DDFDF91B4C1F7D128\r
+:10DCE00080F09F3740F491110EF409C260E070E0BB\r
+:10DCF00080E89FE3089526F01B16611D711D811DAC\r
+:10DD00001BC135C1EFD008F481E0089575D1E395CA\r
+:10DD1000ABC10CD098C168D140F05FD130F021F494\r
+:10DD20005F3F19F003C15111EAC12FC1AED198F381\r
+:10DD30009923C9F35523B1F3951B550BBB27AA278C\r
+:10DD400062177307840738F09F5F5F4F220F331FFE\r
+:10DD5000441FAA1FA9F333D00E2E3AF0E0E830D0CA\r
+:10DD600091505040E695001CCAF729D0FE2F27D0CD\r
+:10DD7000660F771F881FBB1F261737074807AB079B\r
+:10DD8000B0E809F0BB0B802DBF01FF2793585F4F10\r
+:10DD90002AF09E3F510568F0C9C0B1C15F3FECF366\r
+:10DDA000983EDCF3869577956795B795F7959F5FD5\r
+:10DDB000C9F7880F911D9695879597F90895E1E029\r
+:10DDC000660F771F881FBB1F621773078407BA0788\r
+:10DDD00020F0621B730B840BBA0BEE1F88F7E095E3\r
+:10DDE000089504D06894B1118AC1089556D188F07D\r
+:10DDF0009F5790F0B92F9927B751A0F0D1F0660F37\r
+:10DE0000771F881F991F1AF0BA95C9F712C0B13051\r
+:10DE100081F074D1B1E0089571C1672F782F882700\r
+:10DE2000B85F39F0B93FCCF3869577956795B39590\r
+:10DE3000D9F73EF490958095709561957F4F8F4FFF\r
+:10DE40009F4F0895E89409C097FB3EF49095809504\r
+:10DE5000709561957F4F8F4F9F4F9923A9F0F92FB0\r
+:10DE600096E9BB279395F695879577956795B7952E\r
+:10DE7000F111F8CFFAF4BB0F11F460FF1BC06F5F14\r
+:10DE80007F4F8F4F9F4F16C0882311F096E911C026\r
+:10DE9000772321F09EE8872F762F05C0662371F047\r
+:10DEA00096E8862F70E060E02AF09A95660F771F5B\r
+:10DEB000881FDAF7880F9695879597F9089507D107\r
+:10DEC00080F09F3740F491110EF019C160E070E0CE\r
+:10DED00080E89FEB089526F41B16611D711D811DBE\r
+:10DEE0002BC045C0990F0008550FAA0BE0E8FEEFC4\r
+:10DEF00016161706E807F907C0F012161306E4070E\r
+:10DF0000F50798F0621B730B840B950B39F40A2606\r
+:10DF100061F0232B242B252B21F408950A2609F4E4\r
+:10DF2000A140A6958FEF811D811D089597F99F67E8\r
+:10DF300080E870E060E00895882371F4772321F091\r
+:10DF40009850872B762F07C0662311F499270DC0B0\r
+:10DF50009051862B70E060E02AF09A95660F771F4B\r
+:10DF6000881FDAF7880F9695879597F908959F3F50\r
+:10DF700031F0915020F4879577956795B795880F84\r
+:10DF8000911D9695879597F908959FEF80EC0895D8\r
+:10DF9000DF93CF931F930F93FF92EF92DF927B015A\r
+:10DFA0008C01689405C0DA2EEF018DD1FE01E89452\r
+:10DFB000A5912591359145915591AEF3EF01DADDAB\r
+:10DFC000FE019701A801DA9479F7DF90EF90FF90B6\r
+:10DFD0000F911F91CF91DF91089500240A94161696\r
+:10DFE000170618060906089500240A941216130647\r
+:10DFF000140605060895C9CF50D0E8F3E894E0E090\r
+:10E00000BB279F57F0F02AED3FE049EC06C0EE0F2A\r
+:10E01000BB0F661F771F881F28F0B23A620773078D\r
+:10E02000840728F0B25A620B730B840BE3959A9520\r
+:10E0300072F7803830F49A95BB0F661F771F881FE0\r
+:10E04000D2F7904896CF092E0394000C11F4882340\r
+:10E0500052F0BB0F40F4BF2B11F460FF04C06F5FA0\r
+:10E060007F4F8F4F9F4F0895EF93E0FF06C0A2EAC6\r
+:10E070002AED3FE049EC5FEB7DDDE5DF0F90039497\r
+:10E0800001FC9058E9EAF0E0C7C157FD9058440FF1\r
+:10E09000551F59F05F3F71F04795880F97FB991F07\r
+:10E0A00061F09F3F79F087950895121613061406C4\r
+:10E0B000551FF2CF4695F1DF08C016161706180651\r
+:10E0C000991FF1CF86957105610508940895E5DFE4\r
+:10E0D000A0F0BEE7B91788F4BB279F3860F4161686\r
+:10E0E000B11D672F782F8827985FF7CF8695779592\r
+:10E0F0006795B11D93959639C8F30895E894BB27A9\r
+:10E1000066277727CB0197F90895ECDE08F48FEFA7\r
+:10E11000089563DF19F068DF09F037CF07CFB90141\r
+:10E12000CA0125CF9F775F77B0DF98F39923B9F3C2\r
+:10E130005523B9F3FF27951758F4E52FE91BED3068\r
+:10E1400070F75E3B10F0F1E41CC09034E0F40AC0BC\r
+:10E15000E92FE51BED3028F79E3B10F0F1E411C0EC\r
+:10E16000503488F4F9EA88232AF09A95660F771FCD\r
+:10E17000881FDAF744232AF05A95220F331F441FD1\r
+:10E18000DAF79F1B5F1BFF931F930F93FF92EF9292\r
+:10E1900079018A01BB27AB2F9B01AC0196D0970177\r
+:10E1A000A801BF937B018C01AA27BA2FB901CA012C\r
+:10E1B0008CD0AF919701A801EF90FF900F911F9124\r
+:10E1C000D9DC41DFE1D04F9140FF0895552747FD4D\r
+:10E1D000509509C09B01AC0160E070E080E89FE3CE\r
+:10E1E00098CDA4CEC4CE59DFE8F39923D9F3940F88\r
+:10E1F000511DBBF39150504094F059F0882332F0F8\r
+:10E20000660F771F881F91505040C1F79E3F510500\r
+:10E2100044F7880F911D9695879597F908955F3F6C\r
+:10E22000ACF0983E9CF0BB27869577956795B7959F\r
+:10E2300008F4B1609395C1F7BB0F58F711F460FF74\r
+:10E24000E8CF6F5F7F4F8F4F9F4FE3CF58CF25DFD2\r
+:10E2500058F19E5758F19851A0F0E9F0983020F508\r
+:10E26000092E9927660F771F881F991F0A94D1F7E7\r
+:10E2700012C0062E672F782F8827985F11F4000CA4\r
+:10E2800007C0993FB4F38695779567959395D9F72D\r
+:10E29000611D711D811D3EF490958095709561956D\r
+:10E2A0007F4F8F4F9F4F0895689429CF27CF0BD072\r
+:10E2B000CACE93DE28F098DE18F0952309F036CE0A\r
+:10E2C00064CE11241CCFE1DEA0F3959FD1F3950F0E\r
+:10E2D00050E0551F629FF001729FBB27F00DB11DEA\r
+:10E2E000639FAA27F00DB11DAA1F649F6627B00D7A\r
+:10E2F000A11D661F829F2227B00DA11D621F739F63\r
+:10E30000B00DA11D621F839FA00D611D221F749F70\r
+:10E310003327A00D611D231F849F600D211D822FB7\r
+:10E32000762F6A2F11249F5750408AF0E1F08823FE\r
+:10E330004AF0EE0FFF1FBB1F661F771F881F91500B\r
+:10E340005040A9F79E3F510570F0F0CDD8CE5F3F09\r
+:10E35000ECF3983EDCF3869577956795B795F7953E\r
+:10E36000E7959F5FC1F7FE2B880F911D96958795C6\r
+:10E3700097F908959F9340DE0F9007FCEE5F74CEEF\r
+:10E3800011F40EF402CEF3CD88DED0F39923D9F345\r
+:10E39000CEF39F57550B87FF38D00024A0E640EA04\r
+:10E3A000900180585695979528F4805C660F771FEA\r
+:10E3B000881F20F026173707480730F4621B730BBD\r
+:10E3C000840B202931294A2BA695179407942025E0\r
+:10E3D00031254A2758F7660F771F881F20F0261728\r
+:10E3E0003707480730F4620B730B840B200D311D87\r
+:10E3F000411DA09581F7B901842F9158880F9695FA\r
+:10E40000879508959B01AC0152CF91505040660F03\r
+:10E41000771F881FD2F708959F938F937F936F93F1\r
+:10E42000FF93EF939B01AC0142DFEF91FF91B0DDD1\r
+:10E430002F913F914F915F913ACF0E949272A59F89\r
+:10E44000900DB49F900DA49F800D911D11240895EF\r
+:10E45000B7FF0C941D720E941D72821B930B0895CE\r
+:10E46000DB018F939F930E941D72BF91AF91A29F7A\r
+:10E47000800D911DA39F900DB29F900D11240895C2\r
+:10E4800097FB072E16F4009407D077FD09D00E9461\r
+:10E490009E7207FC05D03EF4909581959F4F08959C\r
+:10E4A000709561957F4F0895A1E21A2EAA1BBB1BA0\r
+:10E4B000FD010DC0AA1FBB1FEE1FFF1FA217B30750\r
+:10E4C000E407F50720F0A21BB30BE40BF50B661F66\r
+:10E4D000771F881F991F1A9469F76095709580952A\r
+:10E4E00090959B01AC01BD01CF010895052E97FBCE\r
+:10E4F00016F4009407D057FD0DD00E94547207FC0B\r
+:10E5000009D07EF490958095709561957F4F8F4FDF\r
+:10E510009F4F089550954095309521953F4F4F4F0F\r
+:10E520005F4F0895A29FB001B39FC001A39F01D088\r
+:10E53000B29F700D811D1124911D0895AA1BBB1B54\r
+:10E5400051E107C0AA1FBB1FA617B70710F0A61BF3\r
+:10E55000B70B881F991F5A95A9F780959095BC0114\r
+:10E56000CD010895EE0FFF1F0590F491E02D099461\r
+:10E57000A0E0B0E0EEEBF2E70C9478788B01611547\r
+:10E58000710519F0FB01808391837C01F701C19033\r
+:10E590007F01EF018C2D90E00E942675892BB1F749\r
+:10E5A000FDE2CF1204C0C990DD24D39405C02BE254\r
+:10E5B000C21201C0C990D12C7E0141E0E41AF108D9\r
+:10E5C00043E050E06AE773E1C7010E943675892B8A\r
+:10E5D000E9F47E0182E0E80EF11C45E050E06DE7D1\r
+:10E5E00073E1C7010E943675892B21F47E01E7E0B3\r
+:10E5F000EE0EF11C0115110519F0F801F182E0820F\r
+:10E6000010E0D0E0C0E8D110FBC0FFC043E050E014\r
+:10E6100062E873E1C7010E943675892B49F4011540\r
+:10E62000110509F4EFC02296F801D183C083EAC036\r
+:10E6300060E070E0CB01E12CF12CEC2DE053EA30EE\r
+:10E6400048F5FD2DF2602D2D2870D2FE06C0211157\r
+:10E6500027C02FEFE21AF20A23C0222319F041E06B\r
+:10E66000E41AF108A5E0B0E09B01AC010E941D7224\r
+:10E67000660F771F881F991F6E0F711D811D911DD9\r
+:10E680006839E9E97E078E07E9E19E0748F0FD2D2C\r
+:10E69000F66006C0EE3F39F4D3FC3DC0FD2DF860B6\r
+:10E6A000C990DF2ECACFE53311F0E531A1F5A8817D\r
+:10E6B000AD3219F4F0E1DF2A06C0AB3221F0219629\r
+:10E6C00021E030E004C0A981229622E030E0A0538E\r
+:10E6D000AA3018F0C21BD30B1EC0FE0120E030E0B0\r
+:10E6E00020384CE034075CF4A901440F551F440F57\r
+:10E6F000551F240F351F220F331F2A0F311DA191E3\r
+:10E70000A053EF01AA3060F3D4FE03C031952195E8\r
+:10E710003109E20EF31ED1FE07C00115110521F0EB\r
+:10E720002197F801D183C0830E94226F2D2D237081\r
+:10E73000233029F0162FD72FC82F092F07C0DC014F\r
+:10E74000CB01B058182FD92FCA2F0B2F20E030E063\r
+:10E75000A901612F7D2F8C2F902F0E94826E88231C\r
+:10E7600009F454C0F7FE08C0F194E194F10831EBCC\r
+:10E77000C32E33E1D32E04C029E9C22E23E1D22EC9\r
+:10E780004601F8E18F1A910890E2A92EB12C15C02C\r
+:10E79000F6014591559165917491242F352F462F9F\r
+:10E7A000572F612F7D2F8C2F902F0E945771162F7E\r
+:10E7B000D72FC82F092FEA18FB08EA14FB0444F7E7\r
+:10E7C00024E0C21AD108B594A794C814D904A9F7B3\r
+:10E7D000612F7D2F8C2F902F282F220F292F221F62\r
+:10E7E0002F3F39F020E030E0A9010E94826E8111B4\r
+:10E7F0000DC082E290E09093440E8093430E06C0D9\r
+:10E800000FEF04C010E0D0E0C0EC0FE7612F7D2FC8\r
+:10E810008C2F902FCDB7DEB7ECE00C949478A0E06D\r
+:10E82000B0E0E5E1F4E70C9474782B018A016115FE\r
+:10E83000710519F0FB01808391830115110539F0F1\r
+:10E840009801225031092332310508F0E6C07C01DD\r
+:10E85000F701C1917F013F018C2F90E00E94267546\r
+:10E86000892BB1F7CD3229F4F701C1913F01D1E0F5\r
+:10E8700006C0CB3219F4F701C1913F01D0E0011578\r
+:10E88000110509F4D8C00031110581F4D0C0F3019D\r
+:10E890008081883719F0883509F0C3C0F301C18140\r
+:10E8A000F2E06F0E711CD26000E110E0083011053B\r
+:10E8B000D9F024F402301105F9F407C00A3011052B\r
+:10E8C00051F000311105C1F425C0C12CD12CE12C2F\r
+:10E8D00030E4F32E24C00AE010E02CECC22EDC2C35\r
+:10E8E000EC2C2CE0F22E1BC008E010E0C12CD12C47\r
+:10E8F000E12C90E1F92E13C09801442737FD409593\r
+:10E90000542F60E070E080E090E80E94547269014A\r
+:10E910007A0105C0C12CD12CE12C88E0F82E40E012\r
+:10E9200060E070E0CB014801AA2497FCA094BA2CC7\r
+:10E93000EC2FE053EA3060F02C2F21542A3110F4F0\r
+:10E94000E75006C02C2F21562A3128F5EC2FE75529\r
+:10E950002E2F30E020173107F4F447FD18C0C616FB\r
+:10E96000D706E806F90680F09B01AC01C501B401A9\r
+:10E970000E9430726E0F711D811D911D61307105F5\r
+:10E98000810520E8920710F04FEF01C041E0F3014C\r
+:10E99000C1913F01CDCF4114510491F0442339F08E\r
+:10E9A000F1E06F1A7108F2017182608209C0D1FF33\r
+:10E9B0001BC0F2E06F1A7108F2017182608214C00C\r
+:10E9C00047FF12C0D0FF05C060E070E080E090E833\r
+:10E9D00004C06FEF7FEF8FEF9FE722E230E03093CC\r
+:10E9E000440E2093430E16C0D0FF08C0909580952A\r
+:10E9F000709561957F4F8F4F9F4F0CC097FF0AC056\r
+:10EA000082E290E09093440E8093430E6FEF7FEF8D\r
+:10EA10008FEF9FE76B017C0112C0C12CD12C7601D6\r
+:10EA20000EC0C0E30115110509F45ECF3FCFC0331E\r
+:10EA300009F03CCF2CCFC03309F04DCF28CFB60121\r
+:10EA4000C701CDB7DEB7E0E10C9490789111A7C271\r
+:10EA5000803219F089508550D0F7089591110895AA\r
+:10EA600081548A5108F4805E855A0895FB01DC01C7\r
+:10EA70004150504088F08D9181341CF08B350CF45E\r
+:10EA8000805E659161341CF06B350CF4605E861B12\r
+:10EA9000611171F3990B0895881BFCCFFB01DC0118\r
+:10EAA00004C08D910190801921F441505040C8F765\r
+:10EAB000881B990B0895FB01DC0102C001900D92A7\r
+:10EAC00041505040D8F70895FB01DC010D90002023\r
+:10EAD000E9F7119701900D920020E1F70895FC01EC\r
+:10EAE0008191861721F08823D9F7992708953197C6\r
+:10EAF000CF010895FB01DC0101900D920020E1F7A8\r
+:10EB00000895FB01DC014150504030F08D9101909F\r
+:10EB1000801919F40020B9F7881B990B0895FB019F\r
+:10EB2000DC014150504048F001900D920020C9F79F\r
+:10EB300001C01D9241505040E0F70895FB015191F2\r
+:10EB40005523A9F0BF01DC014D9145174111E1F7B3\r
+:10EB500059F4CD010190002049F04D91401541112B\r
+:10EB6000C9F3FB014111EFCF81E090E001970895D7\r
+:10EB7000A0E1B0E0EEEBF5E70C9480780F89188DFA\r
+:10EB800086E08C831A8309838FEF9FE79E838D83B2\r
+:10EB9000AE01455E5F4F588B4F87698D7A8DCE01F0\r
+:10EBA00001960E94DC75EF81F885E00FF11F10825D\r
+:10EBB0006096E4E00C949C78ACE0B0E0E2EEF5E71F\r
+:10EBC0000C9472787C016B018A01FC011782168219\r
+:10EBD000838181FFC7C188248394912C8C0E9D1E54\r
+:10EBE000F7019381F60193FD859193FF81916F0168\r
+:10EBF000882309F4B3C1853239F493FD859193FFDD\r
+:10EC000081916F01853229F4B70190E00E94E87785\r
+:10EC1000E7CF712C312C20E02032A8F48B3261F048\r
+:10EC200028F4803251F0833271F40BC08D3239F008\r
+:10EC3000803349F4216028C02260246025C0286008\r
+:10EC400023C0206121C027FD27C0382F30533A3020\r
+:10EC500078F426FF06C0FAE07F9E300D1124732E53\r
+:10EC600013C08AE0389E300D1124332E20620CC070\r
+:10EC70008E3221F426FD72C1206406C08C3611F458\r
+:10EC8000206802C0883641F4F60193FD859193FF18\r
+:10EC900081916F018111C0CF982F9554933018F056\r
+:10ECA0009052933028F40C5F1F4FFFE3F9830DC09F\r
+:10ECB000833631F0833771F0833509F05EC023C0AD\r
+:10ECC000F801808189830E5F1F4F66246394712C45\r
+:10ECD000540115C02801F2E04F0E511CF801A0802C\r
+:10ECE000B18026FF03C0672D70E002C06FEF7FEF99\r
+:10ECF000C5012C870E94DD773C0182012C856FE7DE\r
+:10ED0000262E222218C02801F2E04F0E511CF801D5\r
+:10ED1000A080B18026FF03C0672D70E002C06FEFB6\r
+:10ED20007FEFC5012C870E94D2773C012C8550E8EB\r
+:10ED3000252E222A820123FC1CC006C0B70180E2D6\r
+:10ED400090E00E94E8773A94832D90E06816790667\r
+:10ED5000A8F30FC0F50127FC859127FE81915F0183\r
+:10ED6000B70190E00E94E87731103A94F1E06F1A11\r
+:10ED700071086114710471F7EEC0843611F08936A0\r
+:10ED800041F5F80127FF07C060817181828193817D\r
+:10ED90000C5F1F4F08C060817181882777FD8095C7\r
+:10EDA000982F0E5F1F4F4FE6642E622297FF09C017\r
+:10EDB00090958095709561957F4F8F4F9F4FF0E8AC\r
+:10EDC0006F2A2AE030E0A4010E941478A82EA81827\r
+:10EDD00044C0853731F43FEEB32EB2222AE030E052\r
+:10EDE00024C099EFB92EB2228F36B9F020F48835BD\r
+:10EDF00009F0B4C00DC0803721F0883709F0AEC0EB\r
+:10EE000002C020E1B22AB4FE0BC084E0B82A08C0D8\r
+:10EE100024FF09C0E6E0BE2A06C028E030E005C0B5\r
+:10EE200020E130E002C020E132E0F801B7FE07C087\r
+:10EE300060817181828193810C5F1F4F06C0608168\r
+:10EE4000718180E090E00E5F1F4FA4010E94147852\r
+:10EE5000A82EA8188FE7682E6B2066FE0BC0362DF3\r
+:10EE60003E7FA71450F464FE0AC062FC08C0362D31\r
+:10EE70003E7E05C0BA2C362D03C0BA2C01C0B72C7B\r
+:10EE800034FF0DC0FE01EA0DF11D8081803311F4C5\r
+:10EE9000397E09C032FF06C0B394B39404C0832FF7\r
+:10EEA000867809F0B39433FD14C030FF0FC07A2C7C\r
+:10EEB000B31460F4730C7B18B32C08C0B70180E264\r
+:10EEC00090E03C870E94E877B3943C85B314B0F39C\r
+:10EED00004C0B31410F43B1801C0312C34FF12C02D\r
+:10EEE000B70180E390E03C870E94E8773C8532FFE1\r
+:10EEF0001EC031FF03C088E590E002C088E790E0C3\r
+:10EF0000B7010CC0832F867891F031FD02C080E2FA\r
+:10EF100001C08BE237FD8DE2B70190E00E94E877F7\r
+:10EF200006C0B70180E390E00E94E8777A94A714C6\r
+:10EF3000C0F3AA94F401EA0DF11DB701808190E0BD\r
+:10EF40000E94E877A110F5CF06C0B70180E290E0FB\r
+:10EF50000E94E8773A943110F8CF42CEF70126812B\r
+:10EF6000378102C02FEF3FEFC9012C96E2E10C94EC\r
+:10EF70008E78F999FECF92BD81BDF89A992780B518\r
+:10EF80000895262FF999FECF1FBA92BD81BD20BDED\r
+:10EF90000FB6F894FA9AF99A0FBE01960895992738\r
+:10EFA00088270895FC010590615070400110D8F742\r
+:10EFB000809590958E0F9F1F0895FC0161507040C1\r
+:10EFC00001900110D8F7809590958E0F9F1F08959E\r
+:10EFD0000F931F93CF93DF938C01EB018B8181FD06\r
+:10EFE00003C00FEF1FEF1AC082FF0DC02E813F81BB\r
+:10EFF0008C819D812817390764F4E881F981019398\r
+:10F00000F983E88306C0E885F985802F0995892B67\r
+:10F0100041F78E819F8101969F838E83C801DF9186\r
+:10F02000CF911F910F910895FA01AA27283051F12D\r
+:10F03000203181F1E8946F936E7F6E5F7F4F8F4F29\r
+:10F040009F4FAF4FB1E03ED0B4E03CD0670F781F88\r
+:10F05000891F9A1FA11D680F791F8A1F911DA11D6D\r
+:10F060006A0F711D811D911DA11D20D009F46894A6\r
+:10F070003F912AE0269F11243019305D3193DEF64E\r
+:10F08000CF010895462F4770405D4193B3E00FD004\r
+:10F09000C9F7F6CF462F4F70405D4A3318F0495DEF\r
+:10F0A00031FD4052419302D0A9F7EACFB4E0A695D2\r
+:10F0B0009795879577956795BA95C9F700976105F4\r
+:10F0C000710508959B01AC010A2E0694579547954A\r
+:10F0D00037952795BA95C9F7620F731F841F951F3F\r
+:10F0E000A01D08952F923F924F925F926F927F9250\r
+:10F0F0008F929F92AF92BF92CF92DF92EF92FF9248\r
+:10F100000F931F93CF93DF93CDB7DEB7CA1BDB0BF3\r
+:10F110000FB6F894DEBF0FBECDBF09942A88398898\r
+:10F1200048885F846E847D848C849B84AA84B9849F\r
+:10F13000C884DF80EE80FD800C811B81AA81B981AB\r
+:10F14000CE0FD11D0FB6F894DEBF0FBECDBFED01BF\r
+:10F15000089513E1C8EBD3E104C0FE010E94B4722C\r
+:0CF160002296CA3BD107C9F7F894FFCFF4\r
+:10F16C0000001B43000016430000B44200000000E6\r
+:10F17C0000000000000080BF6400640000803B457C\r
+:10F18C0000803B4500007043000000000080BB4441\r
+:10F19C000160EA0000CDCCCC3D0000524300007A67\r
+:10F1AC004301010101010000803FE7849145AECF8E\r
+:10F1BC001A3D00004041FF3FFF3F0171DBB6427139\r
+:10F1CC00DBB64200007A4500C05A440000FA430006\r
+:10F1DC0000FA430000A04000003442E8030000E8BD\r
+:10F1EC0003000032000000FA0000004D3131300005\r
+:10F1FC002569206D696E2C20256920736563005587\r
+:10F20C0073696E672044656661756C7420736574F0\r
+:10F21C0074696E67733A004820004C20004D313001\r
+:10F22C003420496E76616C696420657874727564FB\r
+:10F23C00657220004D31303520496E76616C696401\r
+:10F24C0020657874727564657220004D31303920F8\r
+:10F25C00496E76616C696420657874727564657248\r
+:10F26C0020003F006F6B0020703A0020693A0020AC\r
+:10F27C00643A0020633A005400496E76616C69640C\r
+:10F28C002065787472756465720041637469766583\r
+:10F29C002045787472756465723A20004D323900DD\r
+:10F2AC0058595A45000001000F0015000E001200BD\r
+:10F2BC00FFFF160017000E001300FFFF03000200F3\r
+:10F2CC001A001400FFFFFFFFFFFF0A00FFFF010001\r
+:10F2DC0000000E000D000700FFFFFFFF06002E00D0\r
+:10F2EC00000000007D3771371C6B537465707261C0\r
+:10F2FC00746520746F2068696768203A2000504953\r
+:10F30C0044204175746F74756E65207374617274EA\r
+:10F31C00002F0043616E6E6F74206F70656E2073EA\r
+:10F32C007562646972006175746F25692E67004D92\r
+:10F33C003233202573004D3234004D3834205820A0\r
+:06F34C0059205A20450083\r
+:00000001FF\r
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..36ca447
--- /dev/null
+++ b/README
@@ -0,0 +1,26 @@
+This directory contains source code a number of objects of various
+kinds, and a simple build system.
+
+In general for a simple file FOO.scad you can say `make FOO.stl'.
+
+
+More complicated files contain source code for multiple related parts.
+The actual objects which might be printed or previewed are indicated
+in the source code with `///toplevel'.  For a complicated file
+BAR.scad you can generate simple template openscad files, one for each
+such object, with `make BAR.auto.scads'.  And you can make all the
+corresponding .stl files with `make BAR.auto.stls'.
+
+
+Many objects have `slop' in them, which represents an amount by which
+holes or gaps are bigger, in order to make things fit well.  You will
+need to examine the openscad source or play around with previews to
+see which slop does what.
+
+
+Unless otherwise noted every object and file here is:
+
+  Copyright Ian Jackson
+  Licenced under the GNU General Public Licence, version 3, or
+  (at your option) any later version.  There is NO WARRANTY.
+
diff --git a/adafruit-powerboost-1000.scad b/adafruit-powerboost-1000.scad
new file mode 100644 (file)
index 0000000..683eb81
--- /dev/null
@@ -0,0 +1,175 @@
+// -*- C -*-
+
+psu_sz_nom = [ 11.43*2, 36.20 ];
+
+//// toplevels-from:
+include <adafruit-powerboost-common.scad>
+
+psu_hole_pos = [ 2.05, // from back edge of psu_sz[0]
+                0.55 * 0.5 * 25.4, // from centreline
+                ];
+
+psu_led_low_x = 4;
+
+psu_led_usbend_y_min = 4.5;
+psu_led_chrg_min_x = -1.0;
+psu_led_chrg_max_x = 3.0;
+psu_led_chrg_both_sz_y = 9.4;
+
+psu_led_low_sz_x = 4.5;
+psu_led_low_min_y = -1.5;
+psu_led_low_max_y = 3.25;
+
+psu_led_baffle_th = 0.8;
+psu_led_baffle_ends = 1.5;
+
+psu_baffle_th = [ 0.8, 3.5 ];
+psu_innerend_led_depth = 7;
+
+psu_led_legend_line = 0.75;
+
+psu_led_legend_battery_l = 6.0;
+psu_led_legend_battery_w = 4.0;
+psu_led_legend_battery_nub_l = 0.75;
+psu_led_legend_battery_nub_w = 1.5;
+
+psu_led_legend_power_dia = 5.0;
+
+psu_led_legend_gap = 1.25;
+
+// ----- calculated -----
+
+psu_led_legend_battery_edge = psu_led_legend_line;
+
+psu_led_legend_power_tick_l =
+  psu_led_legend_power_dia * 0.65;
+psu_led_legend_power_tick_dy = psu_led_legend_line;
+
+psu_led_legend_power_sz_y =
+  psu_led_legend_power_dia/2 + psu_led_legend_power_tick_l/2
+  - psu_led_legend_power_tick_dy;
+
+psu_innerend_led_x_midder =  - psu_hole_pos[1] - psu_hole_dia/2;
+
+module PsuLedBafflePlan(){
+  AtPsuMountCorner(0,0) {
+    translate([ (psu_led_chrg_min_x + psu_led_chrg_max_x)/2,
+              psu_led_usbend_y_min + psu_led_chrg_both_sz_y/2 ])
+      square(center=true,
+            [ psu_led_chrg_max_x - psu_led_chrg_min_x
+              + psu_led_baffle_ends*2,
+              psu_led_baffle_th ]);
+  }
+}
+
+module PsuLedWindowsPlanCore(){
+  difference(){
+    union(){
+      // Two LEDs one side of inlet connector
+      // "Full" (near edge) and "Chrg" (inner)
+      AtPsuMountCorner(0,0) {
+       translate([0, psu_led_usbend_y_min ])
+         rectfromto([ psu_led_chrg_min_x, 0 ],
+                    [ psu_led_chrg_max_x,
+                      psu_led_chrg_both_sz_y ]);
+      }
+
+      // One LED, "Low", other side of inlet connector
+      AtPsuMountCorner(1,0) {
+       translate([0, psu_led_usbend_y_min ])
+         rectfromto([ 0,
+                      psu_led_low_min_y ],
+                    [ psu_led_low_sz_x,
+                      psu_led_low_max_y ]);
+      }
+
+      // One LED, PWR, near outlet USB pads
+      AtPsuMountCorner(0,1){
+       rectfromto([0,0],
+                  [psu_sz[0]/2 + psu_innerend_led_x_midder,
+                   psu_innerend_led_depth]);
+      }
+    }
+  }
+}
+
+module PsuLedLegendBattery(percent=50){
+  e = psu_led_legend_battery_edge;
+  empty_l = (100-percent)/100 * (psu_led_legend_battery_l - e*2);
+  difference(){
+    union(){
+      square([psu_led_legend_battery_l,
+             psu_led_legend_battery_w], center=true);
+      translate([psu_led_legend_battery_l/2, 0])
+       square([psu_led_legend_battery_nub_l*2,
+               psu_led_legend_battery_nub_w], center=true);
+    }
+    if (empty_l > 0)
+      translate([-(psu_led_legend_battery_l/2-e),
+                -(psu_led_legend_battery_w/2-e)])
+       square([empty_l, psu_led_legend_battery_w - e*2]);
+  }
+}  
+
+module PsuLedLegendPowerSymbol(){
+  $fn=30;
+  tick_mid = [0, psu_led_legend_power_dia/2 - psu_led_legend_power_tick_dy];
+
+  cut_slope = ( psu_led_legend_gap + psu_led_legend_line/2 ) / tick_mid[1];
+  cut_y = psu_led_legend_power_dia + 1;
+
+  translate(tick_mid)
+    square([psu_led_legend_line, psu_led_legend_power_tick_l], center=true);
+  
+  difference(){
+    circle(r= psu_led_legend_power_dia/2);
+    circle(r= psu_led_legend_power_dia/2 - psu_led_legend_line);
+
+    polygon([[0, 0],
+            [-cut_y * cut_slope, cut_y],
+            [ cut_y * cut_slope, cut_y]]);
+
+    if(0) translate(tick_mid)
+      square([psu_led_legend_line, psu_led_legend_power_tick_l]
+            + [psu_led_legend_gap*2, 0.1],
+            center=true);
+  }
+}
+
+module PsuLedLegendsPlan(){
+  translate([psu_led_legend_power_dia/2
+            + psu_innerend_led_x_midder
+            + psu_led_legend_gap,
+            psu_sz[1]/2
+            - psu_innerend_led_depth/2
+            ])
+    rotate([0,0,180])
+    PsuLedLegendPowerSymbol();
+
+  for (full=[0,1]) {
+    translate([-psu_sz[0]/2
+              + psu_led_legend_battery_l/2
+              + psu_led_chrg_max_x
+              + psu_led_legend_gap,
+              -psu_sz[1]/2
+              + psu_led_usbend_y_min
+              + psu_led_chrg_both_sz_y * 0.5
+              + max(
+                    psu_led_legend_battery_w + psu_led_legend_gap,
+                    psu_led_chrg_both_sz_y * 0.5
+                    ) * (0.5 - full)
+              ])
+      PsuLedLegendBattery(full ? 100 : 50);
+  }
+
+  translate([psu_sz[0]/2
+            - psu_led_legend_battery_nub_l
+            - psu_led_legend_battery_l/2,
+            -psu_sz[1]/2
+            + psu_led_legend_gap
+            + psu_led_usbend_y_min
+            + psu_led_low_max_y
+            + psu_led_legend_battery_w/2
+            ])
+    PsuLedLegendBattery(0);
+}
diff --git a/adafruit-powerboost-500.scad b/adafruit-powerboost-500.scad
new file mode 100644 (file)
index 0000000..d10002e
--- /dev/null
@@ -0,0 +1,66 @@
+// -*- C -*-
+
+psu_sz_nom = [ 21.59, 35.56 ];
+
+//// toplevels-from:
+include <adafruit-powerboost-common.scad>
+
+psu_baffle_cnr_y = 7.45; // from connector end
+psu_baffle_th = [ 0.8, 3.5 ];
+psu_usbend_led_x = 4.5;
+psu_innerend_led_depth = 10;
+
+// ----- calculated -----
+
+psu_usbend_led_depth = psu_baffle_cnr_y*2 - psu_usbend_led_x;
+
+
+module PsuLedBafflePlan(){
+  baffle_tr = [0, psu_baffle_cnr_y]
+    + 0.5 * [psu_baffle_th[1], psu_baffle_th[0]];
+  translate([0, -psu_sz[1]/2]) {
+    mirror([1,0,0]) {
+      rectfromto([-psu_baffle_th[1]/2, 0],
+                baffle_tr);
+      rectfromto([-psu_sz[0]/2 - psu_board_support_wall *2,
+                 baffle_tr[1] - psu_baffle_th[0]],
+                baffle_tr);
+    }
+  }
+}
+
+module PsuLedLegendsPlan(){
+}
+
+module PsuLedWindowsPlanCore(){
+  difference(){
+    union(){
+      // Two LEDs incl "Chrg", one side of inlet connector
+      AtPsuMountCorner(1,0) {
+       rectfromto([ -(psu_board_support_wall + 0.1),
+                    +psu_usbend_led_x ],
+                  [ psu_sz[0]/2,
+                    +psu_usbend_led_depth ]);
+      }
+
+      // One LED, "Low", other side of inlet connector
+      AtPsuMountCorner(0,0) {
+       sz = psu_baffle_cnr_y - psu_board_support_wall - psu_baffle_th[0];
+       translate([0, psu_baffle_cnr_y])
+         rectfromto([ -(psu_board_support_wall + 0.1),
+                      -sz/2 ],
+                    [ psu_sz[0]/2,
+                      +sz/2 ]);
+      }
+
+      // One LED, PWR, near outlet USB pads
+      AtPsuMountCorner(0,1){
+       rectfromto([0,0],
+                  [psu_sz[0]/2 - psu_hole_pos[1] - psu_hole_dia/2,
+                    psu_innerend_led_depth]);
+      }
+    }
+    translate([0, -psu_sz[1]/2])
+      square(center=true, [psu_baffle_th[1], psu_sz[1]]);;
+  }
+}
diff --git a/adafruit-powerboost-common.scad b/adafruit-powerboost-common.scad
new file mode 100644 (file)
index 0000000..b62643a
--- /dev/null
@@ -0,0 +1,337 @@
+// -*- C -*-
+
+include <nutbox.scad>
+include <utils.scad>
+
+psu_sz  = psu_sz_nom + [ 0.11, 0.44 ] + [ 0.25, 0.25 ];
+
+psu_hole_pos = [ 2.05, // from back edge of psu_sz[0]
+                0.55 * 0.5 * 25.4, // from centreline
+                ];
+
+psu_th = 1.70 + 0.25;
+psu_th_for_clamp = 1.50;
+
+psu_hole_dia = 2.5 - 0.5;
+psu_connector_z = 2.9 + 0.1;
+psu_connector_z_overlap = 0.15;
+psu_connector_depth = 6.25;
+psu_connector_w = 8.0 + 0.5;
+psu_usb_protr = 0.6;
+
+psu_clamp_th = 4.0 + 0.75;
+psu_clamp_w = 8.0;
+psu_clamp_gap = 0.4;
+
+psu_board_clamp_ovlp = 4.5;
+psu_board_nutbox = nutbox_data_M3;
+
+psu_board_gap = 0.5;
+psu_board_support_wall = 2;
+psu_board_support_ovlp = 4.5;
+psu_board_support_ovlp_ceil = 2;
+psu_board_support_z = 2;
+
+psu_baffle_gap = 1.0 + 0.5;
+
+psu_y = +psu_sz[1]/2 + psu_usb_protr;
+
+psu_usba_v_apart = 7.0;
+psu_usba_v_from_edge = 4.86;
+psu_usba_v_space_below = 1.5;
+psu_usba_v_space_w = 1.7;
+psu_usba_v_space_l = 3.0;
+
+psu_test_ceil = 2.5;
+
+// ----- calculated -----
+
+psu_z = NutBox_h_base(psu_board_nutbox);
+psu_z_down = psu_z + 0.1;
+psu_fix_sz = NutBox_outer_size(psu_board_nutbox);
+psu_board_nutbox_y = psu_sz[1]/2 + psu_board_nutbox[0]/2;
+
+psu_mount_outer_sz_x = psu_sz[0] + psu_board_support_wall * 2; // centred
+psu_mount_outer_sz_y = psu_y + max(psu_board_support_wall, // at psu_y
+                                  psu_board_nutbox_y + psu_fix_sz/2);
+
+module PsuBoardRepresentation(){
+  linear_extrude(height= psu_th)
+    square(center=true, [psu_sz[0],psu_sz[1]]);
+}
+
+module PsuRepresentation(){
+  PsuBoardRepresentation();
+  translate([0, -psu_sz[1]/2, -psu_connector_z])
+    linear_extrude(height= psu_connector_z + psu_connector_z_overlap)
+    rectfromto([ -psu_connector_w/2, -10 ],
+              [ +psu_connector_w/2, psu_connector_depth ]);
+}
+
+module AtPsuMountCorner(mx,my){
+  mirror([mx,0,0])
+    mirror([0,my,0])
+      translate(-0.5 * [psu_sz[0], psu_sz[1], 0]
+               -1 * [0,0, psu_z_down])
+       children();
+}
+
+module PsuMountCornerExtrude(mx,my, plus_z=psu_board_support_z){
+  AtPsuMountCorner(mx,my){
+    linear_extrude(height= psu_z_down + plus_z, convexity=10) {
+      children();
+    }
+  }
+}
+
+module PsuUsbAVSpacePlan(){
+  for (x= [-1,+1] * psu_usba_v_apart/2) {
+    translate([x, -psu_usba_v_from_edge ]) {
+      hull(){
+       for (y= [-1,+1] * 0.5 * (psu_usba_v_space_l - psu_usba_v_space_w)) {
+         translate([0,y])
+           circle(r= psu_usba_v_space_w);
+       }
+      }
+    }
+  }
+}
+
+module PsuMountPositiveMain(){
+  for (mx=[0,1]) {
+    for (my=[0,1]) {
+      PsuMountCornerExtrude(mx,my){
+       rectfromto(-[1,1]*psu_board_support_wall,
+                  +[1,1]*psu_board_support_ovlp);
+      }
+    }
+    // mount above at plug end
+    PsuMountCornerExtrude(mx,0, psu_th + psu_board_support_wall){
+      rectfromto(-[1,1]*psu_board_support_wall,
+                [psu_board_support_ovlp,
+                 psu_board_support_ovlp_ceil]);
+    }
+  }
+  translate([0,0, -psu_z_down])
+    linear_extrude(psu_z_down - psu_baffle_gap, convexity=10)
+      PsuLedBafflePlan();
+}
+
+module PsuMountNegative(){
+  axis = [0, -psu_sz[1]/2, psu_th];
+  PsuRepresentation();
+  translate(axis)
+    rotate([atan(2 * psu_board_support_z / psu_sz[1]),
+           0,0])
+    translate(-axis)
+    PsuBoardRepresentation();
+}
+
+module PsuMountPositive(){
+  difference(){
+    intersection(){
+      PsuMountPositiveMain();
+      linextr_y_xz(-psu_y, psu_sz[1]*2) square(100, center=true);
+    }
+    PsuMountNegative();
+    intersection(){
+      hull(){
+       PsuBoardRepresentation();
+       translate([0,0,5]) PsuBoardRepresentation();
+      }
+      translate([-20,0,-20]) cube(40);
+    }
+  }
+  for (mx=[0,1]) {
+    PsuMountCornerExtrude(mx,1){
+      translate([psu_sz[0]/2 - psu_hole_pos[1],
+                psu_hole_pos[0]]
+               + psu_board_gap * [1,1] )
+       circle(r= psu_hole_dia/2);
+    }
+  }
+  difference(){
+    translate([0, psu_board_nutbox_y, 0])
+      rotate([0,0,180])
+      NutBox(psu_board_nutbox, psu_z_down);
+    translate([0, psu_sz[1]/2, 0])
+      linextr(-psu_usba_v_space_below, +10)
+      PsuUsbAVSpacePlan();
+  }
+}
+
+module PsuClamp(){ ////toplevel
+  rotate([180,0,0]) difference(){
+    linear_extrude(height=psu_clamp_th + psu_th_for_clamp, convexity=5) {
+      difference(){
+       hull(){
+         circle(r = psu_fix_sz/2);
+         translate([ -psu_board_nutbox[0]/2, 0])
+           square(center=true, [ psu_board_clamp_ovlp*2, psu_clamp_w ]);
+       }
+       circle(r = psu_board_nutbox[0]/2);
+      }
+    }
+    translate([0,0,-1]) linear_extrude(height=psu_th_for_clamp+1) {
+      translate([ -psu_board_nutbox[0]/2 + psu_clamp_gap, 0 ])
+       mirror([1,0])
+       translate([0,-20]) square(40);
+    }
+    linextr(-10,10) {
+      rotate(-90)
+       translate([0, -psu_board_nutbox[0]/2])
+       PsuUsbAVSpacePlan();
+    }
+  }
+}
+
+module PsuLedWindowsPlan(){
+  difference(){
+    PsuLedWindowsPlanCore();
+    PsuLedBafflePlan();
+  }
+}
+
+module PsuLedWindowsWindows(ceil){
+  translate([0,0, -psu_z - ceil])
+    linextr(0, psu_initial_layer_thick)
+    offset(delta=psu_window_ledge)
+    PsuLedWindowsPlan();
+}
+
+module PsuFirstLayerNegative(ceil){
+  translate([0, 0, -psu_z - ceil])
+    linextr(-1, psu_initial_layer_thick)
+    children();
+}
+
+module PsuMountWindowsNegative(ceil){
+  linextr(-10, 0.1)
+    PsuLedWindowsPlan();
+  PsuFirstLayerNegative(ceil)
+    offset(delta= psu_window_ledge + psu_multicolour_gap)
+    PsuLedWindowsPlan();
+}
+
+module PsuLedLegendsNegative(ceil){
+  PsuFirstLayerNegative(ceil)
+    offset(delta= psu_multicolour_gap)
+    PsuLedLegendsPlan();
+}
+
+module PsuMountDemo() { ////toplevel
+  ceil = psu_test_ceil;
+
+  translate([0, psu_y, psu_z]) {
+    difference(){
+      PsuMountPositive();
+      linextr(-20, 0.1)
+       PsuLedWindowsPlan();
+    }
+    %PsuMountNegative();
+
+    color("yellow") translate([0,0, -psu_z - ceil])
+      linear_extrude(height=0.4, convexity=10)
+      PsuLedWindowsPlan();
+
+    color("blue") translate([0,0, -psu_z - ceil])
+      linear_extrude(height=0.4, convexity=10)
+      PsuLedLegendsPlan();
+
+    translate([0, psu_board_nutbox_y, 10])
+      rotate([180,0,0])
+      rotate([0,0,-90])
+      PsuClamp();
+  }
+}
+
+module PsuMountTest() { ////toplevel
+  ceil = psu_test_ceil;
+  $fs = 0.1;
+  $fa = 3;
+  difference(){
+    union(){
+      translate([0, psu_y, psu_z])
+       PsuMountPositive();
+      difference(){
+
+       // rectangular box with wall
+       linextr_x_yz(-psu_mount_outer_sz_x/2,
+                    +psu_mount_outer_sz_x/2) {
+         difference(){
+           rectfromto([0, -ceil],
+                      [psu_mount_outer_sz_y, psu_z + 10]);
+           rectfromto([ceil,0], 400*[1,1]);
+         }
+       }
+
+       translate([0, psu_y, psu_z]) {
+         PsuMountNegative();
+       }
+      }
+    }
+    translate([0, psu_y, psu_z]) {
+      PsuMountWindowsNegative(ceil);
+      PsuLedLegendsNegative(ceil);
+    }
+  }
+}
+
+psu_multicolour_gap = 0.075;
+psu_initial_layer_thick = 0.400;
+psu_initial_layer_width = 0.750;
+psu_window_ledge = 0.50; // each side
+
+psu_frame_gap = 1.0;
+
+module PsuMountLayerFrame(bl, tr, ix) {
+  gap0 = [1,1] * (psu_frame_gap + psu_initial_layer_width*(ix+0));
+  gap1 = [1,1] * (psu_frame_gap + psu_initial_layer_width*(ix+1));
+  linextr(0, psu_initial_layer_thick) {
+    difference(){
+      rectfromto(bl-gap1, tr+gap1);
+      rectfromto(bl-gap0, tr+gap0);
+    }
+  }
+}
+
+module PsuMountTestFullLayerFrame(ix) {
+  PsuMountLayerFrame([-0.5 * psu_mount_outer_sz_x, 0],
+                    [+0.5 * psu_mount_outer_sz_x,
+                     psu_mount_outer_sz_y],
+                    ix);
+}
+
+module PsuMountTestFullMain() { ////toplevel
+  ceil = psu_test_ceil;
+
+  PsuMountTestFullLayerFrame(2);
+  
+  difference(){
+    translate([0,0, ceil])
+      PsuMountTest();
+  }
+}
+
+module PsuMountTestFullOneLayer(ix) {
+  PsuMountTestFullLayerFrame(ix);
+  linextr(0, psu_initial_layer_thick) {
+    translate([0, psu_y]) children();
+  }
+}
+
+module PsuMountTestFullText() { ////toplevel
+  PsuMountTestFullOneLayer(0)
+    PsuLedLegendsPlan();
+}
+module PsuMountTestFullWindows() { ////toplevel
+  PsuMountTestFullLayerFrame(1);
+  translate([0, psu_y, psu_z + psu_test_ceil])
+    PsuLedWindowsWindows(psu_test_ceil);
+}
+
+module PsuMountTestFullDemo() { ////toplevel
+  color("blue") PsuMountTestFullMain();
+  color("yellow") PsuMountTestFullText();
+  %PsuMountTestFullWindows();
+}
diff --git a/anglepoise-neck.scad b/anglepoise-neck.scad
new file mode 100644 (file)
index 0000000..aa39ce2
--- /dev/null
@@ -0,0 +1,83 @@
+// -*- C -*-
+
+arm_depth = 25;
+arm_innerwidth = 9.60 - 0.50;
+arm_innerheight = 8.90 - 0.50;
+arm_pin_depth = 18.50 + 1.0;
+arm_pin_dia = 1.5 + 0.7;
+
+armpart_hex_rad = 15;
+armpart_main_thick = 8;
+
+hingepin_dia = 3 + 1.0;
+hingenut_width = 6 + 1.0;
+hingenut_depth = 4;
+hingenut_clear = 5;
+
+headpart_main_dia = 15 + 0.3;
+headpart_main_len = 16 + 1.1;
+headpart_stub_protrude = 2;
+headpart_stub_width = 11.7 - 0.6;
+
+headpart_flatten_angle = 45;
+
+// computed
+
+armpart_hinge_height = arm_innerheight + hingenut_width/2 + hingenut_clear;
+armpart_main_height = armpart_hinge_height + headpart_stub_width / 2;
+armpart_main_width = headpart_stub_width;
+armpart_x_unit = armpart_hex_rad * tan(30);
+headpart_flatten_z = headpart_main_dia/2 * cos(headpart_flatten_angle);
+headpart_stub_support_x = headpart_stub_width * cos(59) / 2;
+headpart_stub_len = headpart_stub_protrude + headpart_main_dia/2;
+hingenut_depth_y =
+  sqrt(headpart_main_dia*headpart_main_dia/4 - hingenut_width*hingenut_width/4)
+  - hingenut_depth;
+
+module ArmPart(){ ////toplevel
+  difference(){
+    translate([-arm_innerwidth/2, 1, 0])
+      mirror([0,-1,0])
+      cube([arm_innerwidth, arm_depth+1, arm_innerheight]);
+    translate([0, -arm_pin_depth, -50])
+      cylinder(r=arm_pin_dia/2, h=100, $fn=20);
+  }
+  difference(){
+    translate([-armpart_main_width/2, 0, 0])
+      cube([armpart_main_width, armpart_main_thick, armpart_main_height]);
+    translate([0,50,armpart_hinge_height])
+      rotate([90,0,0])
+      cylinder(r=hingepin_dia/2, h=100, $fn=20);
+  }
+}
+
+module HeadPart(){ ////toplevel
+  difference(){
+    union(){
+      translate([-headpart_main_len/2, 0,0])
+       rotate([0,90,0])
+       cylinder(r=headpart_main_dia/2, h=headpart_main_len, $fn=40);
+      rotate([90,0,0])
+       cylinder(h = headpart_stub_len,
+                r = headpart_stub_width/2,
+                $fn = 6);
+      translate([-headpart_stub_support_x,
+                -headpart_stub_len,
+                -headpart_main_dia/2])
+       cube([headpart_stub_support_x*2,
+             headpart_stub_len,
+             headpart_main_dia/2]);
+    }
+    translate([-100,-100,-100])
+      cube([200,200, 100 - headpart_flatten_z]);
+    rotate([90,0,0])
+      translate([0,0, -100])
+      cylinder(r=hingepin_dia/2, h = 200, $fn=20);
+    translate([0,hingenut_depth_y,0])
+      rotate([90,0,180])
+      cylinder(r=hingenut_width/2/cos(30), h=20, $fn=6);
+  }
+}
+
+//ArmPart();
+//HeadPart();
diff --git a/anglepoise-neck.slic3r b/anglepoise-neck.slic3r
new file mode 100644 (file)
index 0000000..b5079ea
--- /dev/null
@@ -0,0 +1 @@
+perimeters = 4
diff --git a/anke-gps-bracket.scad b/anke-gps-bracket.scad
new file mode 100644 (file)
index 0000000..084eefd
--- /dev/null
@@ -0,0 +1,374 @@
+// -*- C -*-
+
+include <doveclip.scad>
+
+// Dimensions of the main GPS body
+outerw = 120;
+outerh =  75;
+outert =  15;
+outerbackbevel = 3;
+
+// Dimensions for the holder
+holder_outerw = outerw - 0.0;
+holder_outerh = outerh + 0.0;
+holder_outert = outert + 0.0;
+
+// Dimensions for the model
+model_outerw = outerw + 2.5;
+model_outerh = outerh - 0.2;
+model_outert = outert - 1.0;
+
+// Dimensions of the bezel area round the edges
+bezelw =    11 - 0.5;
+bezelboth = 11 - 0.5;
+bezeltoph =  7 - 0.5;
+
+// Dimensions of the speaker at the back
+spkrdia =  22;
+spkr2bot = 19;
+spkr2rhs = 25;
+
+// Dimensions of the plug and wire
+plugw =      12;
+plugh =       9;
+plug2bot =   11;
+plug2lhs =   11;
+plugtotald = 15;
+pluggapd =    5;
+
+// Dimensions of the hole in the tray
+//   width and height (vertical) at the top
+nestleh = 53;
+nestlew = 60.9;
+//   depths (back to front distance):
+nestledl = 40.2;
+nestledr = 43.9;
+//   differences in width, depth, at bottom:
+nestledwl = 2.1;
+nestledwr = 1.4;
+nestleddf = 4.0;
+nestleddbl = 5.7;
+nestleddbr = 5.2;
+
+// Adjustment for the GPS attitude and position
+gpsazimuth = 45;
+gpselevation = 40;
+gpsrightwardoffset = 5;
+gpsrearwardoffset = 2;
+gpsrightwardoffsetonbar = 0;
+
+// Amount of wire protrusion to allow for
+plugwiremoreh = 25;
+
+// Slops and steps etc.
+plugslop = 0.5;
+plughstep = 1.5;
+bodylhsrhsslop = 0.5;
+holderhgap = 5;
+holderbezelmore = 2;
+nestlebevel = 1;
+
+// Dimensions for strength only
+screent = 1.0;
+plugstrutw = 4;
+plugstrutt = min(model_outert, 5);
+nestledoveclipw = 20;
+holderh = model_outerh * 0.5;
+holderwallt = 2.5;
+holderbackt = 2.8;
+holderdccount = 2;
+holderdoveclipl = 15;
+chassish = 13;
+chassist = 13;
+nestlefloorh = 4.7;
+nestleceilh = 6.0;
+nestlewallmin = 10.0;
+nestlearchslope = 0.75 * sqrt(0.5);
+
+// Consequential values
+holderdcw = DoveClipPairSane_width(holderdccount);
+
+module GpsPlugPlug(slop){
+  effhslop = slop - plughstep;
+  effplugw = plugw + slop*2;
+  effplugh = plugh + effhslop*2;
+  translate([plug2lhs-slop, plug2bot-effhslop, -1])
+    cube([effplugw, effplugh, model_outert+2]);
+}
+
+module GpsBodyOuterBevel(len){
+  translate([0,-1,0]) {
+    rotate([-90,0,0]) {
+      linear_extrude(height=len+2) {
+       polygon([[-outerbackbevel, 0],
+                [ 0, outerbackbevel],
+                [outerbackbevel, 0],
+                [ 0, -outerbackbevel]]);
+      }
+    }
+  }
+}
+
+module GpsBody() { ////toplevel
+  difference(){
+    union(){
+      difference(){
+       cube([model_outerw, model_outerh, model_outert]);
+       translate([bezelw, bezelboth, screent])
+         cube([model_outerw-bezelw*2,
+               model_outerh-bezelboth-bezeltoph,
+               model_outert]);
+       translate([model_outerw-spkr2rhs, spkr2bot, -1])
+         cylinder(r=spkrdia/2, h=model_outert+2);
+      }
+      translate([plug2lhs+plugw/2, plug2bot+plugh/2, 0])
+       cylinder(r=(plugw+plugh)/2, h=model_outert);
+      for (x=[plug2lhs-plugstrutw, plug2lhs+plugw])
+       translate([x, 0.1, 0])
+         cube([plugstrutw, model_outerh-0.2, plugstrutt-0.10]);
+    }
+    GpsPlugPlug(0);
+    for (x=[0,model_outerw]) translate([x,0,0]) GpsBodyOuterBevel(model_outerh);
+    for (y=[0,model_outerh]) translate([0,y,0])
+      rotate([0,0,-90]) GpsBodyOuterBevel(model_outerw);
+  }
+}
+
+module GpsPlug() {
+  plugwireh = plug2bot + plugwiremoreh;
+  translate([-plugslop,0,0]) GpsPlugPlug(-plugslop);
+  mirror([0,0,1]) translate([plug2lhs, plug2bot, 0]) {
+    cube([plugw, plugh, plugtotald-0.05]);
+    translate([0, -plugwireh, pluggapd])
+      cube([plugw, plugwireh+0.05, plugtotald-pluggapd]);
+  }
+}
+
+lhsteethu = 2;
+
+module GpsLHSMask(xslop=0){
+  translate([plug2lhs + plugw+plugh+plugstrutw,
+            0,
+            -50]) {
+    for (iter=[-100/lhsteethu : 100/lhsteethu]) {
+      translate([0, iter*lhsteethu*2, 0]) {
+       linear_extrude(height=100) {
+         polygon([[-300,     0],
+                  [   0,     0],
+                  [lhsteethu,lhsteethu],
+                  [   0,     lhsteethu*2],
+                  [-300,     lhsteethu*2+0.1]]);
+       }
+      }
+    }
+  }
+}
+
+module GpsAssembled(){ ////toplevel
+  GpsBody();
+  GpsPlug();
+}
+
+module GpsBodyLT(){
+  intersection(){
+    GpsBody();
+    GpsLHSMask();
+  }
+}
+
+module GpsBodyRT(){
+  difference(){
+    GpsBody();
+    GpsLHSMask(bodylhsrhsslop);
+  }
+}
+
+module GpsPlugT(){ ////toplevel
+  rotate([0,-90,0]) GpsPlug();
+}
+
+module NestleCubeCutout(ca,cb,d){
+  dist = cb - ca;
+  cuth = -nestleh + nestlefloorh;
+  mirror([0,1,0]){
+    translate([0,1,0])
+    rotate([90,0,0]){
+      linear_extrude(height=d+2){
+       polygon([[ca+nestlebevel, cuth],
+                [ca, cuth+nestlebevel*2],
+                [ca, -dist/2/nestlearchslope-nestleceilh],
+                [(ca+cb)/2, -nestleceilh],
+                [cb, -dist/2/nestlearchslope-nestleceilh],
+                [cb, cuth+nestlebevel*2],
+                [cb-nestlebevel, cuth]]);
+      }
+    }
+  }
+}
+
+module NestleCube(){ ////toplevel
+  midw = nestlew/2;
+  midd = min(nestledl,nestledr);
+  midddb = max(nestleddbl,nestleddbr);
+
+  based0 = nestleddf;
+  based1 = midd - midddb;
+  basew0 = -nestledwr;
+  basew1 = +nestledwl-nestlew;
+
+  echo("wl,wr=", basew1, basew0);
+  echo("df,dbl,dbm,dbr",
+       based0, nestledl-nestleddbl, based1, nestledr-nestleddbr);
+
+  cutd0 = based0 + nestlewallmin;
+  cutd1 = based1 - nestlewallmin;
+  cutw0 = basew0 - nestlewallmin;
+  cutw1 = basew1 + nestlewallmin;
+
+  bevth = -nestleh + nestlebevel*2;
+  bevw = nestlebevel;
+  bevd = nestlebevel;
+
+  translate([-(basew0+basew1)/2, -(based0+based1)/2, 0]) {
+    difference(){
+      polyhedron
+       (points=
+        [[          +0      ,            +0,        0], // 0
+         [          +0      ,            +nestledr, 0], // 1
+         [          -midw   ,            +midd,     0], // 2
+         [          -nestlew,            +nestledl, 0], // 3
+         [          -nestlew,            +0,        0], // 4
+         [-nestledwr+0      , +nestleddf +0,        bevth], // 5
+         [-nestledwr+0      , -nestleddbr+nestledr, bevth], // 6
+         [          -midw   , -midddb    +midd,     bevth], // 7
+         [+nestledwl-nestlew, -nestleddbl+nestledl, bevth], // 8
+         [+nestledwl-nestlew, +nestleddf +0,        bevth], // 9
+         [-nestledwr+0      -bevw, +nestleddf +0       +bevd, -nestleh], // 10
+         [-nestledwr+0      -bevw, -nestleddbr+nestledr-bevd, -nestleh], // 11
+         [          -midw        , -midddb    +midd    -bevd, -nestleh], // 12
+         [+nestledwl-nestlew+bevw, -nestleddbl+nestledl-bevd, -nestleh], // 13
+         [+nestledwl-nestlew+bevw, +nestleddf +0       +bevd, -nestleh]], // 14
+        triangles=[// main side panels
+                   [0,1,6],[6,5,0],
+                   [1,2,7],[7,6,1],
+                   [2,3,8],[8,7,2],
+                   [3,4,9],[9,8,3],
+                   [4,0,5],[5,9,4],
+                   // bevels
+                   [6,7,12],[12,11,6],
+                   [7,8,13],[13,12,7],
+                   [8,9,14],[14,13,8],
+                   [9,5,10],[10,14,9],
+                   [5,6,11],[11,10,5],
+                   // top and bottom
+                   [4,3,2],[2,1,0],[0,4,2],
+                   [12,13,14],[10,11,12],[12,14,10]],
+        convexity=3);
+      union(){
+       #NestleCubeCutout(cutw1, cutw0, max(nestledl,nestledr));
+       #rotate([0,0,90]) NestleCubeCutout(cutd0, cutd1, nestlew);
+      }
+    }
+  }
+
+  translate([gpsrightwardoffset,-gpsrearwardoffset,0])
+    rotate([0,0,90+gpsazimuth])
+    translate([nestledoveclipw/2,0,DoveClip_depth()-0.5])
+    rotate([0,-90,0])
+    DoveClipPairSane(count=3, h=nestledoveclipw);
+}
+
+module NestleCubeBaseTest(){ ////toplevel
+  intersection(){
+    translate([0,0,nestleh]) NestleCube();
+    translate([-100,-100,0]) cube([200,200,nestlebevel*5]);
+  }
+  cube([5,5,10]);
+}
+
+module NestleCubeCeilTest(){ ////toplevel
+  intersection(){
+    translate([0,0,3]) NestleCube();
+    translate([-100,-100,0]) cube([200,200,5.5]);
+  }
+  cube([5,5,10]);
+}
+
+module NestleCubePin(){ ////toplevel
+  DoveClipPin(nestledoveclipw*0.4);
+}
+
+module HolderSideL(){ ////toplevel
+  minz = -(bezelw - holderbezelmore) - holderbackt;
+  holdert = holder_outert + holderwallt*2;
+  cylr = 0.5*sqrt(holderdcw*holderdcw + holderdoveclipl*holderdoveclipl);
+  difference(){
+    translate([-holderh,
+              -holderwallt,
+              minz]) {
+      cube([holderh + holderhgap + cylr,
+           holdert,
+           -minz]);
+      translate([holderh + holderhgap + cylr, holdert/2, 0]) {
+       cylinder(r=cylr, h=-minz);
+       rotate([0,0,gpselevation])
+         translate([0, -holderdoveclipl/2, -minz + DoveClip_depth()])
+         rotate([0,-90,-90])
+         DoveClipPairSane(count=holderdccount, h=holderdoveclipl);
+      }
+    }
+    translate([-holderh-1,
+              0,
+              minz + holderbackt])
+      cube([holderh+1,
+           holder_outert,
+           bezelw]);
+  }
+}
+
+module HolderSideR(){ ////toplevel
+  mirror([0,1,0]) HolderSideL();
+}
+
+module ChassisBar(){ ////toplevel
+  dist = holder_outerw - 2*((bezelw - holderbezelmore) + DoveClip_depth());
+  cliph = holderdcw;
+  for (mir=[0,1]) {
+    mirror([mir,0,0]) {
+      translate([dist/2, cliph/2, 0])
+       DoveClipPairSane(h=holderdoveclipl, count=holderdccount);
+      translate([-1, 0, 0])
+       cube([dist/2 - DoveClip_depth() + 1.1, chassish, chassist]);
+    }
+  }
+  translate([-gpsrightwardoffsetonbar, -DoveClip_depth(), 0])
+    rotate([0,0,-90])
+    DoveClipPairSane(h=nestledoveclipw, count=3,
+                    baseextend=chassist/2);
+}
+
+module HolderSidePin(){ ////toplevel
+  DoveClipPin(holderdoveclipl*0.5);
+}
+
+module Pins(){ ///toplevel
+  for (i=[1:4*holderdccount]) {
+    translate([i*10, 0, 0]) HolderSidePin();
+  }
+  for (i=[1:6]) {
+    translate([i*10, 20, 0]) NestleCubePin();
+  }
+}
+
+//GpsPlugT();
+//GpsAssembled();
+//GpsBody();
+//NestleCube();
+//NestleCubeBaseTest();
+//NestleCubeCeilTest();
+//NestleCubePin();
+//HolderSideL();
+//HolderSideR();
+//HolderSidePin();
+//ChassisBar();
+//Pins();
diff --git a/atreic-piano-stand.scad b/atreic-piano-stand.scad
new file mode 100644 (file)
index 0000000..be81606
--- /dev/null
@@ -0,0 +1,11 @@
+// -*- C -*-
+height = 40;
+depth = 20;
+thick = 3;
+width = 40;
+
+difference(){
+  cube([width, depth, height]);
+  translate([thick, -1, thick])
+    cube([width - thick*2, depth+2, height]);
+}
diff --git a/axlepin.scad b/axlepin.scad
new file mode 100644 (file)
index 0000000..8b47771
--- /dev/null
@@ -0,0 +1,47 @@
+// -*- C -*-
+//
+// axlepin.scad
+//
+// 3D designs for for securing things on axles
+// Copyright 2012,2016 Ian Jackson
+//
+// This work is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This work 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 work.  If not, see <http://www.gnu.org/licenses/>.
+
+
+
+function AxlePin_holerad() = 2;
+function AxlePin_zoffset(holerad=2, slop=0.5) = (holerad - slop)*0.7;
+
+module AxlePin(axlerad, pinlen, holerad=2, tabthick=5, slop=0.5){
+  pinr = holerad - slop;
+  intersection(){
+    union(){
+      translate([0, -pinlen/2, 0]) rotate([-90,0,0])
+       cylinder(r=pinr, h=pinlen, $fn=10);
+      translate([-tabthick, axlerad, -holerad])
+       cube([tabthick*2, holerad*2, holerad*2]);
+    }
+    translate([-50,-50,-AxlePin_zoffset(holerad,slop)])
+      cube([100,100,50]);
+  }
+}
+
+function Washer_thick() = 1.2;
+
+module Washer(axlerad, washerrad, thick=1.2, slop=0.5){
+  difference(){
+    cylinder(h=thick, r=washerrad);
+    translate([0,0,-1]) cylinder(h=thick+2, r=axlerad+slop);
+  }
+}
diff --git a/belt-cut-jig-common.scad b/belt-cut-jig-common.scad
new file mode 100644 (file)
index 0000000..5464678
--- /dev/null
@@ -0,0 +1,95 @@
+// -*- C -*-
+
+jig_max_len = 160; // print diagonally
+//jig_max_len = 30;
+
+registrationgroove_width = 0.8;
+registrationgroove_depth = 1.2;
+
+registrationprotrusion_poke = 3;
+registrationprotrusion_slope = 0.75;
+
+jig_overlap = 1;
+
+jig_ends_extra = 2;
+
+jig_iters = floor((jig_max_len - jig_ends_extra) / jig_interval);
+//jig_iters=2;
+echo(jig_iters);
+
+module RegistrationGroove(l){
+  // runs along the +ve X axis for length l but at correct z pos
+  translate([0, 0, jig_main_zsz + 0.1]) {
+    rotate([90,0,90])
+      linear_extrude(height=l)
+      polygon([[-registrationgroove_width/2, 0],
+              [ +registrationgroove_width/2, 0],
+              [ 0, -registrationgroove_depth ]]);
+  }
+}
+
+module OneJig(){
+  difference(){
+    translate([-(jig_interval/2 + jig_overlap),
+              jig_min_y,
+              -strap_thick])
+      cube([jig_interval + 2,
+           jig_max_y - jig_min_y,
+           jig_main_zsz + strap_thick]);
+    OneJigCutout();
+    translate([-100, -strap_width/2, -10])
+      cube([200, strap_width, 10]);
+   translate([-100,0,0])
+     RegistrationGroove(200);
+   for (xfrac=[-1/4,0,+1/4])
+     translate([jig_interval * xfrac, -100, 0])
+       rotate([0,0,90])
+       RegistrationGroove(200);
+  }
+}
+
+module RegistrationProtrusion(){
+  // points towards the positive x axis
+  xsz = registrationprotrusion_poke;
+  ysz = registrationprotrusion_poke;
+  diag_sz = xsz * sqrt(2);
+  zsz = diag_sz / registrationprotrusion_slope;
+  hull(){
+    translate([0, 0, 0.1]){
+      linear_extrude(height=0.1)
+       polygon([[   0, -ysz ],
+                [ xsz,    0 ],
+                [   0,  ysz ]]);
+      translate([-0.1, 0, zsz ])
+       rotate([0,0,45])
+       cube(0.1);
+    }
+  }
+}
+
+module Jig(){
+  for(end=[0,1]){
+    for(yfrac=[-1/2, 0, 1/2]){
+      translate([ end
+                 ? jig_interval * (jig_iters - 0.5)
+                 : -jig_interval/2,
+                yfrac * strap_width,
+                0])
+       rotate([0,0, end ? 0 : 180])
+       translate([ jig_overlap, 0, 0 ])
+       RegistrationProtrusion();
+    }
+  }
+  for (i=[0:jig_iters-1]) {
+    translate([jig_interval * i, 0, 0])
+      OneJig();
+  }
+}
+
+module JigPrint(){
+  rotate([0,0,-45])
+    translate([0,0,jig_main_zsz])
+    rotate([180,0,0])
+    Jig();
+}
+
diff --git a/belt-hole-cut-jig-simple.scad b/belt-hole-cut-jig-simple.scad
new file mode 100644 (file)
index 0000000..68ef720
--- /dev/null
@@ -0,0 +1,96 @@
+// -*- C -*-
+
+strap_thick = 3;
+strap_width = 26.75 + 0.7;
+
+punch_dia = 11.10;
+
+punch_slop = 0.5;
+
+jig_interval = 20;
+
+reg_blocks = 3;
+
+jig_iters = 7;
+
+roof_thick = 4;
+regblock_thick = 4;
+punchtube_thick = 1.8;
+
+total_h = 33;
+punchfree_h = 8;
+
+reg_prot_width = 4;
+
+// computed:
+
+punchhole_r = punch_dia/2 + punch_slop;
+mainframe_l = jig_interval * jig_iters;
+
+mainframe_w = strap_width + reg_prot_width*2;
+
+echo(mainframe_l);
+
+module RegBlockOutline(){
+  difference(){
+    translate([0, -mainframe_w/2])
+      mirror([1,0])
+      square([total_h, mainframe_w]);
+    translate([1, -strap_width/2])
+      mirror([1,0])
+      square([strap_thick+1, strap_width]);
+  }
+}
+
+module RegBlock(){
+  translate([regblock_thick/2,0,total_h])
+    rotate([0,-90,0])
+    linear_extrude(height=regblock_thick)
+    RegBlockOutline();
+}
+
+module MainFrame(){
+  translate([jig_interval/2, -mainframe_w/2, 0])
+    mirror([1,0,0])
+    cube([mainframe_l, mainframe_w, roof_thick]);
+  for (rbi=[0:reg_blocks-1]) {
+    translate([0 +
+              -(mainframe_l-jig_interval)/(reg_blocks-1) * rbi,
+              0,0])
+      RegBlock();
+  }
+}
+
+module PerHole(){
+  for (holei=[0:jig_iters-1]) {
+    translate([-jig_interval * holei, 0, 0])
+      child(0);
+  }
+}
+
+module Shells(){
+  PerHole(){
+    cylinder(r=punchhole_r+punchtube_thick, h=total_h-punchfree_h, $fn=50);
+  }
+}
+
+module Punches(){
+  PerHole(){
+    translate([0,0,-1]){
+      cylinder(r=punchhole_r, h=total_h+2, $fn=100);
+      %cylinder(r=punch_dia/2, h=total_h);
+    }
+  }
+}
+
+module Jig(){
+  difference(){
+    union(){
+      MainFrame();
+      Shells();
+    }
+    Punches();
+  }
+}
+
+Jig();
diff --git a/belt-hole-cut-jig.scad b/belt-hole-cut-jig.scad
new file mode 100644 (file)
index 0000000..36380a0
--- /dev/null
@@ -0,0 +1,38 @@
+// -*- C -*-
+
+strap_thick = 3;
+strap_width = 26.75 + 0.7;
+
+jig_interval = 20;
+
+edgewall_width = 3;
+
+jig_ywidth = 17;
+
+jig_min_y = -jig_ywidth;
+jig_max_y = +jig_ywidth;
+
+
+jig_main_zsz = 28;
+punch_dia = 12.75;
+
+punch_slop = 0.5;
+
+// common stuff
+
+include <belt-cut-jig-common.scad>
+
+module OneJigCutout(){
+  translate([0,0,-10])
+  cylinder(r= punch_dia/2 + punch_slop, h=100, $fn=50);
+}
+
+module Demo(){ ////toplevel
+  Jig();
+}
+
+module JigT(){ ////toplevel
+  JigPrint();
+}
+
+JigT();
diff --git a/belt-slot-cut-jig,JigT.auto.slic3r b/belt-slot-cut-jig,JigT.auto.slic3r
new file mode 100644 (file)
index 0000000..33b5b29
--- /dev/null
@@ -0,0 +1,2 @@
+fill_angle = 0
+skirt_distance = 1
diff --git a/belt-slot-cut-jig,Kit.auto.slic3r b/belt-slot-cut-jig,Kit.auto.slic3r
new file mode 100644 (file)
index 0000000..33b5b29
--- /dev/null
@@ -0,0 +1,2 @@
+fill_angle = 0
+skirt_distance = 1
diff --git a/belt-slot-cut-jig.scad b/belt-slot-cut-jig.scad
new file mode 100644 (file)
index 0000000..543a53f
--- /dev/null
@@ -0,0 +1,195 @@
+// -*- C -*-
+
+// todo
+//  various registration marks
+//   protrustions at ends at strap width and middle
+//   grooves on top face at 1/4,1/2,3/4 length and 1/2 width
+
+strap_thick = 3;
+strap_width = 26.75 + 0.7;
+
+jig_interval = 25;
+
+edgewall_width = 3;
+crewpunch_slop = 0.3;
+main_slop = 0.25;
+
+holder_min_wall = 2;
+holder_attach_near_wall = 5;
+
+holder_attach_xsz = 5;
+holder_ctie_width = 4.0 + 0.5;
+holder_ctie_thick = 3.0 + 0.5;
+holder_attach_walls = 3;
+holder_attach_roof = 2.5;
+
+holder_corner_round = 2.0;
+
+punch_travel = 8;
+
+// from careful measurement
+
+crewpunch_shape =
+  [[  6, [0.6, 6.0], [1.6, 12.3] ],
+   [  8, [1.1, 6.2], [1.9, 12.5] ],
+   [ 10, [1.6, 6.5], [2.1, 12.8] ],
+   [ 12, [1.8, 6.6], [2.3, 12.7] ],
+   [ 14, [2.1, 6.8], [2.6, 13.0] ],
+   [ 16, [2.4, 6.9], [2.7, 13.2] ],
+   [ 18, [2.5, 7.0], [2.9, 13.3] ],
+   [ 22, [3.1, 7.1], [3.2, 13.4] ],
+   [ 26, [3.3, 7.2], [3.5, 13.6] ], 
+   ];
+
+crewpunch_shaft_max_y = 7.5;
+
+crewpunch_systematic_size_error = +0.36;
+
+crewpunch_smallest_shape = crewpunch_shape[0];
+crewpunch_biggest_shape = crewpunch_shape[len(crewpunch_shape)-1];
+
+crewpunch_skew_angle = 3.5; //degrees
+crewpunch_skew_yoff = +1.1; //mm
+
+// computed
+
+punch_dx = 0.5 * (-crewpunch_biggest_shape[2][0]
+                 +crewpunch_biggest_shape[2][1]);
+punch_dy = 0.5 * (+crewpunch_biggest_shape[1][1]
+                 -crewpunch_biggest_shape[1][0]) + crewpunch_skew_yoff;
+
+attach_ysz = holder_attach_walls*2 + holder_ctie_width;
+
+holder_block_zsz = crewpunch_biggest_shape[0] - crewpunch_smallest_shape[0];
+holder_xsz = crewpunch_biggest_shape[2][0] + crewpunch_biggest_shape[2][1] +
+  holder_min_wall*2;
+
+holder_skewangle_yextra = holder_xsz/2 * sin(abs(crewpunch_skew_angle));
+
+holder_max_y = punch_dy + crewpunch_biggest_shape[1][0] + holder_min_wall
+  + crewpunch_systematic_size_error + holder_skewangle_yextra;
+
+holder_attach_max_y = punch_dy
+  - max(crewpunch_biggest_shape[1][1], crewpunch_shaft_max_y)
+  - crewpunch_systematic_size_error - holder_skewangle_yextra;
+
+holder_block_min_y = punch_dy
+  - crewpunch_biggest_shape[1][1] - holder_attach_near_wall +
+  - crewpunch_systematic_size_error - holder_skewangle_yextra;
+
+holder_all_min_y = holder_attach_max_y - attach_ysz;
+
+jig_max_y = max(holder_max_y + main_slop, strap_width/2) + edgewall_width;
+jig_min_y = min(holder_all_min_y - main_slop, -strap_width/2) - edgewall_width;
+
+jig_main_zsz = holder_block_zsz + punch_travel;
+
+// common stuff
+
+include <belt-cut-jig-common.scad>
+
+// objects
+
+module CrewPunch(){
+  ourslop = crewpunch_slop - crewpunch_systematic_size_error;
+  hull(){
+    for(layer=crewpunch_shape){
+      translate([0,0, layer[0]]){
+       for(xind=[0,1]) //translate([xind?0:1,0,0])
+         for(yind=[0,1]) //translate([0,yind?0.5:0,0])
+           mirror([xind?1:0,0,0]) mirror([0,yind?0:1,0]){
+             translate([-0.1,-0.1,-0.1])
+               cube([0.1 + layer[2][xind] + ourslop,
+                     0.1 + layer[1][1-yind] + ourslop,
+                     0.2]);
+           }
+      }
+    }
+  }
+}
+
+module MaybeRoundedCube(sizes, roundedness){
+  if (roundedness > 0) {
+    translate([roundedness, roundedness, 0]){
+      minkowski(){
+       cube([sizes[0] - roundedness*2,
+             sizes[1] - roundedness*2,
+             sizes[2]]);
+       cylinder(h=0.05, r=roundedness, $fn=20);
+      }
+    }
+  } else {
+    cube(sizes);
+  }
+}
+
+module PunchHolder(cutouts=true){
+  roundedness = cutouts ? holder_corner_round : 0;
+    difference(){
+      translate([-holder_xsz/2, holder_block_min_y, 0])
+       MaybeRoundedCube([holder_xsz,
+                         holder_max_y - holder_block_min_y,
+                         holder_block_zsz],
+                        roundedness);
+      if (cutouts)
+       rotate([0,0,-crewpunch_skew_angle])
+       translate([punch_dx,
+                  punch_dy,
+                  -crewpunch_smallest_shape[0]])
+         CrewPunch();
+    }
+    difference(){
+      translate([-holder_attach_xsz/2, holder_all_min_y, 0])
+       MaybeRoundedCube([holder_attach_xsz,
+                         attach_ysz,
+                         holder_block_zsz + punch_travel
+                         + holder_ctie_thick + holder_attach_roof + 1],
+                        roundedness);
+      if (cutouts)
+       translate([-30,
+                  holder_all_min_y + holder_attach_walls,
+                  holder_block_zsz + punch_travel])
+         cube([60, holder_ctie_width, holder_ctie_thick]);
+    }
+}
+
+module OneJigCutout(){
+  minkowski(){
+    cube([main_slop*2, main_slop*2, 50], center=true);
+    PunchHolder(false);
+  }
+}
+
+module JigT(){ ////toplevel
+  JigPrint();
+}
+
+module PunchHolderT(){ ////toplevel
+  PunchHolder(true);
+}
+
+module Demo(){ ////toplevel
+  %PunchHolder();
+  Jig();
+}
+
+module Kit(){ ////toplevel
+  JigT();
+  rotate([0,0,-45]){
+    translate([(jig_iters-1)*jig_interval/2,
+              jig_min_y - holder_max_y - 5,
+              0])
+      PunchHolder();
+  }
+}
+
+//CrewPunch();
+//PunchHolder();
+//PunchHolder(false);
+//OneJig();
+//Jig();
+Demo();
+//JigT();
+//RegistrationProtrusion();
+//PunchHolderT();
+//Kit();
diff --git a/bike-lipo-box-gland.scad b/bike-lipo-box-gland.scad
new file mode 100644 (file)
index 0000000..0e9228b
--- /dev/null
@@ -0,0 +1,59 @@
+// -*- C -*-
+
+include <commitid.scad>
+
+cable_dias = [6.5, 8.2];
+
+cd = cable_dias[1] + 0.5;
+wall = 2.5;
+
+function Gland_xlen(cabledia)    = cabledia * 1.5;
+function Gland_xdia(cabledia)    = cabledia * 2.0;
+function Gland_xoutdia(cabledia) = Gland_xdia(cabledia) * 1.1 + 0.5;
+
+// origin is centre, on outside
+// outside is in direction of positive X axies
+module GlandNegative(cabledia){
+  xlen = Gland_xlen(cabledia);
+  xdia = Gland_xdia(cabledia);
+
+  hull(){
+    rotate([0,90,0]) cylinder(r= cabledia/2, h=1);
+    translate([xdia,0,0]) rotate([0,90,0]) cylinder(r= xdia/2, h=1);
+  }
+  translate([-10,0,0])
+    rotate([0,90,0])
+    cylinder(r= cabledia/2, h=11);
+}
+
+module GlandPositive(cabledia){
+  translate([-0.1, 0,0])
+    rotate([0,90,0])
+    cylinder(r= Gland_xoutdia(cabledia)/2, h= Gland_xlen(cabledia) + 0.1);
+}  
+
+platesz = [wall, 24, 28];
+plateoff = [-platesz[0]/2, -platesz[1]/2, -platesz[2] + platesz[1]/2];
+
+module Plate(){
+  difference(){
+    union(){
+      GlandPositive(cd);
+      translate(plateoff)
+       cube(platesz);
+    }
+    GlandNegative(cd);
+  }
+}
+
+module Test(){ ////toplevel
+  Plate();
+  translate(plateoff){
+    difference(){
+      cube([15, 20, 1.2]);
+      Commitid_BestCount_M([15, 20]);
+    }
+  }
+}
+
+//Test();
diff --git a/bike-lipo-box.scad b/bike-lipo-box.scad
new file mode 100644 (file)
index 0000000..edd3701
--- /dev/null
@@ -0,0 +1,294 @@
+// -*- C -*-
+
+include <commitid.scad>
+include <utils.scad>
+include <sealing-box.scad>
+include <bike-lipo-box-gland.scad>
+
+pxp6012_rad = 22.5 / 2 + 0.5; // make circular hole this size in outer wall
+pxp6012_rad_outer = 32.0 / 2 - 0.5;
+
+s1930_y = 30.2 + 0.2;
+s1930_x =   22 + 0.2;
+s1930_y_outer = 36.4 + 0.2;
+s1930_x_outer = 27.6 + 0.2;
+
+s1930_recess = 3;
+s1930_around = 3;
+s1930_behind = 3;
+
+jdae12pa_rad = 12 / 2 + 0.5;
+jdae12pa_rad_outer = 19 / 2 + 0.5; // head of an "M12 bolt"
+
+totx_inner = 180;
+toty_outer = 95;
+totz_inner = 27.0;
+
+wallthick = 2.5;
+
+cabledia = 8.7;
+
+strap_w = 5 + 1;
+strap_th = 4 + 1;
+strap_pillar = 3;
+strap_pillard = 5;
+strap_over = 2;
+
+lipokeeper_w = 10;
+lipokeeper_h = 8;
+lipokeeper_d_min = 2;
+lipokeeper_slope = 0.75;
+lipokeeper_end_h = 12;
+lipokeeper_end_d_min = 15;
+
+straps_at_box = [45, 95, 125, 160];
+straps_every = 30;
+
+// calculated
+
+totx_outer = totx_inner + wallthick*2;
+toty_inner = toty_outer - wallthick*2;
+totz_outer = totz_inner + wallthick*2;
+
+sb_box_sz = [totx_outer, totz_outer, toty_inner];
+
+// origin is at centre on outer face wall
+// outside is towards positive x
+// mounting is vertical
+module S1930_Positive(){
+  d = s1930_recess + s1930_behind;
+  translate([-d/2, 0,0])
+    cube([d,
+         s1930_x_outer + s1930_around,
+         s1930_y_outer + s1930_around], center=true);
+}
+module S1930_Negative(){
+  cube([60, s1930_x, s1930_y],
+       center=true);
+  translate([1, 0,0])
+    cube([s1930_recess*2+2, s1930_x_outer, s1930_y_outer],
+        center=true);
+}
+
+module TestWall(){ ////toplevel
+  sw_ctr = [25, wallthick, 25];
+
+  rotate([0,0,-90]){
+    difference(){
+      union(){
+       cube([50, wallthick, 42]);
+      }
+
+      translate([30, -1, 20])
+       rotate([-90,0,0])
+       cylinder(r = pxp6012_rad, h=10, $fn=60);
+
+      rotate([90,0,0])
+       Commitid_BestCount([15,40]);
+    }
+  }
+
+  difference(){
+    union(){
+      cube([50, wallthick, 50]);
+      translate(sw_ctr)
+       rotate([0,0,90])
+       S1930_Positive();
+    }
+
+    translate(sw_ctr) {
+      rotate([0,0,90])
+       S1930_Negative();
+    }
+  }    
+}
+
+ts_totx = 30;
+ts_toty = 25;
+ts_totz_inner = 8;
+
+ts_box_sz = [ts_totx, ts_toty, ts_totz_inner];
+
+$sealingbox_wallth = wallthick;
+$sealingbox_floorth = wallthick;
+$sealingbox_ceilth = wallthick;
+
+module TestSealBox(){ ////toplevel
+  $sealingbox_sz = ts_box_sz;
+
+  SealingBox_RectBox();
+  ts_cidoff = ($sealingbox_cnrrad * (1-.7) + wallthick * .8) * [1,1];
+  translate(ts_cidoff)
+    Commitid_BestCount([ts_totx,ts_toty] - 2*ts_cidoff);
+}
+
+module TestSealLid(){ ////toplevel
+  $sealingbox_sz = ts_box_sz;
+
+  difference(){
+    SealingBox_RectLid();
+
+    translate([ts_totx * .75, ts_toty/2, 0])
+      cylinder(h=100, r=5);
+    
+    translate([-wallthick + $sealingbox_cnrrad*.5,
+              $sealingbox_cnrrad*.5 - wallthick,
+              ts_totz_inner + $sealingbox_ceilth])
+      Commitid_BestCount([ts_totx * .75 - 2.5 - ($sealingbox_cnrrad*.5),
+                         ts_toty - ($sealingbox_cnrrad*.5 - wallthick)*2]);
+  }
+}
+
+module TestSealLidPrint(){ ////toplevel
+  rotate([180,0,0]) TestSealLid();
+}
+
+module ProfileDemos(){ ////toplevel
+  $sealingbox_sz = ts_box_sz;
+
+  SealingBox_WallProfile();
+  color("blue") SealingBox_FloorProfile();
+  SealingBox_LidProfile();
+  color("blue") SealingBox_CeilProfile();
+  color("red") translate([-5,0]) square([1,ts_totz_inner]);
+}
+
+module AtGlands(){
+  for (dgy=[-15,-45]) {
+    translate([totx_inner + wallthick - $sealingbox_cnrrad * .3,
+              toty_inner + dgy,
+              totz_inner/2])
+      children();
+  }
+}
+
+module StrapKeepers(at){
+  strap_x_tot = strap_w + strap_pillar*2;
+
+  for (sx= at) {
+    echo("strapkeeper at ",sx);
+    translate([sx - strap_x_tot, 0, 0])
+      difference(){
+      translate([0,0, -0.1])
+       cube([strap_x_tot, strap_pillard, strap_th + strap_over]);
+      translate([strap_pillar, -1, 0])
+       cube([strap_w, strap_pillard+2, strap_th]);
+    }
+  }
+}
+
+chargingconn_x = pxp6012_rad_outer + 1 + $sealingbox_cnrrad;
+switch_x = chargingconn_x + pxp6012_rad_outer
+  + s1930_y_outer/2 + s1930_around;
+
+module AtSealingBox(){
+  rotate([90,0,0])
+    translate([-wallthick,-wallthick, -toty_inner])
+    children();
+}
+
+module Box(){ ////toplevel
+  $sealingbox_sz = sb_box_sz;
+
+  difference(){
+    union(){
+      AtSealingBox()
+       SealingBox_RectBox();
+
+      translate([switch_x, toty_inner, totz_inner/2])
+       rotate([90,0,90])
+       S1930_Positive();
+
+      // keepers for lipo
+      for (keepers= [[ 35, lipokeeper_d_min,     lipokeeper_h,
+                      [ 40, 80, 120, 150 ] ],
+                    [ 10, lipokeeper_end_d_min, lipokeeper_end_h,
+                      [ 25 ] ]
+                    // each entry: [ y, d_min, h, [ x, ...] ]
+                    ])
+       for (kx= keepers[3]) {
+         translate([kx, keepers[0], -1])
+           hull(){
+             cube([lipokeeper_w, keepers[1], keepers[2] +1]);
+             cube([lipokeeper_w,
+                   keepers[1] + keepers[2] / lipokeeper_slope,
+                   1]);
+           }
+      }
+
+      AtGlands()
+       GlandPositive(cabledia);
+
+      translate([0, toty_inner+wallthick, -wallthick])
+       rotate([180, 0,0])
+       StrapKeepers(straps_at_box);
+    }
+
+    // charging connector
+    translate([chargingconn_x,
+              toty_inner - (pxp6012_rad_outer + 5),
+              10])
+      cylinder(r= pxp6012_rad, h= totz_outer);
+
+    // vent connector
+    translate([chargingconn_x,
+              toty_inner - (pxp6012_rad_outer*2 + 5 + 15 +
+                            jdae12pa_rad_outer),
+              10])
+      cylinder(r= jdae12pa_rad, h= totz_outer);
+
+    translate([switch_x, toty_inner, totz_inner/2])
+      rotate([90,0,90])
+      S1930_Negative();
+
+    AtGlands()
+      GlandNegative(cabledia);
+
+    translate(-$sealingbox_cnrrad * [1,1,0] +
+             [totx_inner, toty_inner/2, -wallthick])
+      rotate([0,0,180])
+      scale([2,2,1])
+      Commitid_Full16_M();
+  }
+}
+
+module BoxPrint(){ ////toplevel
+  rotate([-90,0,-90])
+    Box();
+}
+
+module Lid(){ ////toplevel
+  $sealingbox_sz = sb_box_sz;
+  difference(){
+    union(){
+      AtSealingBox()
+       SealingBox_RectLid();
+      translate([0, -wallthick, -SealingBox_lidbigger()])
+       mirror([0,0,1])
+       StrapKeepers([ straps_every : straps_every
+                      : totx_inner-straps_every ]);
+    }
+
+    translate($sealingbox_cnrrad * [1,0,1])
+      rotate([90,0,0])
+      scale([1.5, 1.5, 1])
+      Commitid_Small16_M();
+  }
+}
+
+module LidPrint(){ ////toplevel
+  rotate([90,0,-90])
+    Lid();
+}
+
+module Demo(){ ////toplevel
+  color("blue") Box();
+  color("red") Lid();
+}
+
+//TestWall();
+//ProfileDemos();
+//TestSealBox();
+//TestSealLid();
+//FArcSegment_mask(350);
+//StrapKeepers();
diff --git a/bike-phone-mount.scad b/bike-phone-mount.scad
new file mode 100644 (file)
index 0000000..bb69726
--- /dev/null
@@ -0,0 +1,101 @@
+// -*- C -*-
+
+// should rename this to actual name of the product
+
+include <utils.scad>
+
+mount_lip_height = 2.0 - 0.15 - 0.15;
+mount_lip_depth = 2.5 /*?*/ - 0.30;
+mount_neck_width = 26.5 - 0.55 - 0.15;
+mount_neck_length = 1.5 + 0.50;
+
+mount_diag_outer = 34.8        - 0.50;
+mount_diag_inner = 34.6 - 0.20 - 0.50;
+
+mount_slope = .65;
+mount_extra_slope = 3;
+
+mount_demo_ceil = 4;
+
+// calculated
+
+mnep0 = [0,0];
+mnep1 = mnep0 + [0,1] * mount_neck_length;
+mnep7 = mnep0 + [1,0] * mount_lip_depth;
+mnep2 = [ mnep7[0] + mount_extra_slope, mnep1[1] + mount_slope * (mnep7[0] + mount_extra_slope - mnep1[0]) ];
+mnep3 = mnep2 + [0, 0.1];
+mnep4 = [ mnep0[0]-1, mnep3[1] ];
+mnep6 = mnep7 + [0,-1] * mount_lip_height;
+mnep5 = [ mnep4[0], mnep6[1] ];
+mnepm = [ mnep0[0], mnep3[1] ];
+
+mount_total_height = mnep2[1] - mnep6[1];
+mnep_z_offset = -mnep2[1];
+mnep_side_offset = [ mount_neck_width/2, mnep_z_offset ];
+
+module MountNeckEdgePlan() {
+  polygon([ mnep0,
+           mnep1,
+           mnep2,
+           mnep3,
+           mnep4,
+           mnep5,
+           mnep6,
+           mnep7 ]);
+}
+
+module MountNeckSquare() {
+  intersection_for (r=[0,90]) {
+    rotate([0,0,r]){
+      linextr_y_xz(-100,100,convexity=10){
+       for (m=[0,1]) {
+         mirror([m,0]) {
+           translate(mnep_side_offset) MountNeckEdgePlan();
+           rectfromto([-0.1, -mount_total_height],
+                      mnep_side_offset + mnepm);
+         }
+       }
+      }
+    }
+  }
+}
+
+module MountDiagonal() {
+  rotate([0,0,45]){
+    translate([0,0, -mount_total_height]){
+      linextr(0, mount_lip_height)
+       square(center=true, mount_diag_outer);
+      linextr(0, mount_total_height)
+       square(center=true, mount_diag_inner);
+      linextr(mount_lip_height + mount_neck_length,
+             mount_total_height + 1)
+       square(center=true, 100);
+    }
+  }
+}
+
+module MountDemoCeil() {
+  c = mount_demo_ceil + mount_extra_slope;
+  linextr(0, 0.8) {
+    square(mount_neck_width + 2*(mount_demo_ceil + mount_extra_slope),
+          center=true);
+  }
+}
+
+module Mount(){
+  intersection(){
+    MountNeckSquare();
+    MountDiagonal();
+  }
+}
+
+module MountDemo(){ ////toplevel
+  Mount();
+  MountDemoCeil();
+}
+
+//MountNeckEdgePlan();
+//MountNeck();
+//MountDemoCeil();
+//MountDiagonal();
+//MountDemo();
diff --git a/bike-stalk-led-mount.scad b/bike-stalk-led-mount.scad
new file mode 100644 (file)
index 0000000..a3c0c4e
--- /dev/null
@@ -0,0 +1,71 @@
+// -*- C -*-
+
+include <commitid.scad>
+
+stalk_dia = 6.4 + 0.25;
+
+length = 50;
+width = 12;
+
+strap_below = 2;
+strap_thick = 2;
+strap_width = 5;
+strap_above = 0.25;
+
+arch_above = 2;
+
+inside_gap = 0.5;
+
+// calculated
+
+height_base = stalk_dia/2 - inside_gap/2;
+above_height = height_base + arch_above;
+below_height = height_base + max(arch_above,
+                                strap_below + strap_thick + strap_above);
+
+module StalkCutout(){
+  translate([-length,0,0])
+    rotate([0,90,0])
+    cylinder(r= stalk_dia/2, h=length*2, $fn=40);
+}
+
+module SomeBlockBase(height){
+  translate([0,0, height/2 + inside_gap/2]) {
+    difference(){
+      cube([length, width, height], center=true);
+      translate([-length/2, 0, height/2])
+       Commitid_BestCount([length*.66, width/2]);
+    }
+  }
+}
+
+module BlockAbove(){ ////toplevel
+  difference(){
+    SomeBlockBase(above_height);
+    StalkCutout();
+  }
+}
+
+module BlockBelow(){ ////toplevel
+  difference(){
+    SomeBlockBase(below_height);
+    StalkCutout();
+    translate([0,0, inside_gap/2 + strap_above + stalk_dia/2 + strap_thick/2])
+      cube([strap_width, width*2, strap_thick], center=true);
+  }
+}
+
+module BlockAbovePrint(){ ////toplevel
+  rotate([180,0,0]) BlockAbove();
+}
+
+module BlockBelowPrint(){ ////toplevel
+  rotate([180,0,0]) BlockBelow();
+}
+
+module Demo(){ ////toplevel
+  BlockAbove();
+  rotate([180,0,0]) BlockBelow();
+}
+
+//Demo();
diff --git a/biscuits.scad b/biscuits.scad
new file mode 100644 (file)
index 0000000..5f6a42a
--- /dev/null
@@ -0,0 +1,30 @@
+
+scale=0.75;
+rad=30*scale;
+hbase=28.4*scale;
+voff=10*scale;
+height=70*scale;
+
+wallheight = 15;
+wallthick=0.8;
+
+module flatsolid() {
+       circle(r=rad,$fn=50);
+       polygon(points=[[-hbase,voff],[hbase,voff],[0,height]]);
+}
+
+module mink() {
+       minkowski() {
+               flatsolid();
+               circle(r=wallthick/2);
+       }
+}   
+
+module hollow() {
+       difference() {
+               mink();
+               flatsolid();
+       }
+}
+
+linear_extrude(height=wallheight) hollow();
diff --git a/brompton-computer-guard.scad b/brompton-computer-guard.scad
new file mode 100644 (file)
index 0000000..f0154c0
--- /dev/null
@@ -0,0 +1,107 @@
+// -*- C -*-
+
+arch_height = 18;
+arch_width = 75;
+end_width = 25;
+
+arch_thick = 4;
+
+arch_breadth = 25;
+
+hole_dia = 4 + 0.5;
+
+pbase_tab = 12;
+pbase_thick = 4;
+inner_pbase_thick = 12;
+inner_pbase_rad_mul = 3;
+
+// computed
+
+arch_alpha = atan(arch_height / (arch_width/2));
+arch_beta = 2*arch_alpha;
+echo(arch_alpha,arch_beta);
+arch_in_rad = arch_width/2 / sin(arch_beta);
+arch_to_chord = arch_in_rad * cos(arch_beta);
+
+echo(inner_pbase_thick);
+
+inner_pbase_rad = arch_in_rad * inner_pbase_rad_mul;
+
+end_thick = arch_thick;
+
+holes = [[[  5  , 5  ], [16  , 21]], // left
+        [[ 18.5, 4.5], [ 4.5, 21]]]; // right
+
+module ArchCircle(rad){
+  translate([0,-arch_to_chord])
+    circle(rad, $fa=0.1);
+}
+
+module ArchProfile(pbase){
+  intersection(){
+    translate([-200,0])
+      square([400,200]);
+    difference(){
+      union(){
+       ArchCircle(arch_in_rad + arch_thick);
+       for (m=[0,1])
+         mirror([m,0])
+           translate([arch_width/2,0])
+           multmatrix([[1,pbase ? -0.75 : 0,0,0],
+                       [0,1,0,0],
+                       [0,0,1,0],
+                       [0,0,0,1]])
+           square([end_width, pbase ? pbase_tab : end_thick]);
+      }
+    }
+  }
+}
+
+module Holes(){
+  for (m=[0,1]) {
+    mirror([1-m,0])
+      translate([arch_width/2, 50, 0])
+      rotate([90,0,0])
+      for (h=holes[m]) {
+       translate(h)
+         cylinder(r=hole_dia/2, h=100, $fn=20);
+      }
+  }
+}
+
+module MainCutout(){
+  ArchCircle(arch_in_rad);
+}
+
+module Arch(){
+  difference(){
+    rotate([0,0,180]){
+      linear_extrude(height=arch_breadth) {
+       difference(){
+         ArchProfile(false);
+         MainCutout();
+       }
+      }
+      difference(){
+       translate([0,0, arch_breadth - pbase_thick])
+       linear_extrude(height=pbase_thick){
+         difference(){
+           hull(){
+             ArchProfile(true);
+             ArchProfile(false);
+           }
+           intersection(){
+             MainCutout();
+             translate([0, -inner_pbase_thick
+                        - (inner_pbase_rad - arch_in_rad)])
+               ArchCircle(inner_pbase_rad);
+           }
+         }
+       }
+      }
+    }
+    Holes();
+  }
+}
+
+rotate([0,0,45]) translate([0,0,arch_breadth]) rotate([0,180,0]) Arch();
diff --git a/cable-hole-trunking-cover.scad b/cable-hole-trunking-cover.scad
new file mode 100644 (file)
index 0000000..ed2bc31
--- /dev/null
@@ -0,0 +1,119 @@
+// -*- C -*-
+
+holedia = 25;
+tapethick = 1.5;
+cutoutsz= 15;
+innerz = 11;
+
+sidesflatbase = 2;
+endsflatbase = 8;
+
+basex = holedia + endsflatbase*2;
+basey = holedia + sidesflatbase*2;
+
+bevely = 2.75;
+bevelslope = 0.75;
+bevelz = bevely / bevelslope;;
+basebevelt = 3;
+
+sideslop = 0.5;
+
+basebaset = 2;
+sidewallt = 2;
+
+lidt = 1.3;
+endwallt = 2;
+zslop = 0.75;
+endslop = 0.75;
+
+module sheared_cube(sz, xperz, yperz) {
+  multmatrix([[1,0,xperz,0],
+             [0,1,yperz,0],
+             [0,0,1,    0],
+             [0,0,0,    1]])
+    cube(sz);
+}
+
+module Base(cutouty){
+  echo(cutouty);
+  difference(){
+    union(){
+      for (mir=[0,1]) mirror([0,mir,0]) {
+       translate([0, basey/2 - basebevelt, 0])
+         sheared_cube([basex, basebevelt, bevelz], 0, bevelslope);
+       cube([basex, basey/2, basebaset]);
+      }
+    }
+    translate([basex/2, 0, -1])
+      cylinder(r=holedia/2, h=bevelz+2);
+  }
+  rotate([90, 0, 90]) {
+    linear_extrude(height=endwallt) {
+      difference(){
+       for (mir=[0,1]) mirror([mir,0,0]) {
+           polygon([[-0.1,             0],
+                    [basey/2,          0],
+                    [basey/2 + bevely, bevelz],
+                    [basey/2 + bevely, innerz],
+                    [-0.1,             innerz]]);
+       }
+       translate([cutouty, 0])
+         square(size=[cutoutsz, 3*innerz], center=true);
+      }
+    }
+  }
+}
+
+module Lid(){
+  lidx = basex + endslop + endwallt;
+  for (mir=[0,1]) mirror([0,mir,0]) {
+    translate([0, basey/2 + sideslop + bevely, 0])
+      rotate([90,0,90])
+      linear_extrude(height = lidx)
+      polygon([[0,         0],
+              [-bevely,   0],
+              [0,         bevelz],
+              [0,         innerz + lidt + zslop],
+              [sidewallt, innerz + lidt + zslop],
+              [sidewallt, -tapethick],
+              [0,         -tapethick]]);
+    translate([0, -1, innerz + zslop])
+      cube([lidx, 1 + basey/2 + sideslop + bevely + sidewallt, lidt]);
+    translate([basex + endslop, -1, -tapethick])
+      cube([endwallt, 1 + basey/2 + sideslop + bevely + sidewallt,
+           tapethick + innerz + zslop + 0.1]);
+  }
+}
+
+module LidT(){ ////toplevel
+  rotate([180,0,0]) Lid();
+}
+
+module BaseCMid(){ ////toplevel
+  Base(0);
+}
+
+module BaseCTop(){ ////toplevel
+  Base(basey/2 + bevely - cutoutsz/2);
+}
+
+module BaseCBot(){ ////toplevel
+  Base(-(basey/2 + bevely - cutoutsz/2));
+}
+
+module BaseCNone(){ ////toplevel
+  Base(basey);
+}
+
+module Demo(){ ////toplevel
+  BaseCTop();
+  %Lid();
+}
+
+//BaseCTop();
+//BaseCMid();
+//BaseCBot();
+//BaseCNone();
+//Lid();
+//LidT();
+//Demo();
diff --git a/cable-splice-clamp.scad b/cable-splice-clamp.scad
new file mode 100644 (file)
index 0000000..bd27d86
--- /dev/null
@@ -0,0 +1,138 @@
+// -*- C -*-
+
+include <commitid.scad>
+
+rnom = 3.5 / 2;
+
+// alpha is slope angle, which is half of inner concave angle that
+//  wire sits in
+alpha = 40; // degrees
+
+// mu is minimum number of cable radii that cable tangent point (line)
+//  with splint ought to be away from edge of split
+mu = 1/4;
+
+// wall thickness, and base width as fraction of cable size
+wall_r = 3.5 / 6.5;
+base_r = 0.75;
+
+total_len = 60;
+
+strap_width = 3.0 + 0.5;
+
+strap_count = 4;
+
+// for cross-section calculations:
+//
+// origin O is at intersection of straight line segments forming walls
+// C is centre of circle (wire x-section) (of radius r or radius 1)
+//       which is tangent to lines
+// T is said tangent points
+// B is inner base point, which is extension of line from B by mu*r
+
+sina = sin(alpha);
+cosa = cos(alpha);
+tana = sina/cosa;
+
+// blah_r is blah where r=1
+// d_AB is distance AB
+// dy_AB is " " " vertical component
+
+d_OT_r = tana;
+d_OB_r = tana + mu;
+
+d_OC_r = 1/cosa;
+
+dy_OB_r = d_OB_r * sina;
+
+// *0 and *1 relate to smallest and largest wire
+// r[01] is radius
+// r10 is radius ratio
+
+r10 = d_OC_r / dy_OB_r;
+
+r0 = rnom / sqrt(r10);
+r1 = rnom * sqrt(r10);
+
+x_B_r =   d_OB_r * cosa;
+y_B_r = -dy_OB_r;
+
+x_T_r =         sina;
+y_T_r = -tana * sina;
+
+wall_x_r = wall_r / tan(90-alpha);
+
+top = wall_r * r1 - (d_OC_r - 1) * r0;
+basew = base_r * rnom;
+
+echo("dias", r0*2, r1*2, "ratio",r1/r0);
+
+module CrossSectionHalf(plus=0) {
+  difference(){
+    polygon([[-0.1,                                y_T_r * r0],
+            [x_T_r * r0,                          y_T_r * r0],
+            [x_B_r * r1,                          y_B_r * r1],
+            [x_B_r * r1 + wall_x_r * rnom + plus, y_B_r * r1],
+            [basew                        + plus, top],
+            [-0.1,                                top]]);
+    translate([0, -d_OC_r * r0])
+      circle(r = r0, $fn=20);
+  }
+}
+
+module CrossSection(plus=0) {
+  for (m=[0,1]) {
+    mirror([m,0])
+      CrossSectionHalf(plus);
+  }
+}
+
+module CrossSectionDemo(){ ////toplevel
+  color("black") CrossSection(2);
+  CrossSection();
+  for (rc=[["red", r1],
+          ["blue",r0]]) {
+    color(rc[0]) translate([0, -d_OC_r * rc[1]]) circle(r = rc[1]);
+  }
+}
+
+strap_wall_h = 1.5;
+strap_wall_l = 2.0;
+
+writing_dx = total_len / 3;
+writing_dy = basew*2;
+
+module HalfClamp(){ ////toplevel
+  difference(){
+    rotate([90,0,0])rotate([0,90,0]){
+      linear_extrude(height=total_len)
+       CrossSection();
+
+      for (i=[0 : strap_count]){
+       if (i*2 != strap_count) {
+         translate([0, 0,
+                    total_len * (i + 0.5) / (strap_count + 1)])
+           for (m=[0,1]){
+             mirror([0,0,m])
+               translate([0,0, strap_width/2])
+               linear_extrude(height=strap_wall_l)
+               CrossSection(strap_wall_h);
+           }
+       }
+      }
+    }
+
+    translate([0, -basew, top])
+      Commitid_BestCount([writing_dx, writing_dy]);
+  }
+}
+
+module HalfClampPrint(){ ////toplevel
+  rotate([180,0,0])
+    HalfClamp();
+}
+
+//CrossSection();
+//CrossSectionDemo();
+//HalfClamp();
+HalfClampPrint();
diff --git a/calib-fit.scad b/calib-fit.scad
new file mode 100644 (file)
index 0000000..dbee918
--- /dev/null
@@ -0,0 +1,20 @@
+th=3;
+holesz=6;
+outsz=15;
+
+module small(sz=holesz,dsz=0,dz=0) {
+       cube([sz+dsz,sz+dsz,th+dz], center=true);
+}
+
+module osmall() {
+       translate([0,outsz/2 + 10,0]) small();
+}
+module obig() {
+       difference() {
+               cube([outsz,outsz,th], center=true);
+               small(dz=1);
+       }
+}
+
+osmall();
+obig();
diff --git a/camera-mount.scad b/camera-mount.scad
new file mode 100644 (file)
index 0000000..fec9096
--- /dev/null
@@ -0,0 +1,28 @@
+// -*- C -*-
+
+include <threads.scad>
+
+inch = 25.4;
+
+negative_dia = inch * 1/4. + 0.375;
+negative_default_l =   10.0;
+
+negative_tpi = 20;
+negative_pitch = inch/negative_tpi;
+negative_chamfer = negative_pitch/2;
+
+module CameraMountThread(l){
+  rotate([0,180,0])
+    english_thread(diameter=negative_dia/inch,
+                  threads_per_inch=negative_tpi,
+                  leadin=0, internal=true, test=$test,
+                  length= (l + inch/19) / inch);
+  hull(){
+    translate([0,0, negative_chamfer])
+      cylinder(r= negative_dia/2 + negative_chamfer*2,
+              h=1);
+    mirror([0,0,1])
+      cylinder(r= negative_dia/2 - negative_chamfer*2,
+                  h= negative_chamfer*3);
+  }
+}
diff --git a/clip-spring-holder-clip.scad b/clip-spring-holder-clip.scad
new file mode 100644 (file)
index 0000000..11f395f
--- /dev/null
@@ -0,0 +1,48 @@
+// -*- C -*-
+//
+// For holding the spring while reassembling a candle holder.
+
+include <utils.scad>
+
+spring_body_w = 5.0;
+spring_body_l = 6.0;
+axle_dia = 2.0;
+recess_d = 13.0;
+total_len = 45.0;
+
+th_y = 1.5;
+th_x = 2;
+handle_th = 2.5;
+
+// calculated
+
+outer_sz = [spring_body_l + th_x*2, spring_body_w + th_y*2];
+handle_sz = [outer_sz[0], handle_th];
+th_z = th_x;
+
+echo(outer_sz);
+
+module OuterElevation(){
+  square(center=true, outer_sz);
+}
+
+module Elevation(){
+  difference(){
+    OuterElevation();
+
+    square(center=true, [spring_body_l, spring_body_w]);
+    square(center=true, [outer_sz[0] + 10, axle_dia]);
+  }
+}
+
+module Clip(){
+  linextr(-th_z, recess_d) Elevation();
+  linextr(-th_z, 0) OuterElevation();
+  linextr(recess_d - total_len, 0) square(center=true, handle_sz);
+}
+
+module Print(){
+  rotate([0, 90, 0]) Clip();
+}
+
+Print();
diff --git a/cliphook.scad b/cliphook.scad
new file mode 100644 (file)
index 0000000..22f08f5
--- /dev/null
@@ -0,0 +1,103 @@
+// -*- C -*-
+//
+// cliphook.scad
+//
+// 3D design for a small clippy hook
+// Copyright 2012,2016 Ian Jackson
+//
+// This work is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This work 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 work.  If not, see <http://www.gnu.org/licenses/>.
+
+function ClipHook_r2(w,g,l,ye,k) = w/2 + g + w/2;
+function ClipHook_r3(w,g,l,ye,k) = k * (ClipHook_r2(w,g,l,ye,k) + w + g);
+function ClipHook_yd(w,g,l,ye,k) = g/2 + w + g + w/2 - ClipHook_r3(w,g,l,ye,k);
+function ClipHook_xe(w,g,l,ye,k) =
+        l*1.5 + w +
+       sqrt(pow( ClipHook_r3(w,g,l,ye,k),      2) -
+            pow( ClipHook_yd(w,g,l,ye,k) - ye, 2));
+
+module FlatArc(cx,cy,r1,r2,a1,a2=361,$fn=$fn) {
+  astep = (a2-a1)/6;
+  size = 5*(r2/2);
+  translate([cx,cy,0]) {
+    intersection() {
+      difference() {
+       circle(r=r2);
+       translate([0,0,-1])
+         circle(r=r1);
+      }
+      scale(size) {
+       for (ai=[0:4]) {
+         //echo(" jarc ", a1,a2, astep, ai, a1 + astep*ai );
+         rotate(a1 + astep*ai) {
+           polygon([ [0,0], [1,0],
+                     [cos(astep*2),sin(astep*2)] ]);
+         }
+       }
+      }
+    }
+  }
+}
+
+module ClipHook_2D(w,g,l,ye,k,h) {
+  r2 =  ClipHook_r2(w,g,l,ye,k);
+  r3 =  ClipHook_r3(w,g,l,ye,k);
+  yd =  ClipHook_yd(w,g,l,ye,k);
+  xe =  ClipHook_xe(w,g,l,ye,k);
+
+  xd = l*1.5 + w;
+  xc = -l/2;
+  yc = g/2 + w/2;
+
+  alpha = atan2((xe-xd)/r3, (ye-yd)/r3);
+
+  echo("ClipHook(w g l ye k h) ", w, g, l, ye, k, h);
+  echo("ClipHook r2 r3 xd yd xe =", r2,r3, xd,yd, xe);
+
+  $fn = 20;
+
+  module jcirc(x,y) { translate([x,y,0]) circle(r=w/2); }
+  module jbox(y,x1,x2) { translate([x1,y-w/2,0]) square(size=[x2-x1, w]); }
+  module jarc(cx,cy,r,a1=0,a2=360) { FlatArc(cx,cy,r-w/2,r+w/2,a1,a2); }
+
+  jcirc(-xc, -yc);
+  jbox(-yc, xc, -xc);
+  jarc(xc, yc, r2, 90, 270);
+  jbox(yc+r2, xc, xd);
+  jarc(xd, yd, r3, 90-alpha, 90);
+  jcirc(xe,ye);
+}
+
+module ClipHook(w=1.2, g=0.2, l=0.0, ye=0, k=2.0, h=3.5, demo=false,
+               cupcaph=0, cupgapg=0) {
+  difference() {
+    linear_extrude(height=h)
+      ClipHook_2D(w,g,l,ye,k);
+    if (cupcapg != 0) {
+      translate([-g+0.01,-(w+g),h-cupcapg])
+      cube([w,(w+g),cupcaph+1]);
+    }
+  }
+  if (cupcaph != 0) {
+    translate([-l/2, g/2+w/2, h-0.01])
+      intersection() {
+        cylinder(r=ClipHook_r2(w,g,l,ye,k)+w*0.4, h=cupcaph, $fn=16);
+       translate([-50-g,-50,-1]) cube([50,100,h+2]);
+      }
+  }
+}
+
+if (ClipHook_demo) {
+  ClipHook(l=0, k=1, cupcaph=1, cupcapg=0.4);
+  %translate([l+w,0,0]) rotate(180) ClipHook(l=0, k=1, cupcaph=1, cupcapg=0.4);
+}
diff --git a/commitid-2d-test.scad b/commitid-2d-test.scad
new file mode 100644 (file)
index 0000000..a15de77
--- /dev/null
@@ -0,0 +1,3 @@
+// -*- C -*-
+include <commitid.scad>
+Commitid_2DDemo();
diff --git a/commitid-best-test.scad.pl b/commitid-best-test.scad.pl
new file mode 100755 (executable)
index 0000000..b0ca1ba
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/perl -w
+use strict;
+
+our @xm = qw(4.7 6.8 8.2 10 12 15 18 22 27 33 47 68 100);
+our @ym = qw(3.9 5.6 8.2 12 18 27 39 56);
+
+sub p { print @_ or die $!; }
+
+p "include <commitid.scad>\n";
+p "p = Commitid_pixelsz();\n";
+
+my $x = 0;
+foreach my $xm (@xm) {
+    my $y = 0;
+    foreach my $ym (@ym) {
+       p " translate([$x,$y] * p) {\n";
+       p "  difference(){\n";
+       p "   translate(-0.5*p*[1,1]) square([$xm+1,$ym+1]*p);\n";
+       p "   square([$xm,$ym]*p);\n";
+       p "  }\n";
+       p "  Commitid_BestCount_2D([$xm,$ym] * p, margin=0.2);\n";
+       p " }\n";
+       $y += $ym + 2;
+    }
+    $x += $xm + 2;
+}
diff --git a/commitid-cube-test-X.scad b/commitid-cube-test-X.scad
new file mode 100644 (file)
index 0000000..4f2f6fd
--- /dev/null
@@ -0,0 +1,76 @@
+// -*- C -*-
+
+include <commitid.scad>
+
+baseh= 1;
+
+sz = 20;
+
+fdo = [1, 3, 0];
+
+$Commitid_depth = 1.0;
+
+module FD () {
+    translate(fdo)
+      Commitid_FontDemo();
+}
+
+module TC () { ////toplevel
+  difference(){
+    cube([sz,sz,sz]);
+    translate([0,0, sz]) mirror([0,0,1]) FD();
+    rotate([90,0,0]) translate([0,0,0]) FD();
+    translate([sz,0,0]) mirror([1,0,0]) rotate([90,0,90]) FD();
+    translate([sz,sz,0]) rotate([0,0,180]) FD();
+  }
+  translate([sz,sz,0]) rotate([-90,0,0]) rotate([0,0,180]) FD();
+  translate([0,sz,0]) rotate([-90,0,90]) rotate([0,0,180]) FD();
+}
+
+w = 3;
+t = 4;
+
+fdsz = Commitid_FontDemo_sz();
+d = Commitid_depth();
+ru = Commitid_pixelsz();
+
+module TTWall () {
+  difference(){
+    translate([0, 0, -0.1])
+      cube([w, sz, sz - 2 + 0.1]);
+
+    translate([0,sz,0]) rotate([90,0,-90]) FD();
+    translate([0, sz, 0])
+      rotate([90, 0, -90])
+      translate(fdo + [0, -ru*2, -d]) cube([fdsz[0], ru, d*2]);
+  }
+  translate([w,0,0]) rotate([90,0,90]) FD();
+
+  translate([0, sz+d, 0])
+    rotate([90,0,0])
+    translate([0, fdo[1], 0]) cube([d*2, fdsz[1], ru]);
+}
+
+module TT () { ////toplevel
+  difference(){
+    translate([-sz, 0, -t])
+      cube([sz*2 + w, sz, t]);
+
+    translate([0,0,-t]) rotate([0,180,0]) FD();
+    translate([w,0,0]) rotate([0,0,0]) FD();
+
+    translate([(sz+w), 0, -t]) rotate([0,180,0])
+      Commitid_BestCount([sz+w, sz]);
+  }
+  translate([-sz,0,0]) rotate([0,0,0]) FD();
+
+  TTWall();
+  translate([0,0,-t]) rotate([90,0,0]) TTWall();
+}
+
+echo("pixelsz:", str(Commitid_pixelsz()),
+     "depth:", Commitid_depth(),
+     "sz:", Commitid_FontDemo_sz());
+
+//TC();
+TT();
diff --git a/commitid-cube-test-Y.scad b/commitid-cube-test-Y.scad
new file mode 100644 (file)
index 0000000..23aa076
--- /dev/null
@@ -0,0 +1,78 @@
+// -*- C -*-
+
+$Commitid_depth = 1.0;
+$Commitid_pixelsz = 1.5;
+
+include <commitid.scad>
+
+baseh= 1;
+
+fdo = [1, 3, 0];
+
+w = 3;
+t = 4;
+
+fdsz = Commitid_FontDemo_sz();
+d = Commitid_depth();
+ru = Commitid_pixelsz();
+echo($Commitid_pixelsz, ru, fdsz);
+
+sz = max( fdsz[0], fdsz[1] ) + ru;
+
+module FD () {
+    translate(fdo)
+      Commitid_FontDemo();
+}
+
+module TC () { ////toplevel
+  difference(){
+    cube([sz,sz,sz]);
+    translate([0,0, sz]) mirror([0,0,1]) FD();
+    rotate([90,0,0]) translate([0,0,0]) FD();
+    translate([sz,0,0]) mirror([1,0,0]) rotate([90,0,90]) FD();
+    translate([sz,sz,0]) rotate([0,0,180]) FD();
+  }
+  translate([sz,sz,0]) rotate([-90,0,0]) rotate([0,0,180]) FD();
+  translate([0,sz,0]) rotate([-90,0,90]) rotate([0,0,180]) FD();
+}
+
+module TTWall () {
+  difference(){
+    translate([0, 0, -0.1])
+      cube([w, sz, sz - 2 + 0.1]);
+
+    translate([0,sz,0]) rotate([90,0,-90]) FD();
+    translate([0, sz, 0])
+      rotate([90, 0, -90])
+      translate(fdo + [0, -ru*2, -d]) cube([fdsz[0], ru, d*2]);
+  }
+  translate([w,0,0]) rotate([90,0,90]) FD();
+
+  translate([0, sz+d, 0])
+    rotate([90,0,0])
+    translate([0, fdo[1], 0]) cube([d*2, fdsz[1], ru]);
+}
+
+module TT () { ////toplevel
+  difference(){
+    translate([-sz, 0, -t])
+      cube([sz*2 + w, sz, t]);
+
+    translate([0,0,-t]) rotate([0,180,0]) FD();
+    translate([w,0,0]) rotate([0,0,0]) FD();
+
+    translate([(sz+w), 0, -t]) rotate([0,180,0])
+      Commitid_BestCount([sz+w, sz]);
+  }
+  translate([-sz,0,0]) rotate([0,0,0]) FD();
+
+  TTWall();
+  translate([0,0,-t]) rotate([90,0,0]) TTWall();
+}
+
+echo("pixelsz:", str(Commitid_pixelsz()),
+     "depth:", Commitid_depth(),
+     "sz:", Commitid_FontDemo_sz());
+
+//TC();
+TT();
diff --git a/commitid-cube-test.scad b/commitid-cube-test.scad
new file mode 100644 (file)
index 0000000..0a5f21a
--- /dev/null
@@ -0,0 +1,74 @@
+// -*- C -*-
+
+include <commitid.scad>
+
+baseh= 1;
+
+sz = 20;
+
+fdo = [1, 3, 0];
+
+module FD () {
+    translate(fdo)
+      Commitid_FontDemo();
+}
+
+module TC () { ////toplevel
+  difference(){
+    cube([sz,sz,sz]);
+    translate([0,0, sz]) mirror([0,0,1]) FD();
+    rotate([90,0,0]) translate([0,0,0]) FD();
+    translate([sz,0,0]) mirror([1,0,0]) rotate([90,0,90]) FD();
+    translate([sz,sz,0]) rotate([0,0,180]) FD();
+  }
+  translate([sz,sz,0]) rotate([-90,0,0]) rotate([0,0,180]) FD();
+  translate([0,sz,0]) rotate([-90,0,90]) rotate([0,0,180]) FD();
+}
+
+w = 3;
+t = 2;
+
+fdsz = Commitid_FontDemo_sz();
+d = Commitid_depth();
+ru = Commitid_pixelsz();
+
+module TTWall () {
+  difference(){
+    translate([0, 0, -0.1])
+      cube([w, sz, sz - t + 0.1]);
+
+    translate([0,sz,0]) rotate([90,0,-90]) FD();
+    translate([0, sz, 0])
+      rotate([90, 0, -90])
+      translate(fdo + [0, -ru*2, -d]) cube([fdsz[0], ru, d*2]);
+  }
+  translate([w,0,0]) rotate([90,0,90]) FD();
+
+  translate([0, sz+d, 0])
+    rotate([90,0,0])
+    translate([0, fdo[1], 0]) cube([d*2, fdsz[1], ru]);
+}
+
+module TT () { ////toplevel
+  difference(){
+    translate([-sz, 0, -t])
+      cube([sz*2 + w, sz, t]);
+
+    translate([0,0,-t]) rotate([0,180,0]) FD();
+    translate([w,0,0]) rotate([0,0,0]) FD();
+
+    translate([(sz+w), 0, -t]) rotate([0,180,0])
+      Commitid_BestCount([sz+w, sz]);
+  }
+  translate([-sz,0,0]) rotate([0,0,0]) FD();
+
+  TTWall();
+  translate([0,0,-t]) rotate([90,0,0]) TTWall();
+}
+
+echo("pixelsz:", str(Commitid_pixelsz()),
+     "depth:", Commitid_depth(),
+     "sz:", Commitid_FontDemo_sz());
+
+//TC();
+TT();
diff --git a/commitid-layering-test.scad b/commitid-layering-test.scad
new file mode 100644 (file)
index 0000000..04d8c83
--- /dev/null
@@ -0,0 +1,31 @@
+// -*- C -*-
+
+include <commitid.scad>
+
+baseh= 1;
+basex = 31;
+basey= 20;
+basexpos = -12;
+baseypos = -4;
+
+module Body(){
+  mirror([0,0,1])
+    translate([basexpos, baseypos])
+    cube([basex, basey, baseh]);
+}
+
+difference(){
+  Body();
+  translate([basexpos, baseypos, -baseh])
+    Commitid_BestCount_M([basex,basey], margin=3);
+
+  translate([-6, 6, -0.4])
+    cylinder(r=5, h=3, $fn=50);
+}
+
+translate([-6, 6, -0.5])
+  cylinder(r=4, h=3.5, $fn=50);
+
+echo("pause height (mm)", baseh+0.01);
+
+Commitid_FontDemo();
diff --git a/commitid.scad.pl b/commitid.scad.pl
new file mode 100755 (executable)
index 0000000..62ae510
--- /dev/null
@@ -0,0 +1,917 @@
+#!/usr/bin/perl -w
+
+# commitid.scad.pl - a program for annotating solid models with commit info
+# Copyright (C)2016 Ian Jackson.  See below.  There is NO WARRANTY.
+
+
+# USAGE
+# =====
+#
+#   .../commitid.scad.pl [OPTION...] [STRING...] >commitid.scad.new \
+#     && mv -f commitid.scad.new commitid.scad
+#
+# Run without arguments, commitid.scad.pl will output an openscad file
+# which contains 2D and 3D models of the current git commit count and
+# commit object id (commit hash), useful for identifying printed
+# parts.
+#
+# See below for details.  You probably want these two sections, as a
+# quick starting point:
+#    General form of provided openscad modules
+#    Autoscaling modules
+#
+# We can also generate models of short mainly-numeric strings
+# specified on the command line.
+#
+#
+# Options:
+#
+#   --git    Generate git commit indications, as shown below
+#            (this is the default if no strings are requested with -t).
+#            Ie, produce the `Autoscaling modules' and `Specific layouts'.
+#
+#   --git=objid
+#            Generate git commit indication based on commit object only
+#            (ie avoid counting commits).  Ie, do not generate `Small'
+#            and `Full' layouts (and never select them for `Best').
+#
+#   -i       Do not generate `+' dirty indication if git-untracked files
+#            are present (ie, missing .gitignore entries).  The `*'
+#            dirty tree indication (for modified files) cannot be disabled.
+#
+#   [-t[LAYOUT]] TEXT
+#            Generate a layout LAYOUT containing TEXT.  TEXT can
+#            contain newlines (a final newline usually undesirable, as
+#            it will generate a blank line).  If LAYOUT is not specified,
+#            generates Arg0, Arg1, Arg2, etc., for successive such
+#            TEXTs.  The permissible character set in is TEXT is:
+#                 space 0-9 a-f + *
+#
+#
+# OPENSCAD INTERFACE
+# ==================
+#
+# Dynamic variables for configuration
+# -----------------------------------
+#
+# We honour the following variables to control various scaling factors:
+#
+#                             default value  notes
+#    $Commitid_pixelsz         0.8             \ multiplied together
+#    $Commitid_scale           1.0             /
+#    $Commitid_depth           pixelsz/2       \ multiplied together
+#    $Commitid_depth_scale     1.0             / 
+#    $Commitid_max_best_scale  2.0             limits XY scaling in *Best*
+#
+# FYI the font is nominally 3x5 pixels, with 1-pixel inter-line and
+# inter-character gaps.  (It's not strictly speaking a 3x5 bitmap
+# font, size it contains partial pixels and diagonals.)
+#
+#
+# Non-`module'-specific functions
+# -------------------------------
+#
+# We provide the following functions (which depend on the config
+# variables, but not on anything else) and compute useful values:
+#
+#   function Commitid_pixelsz()   Actual size of each nominal pixel
+#   function Commitid_depth()     Depth to use (the amount characters
+#                                  should be raised or sunken)
+#
+# General form of provided openscad modules
+# -----------------------------------------
+#
+#   module Commitid_MODULE_2D(...)   Collection of polygons forming characters
+#   module Commitid_MODULE(...)      The above, extruded up and down in Z
+#   module Commitid_MODULE_M_2D(...) Mirror writing
+#   module Commitid_MODULE_M(...)    3D mirror writing
+#   function Commitid_MODULE_sz()    A 2-vector giving the X,Y size
+#
+# Except for *Best* modules, the XY origin is in the bottom left
+# corner without any margin.  Likewise Commitid_MODULE_sz does not
+# include any margin.
+#
+# For 3D versions, the model is 2*depth deep and the XY plane bisects
+# the model.  This means it's convenient to either add or subtract from
+# a workpiece whose face is in the XY plane.
+#
+# The _M versions are provided to avoid doing inconvenient translation
+# and rotation to get the flipped version in the right place.
+#
+#
+# Autoscaling modules
+# -------------------
+#
+# These modules take a specification of the available XY space, and
+# select and generate a suitable specific identification layout:
+# 
+#   module Commitid_BestCount_2D  (max_sz, margin=Commitid_pixelsz())
+#   module Commitid_BestCount     (max_sz, margin=Commitid_pixelsz())
+#   module Commitid_BestCount_M_2D(max_sz, margin=Commitid_pixelsz())
+#   module Commitid_BestCount_M   (max_sz, margin=Commitid_pixelsz())
+#   module Commitid_BestObjid_2D  (max_sz, margin=Commitid_pixelsz())
+#   module Commitid_BestObjid     (max_sz, margin=Commitid_pixelsz())
+#   module Commitid_BestObjid_M_2D(max_sz, margin=Commitid_pixelsz())
+#   module Commitid_BestObjid_M   (max_sz, margin=Commitid_pixelsz())
+#
+# max_sz should be [x,y].
+#
+# BestCount includes (as much as it can of) the git commit count,
+# ie the result of
+#     git rev-list --first-parent --count HEAD
+# (and it may include some of the git revision ID too).
+#
+# BestObjid includes as much as it can of the git commit object hash,
+# and never includes any of the count.
+#
+# All of these will autoscale and autorotate the selected model, and
+# will include an internal margin of the specified size (by default,
+# one pixel around each edge).  If no margin is needed, pass margin=0.
+#
+# There are no `function Commitid_Best*_sz'.  If they existed they
+# would simply return max_sz.
+#
+#
+# Output format
+# -------------
+#
+# In general the output, although it may be over multiple lines,
+# is always in this order
+#     git commit object id (hash)
+#     dirty indicator
+#     git commit count
+#
+# Not all layouts have all these parts.  The commit object id may
+# sometimes be split over multiple lines, but the count will not be.
+# If both commit id and commit count appear they will be separated
+# by (at least) a newline, or a dirty indicator, or a space.
+#
+# The commit id is truncated to fit, from the right.
+#
+# The commit count is truncated from the _left_, leaving the least
+# significant decimal digits.
+#
+# The dirty indicator can be
+#
+#   *   meaning the working tree contains differences from HEAD
+#
+#   +   meaning the working tree contains untracked files
+#       (ie files you have failed to `git add' and also failed
+#       to add to gitignore).  (But see the -i option.)
+#
+#
+# Specific layouts
+# ----------------
+#
+# If you want to control the exact layout (and make space for it in
+# your design), you can use these:
+#
+#    module Commitid_LAYOUT_2D()
+#    module Commitid_LAYOUT()
+#    module Commitid_LAYOUT_M_2D()
+#    module Commitid_LAYOUT_M()
+#    function Commitid_LAYOUT_sz()
+#
+# Here LAYOUT is one of the following (giving for example, `module
+# Commitid_Full8_2D').  In the examples, we will assume that the tree
+# is dirty, the commit count is 123456, and the commit object id
+# starts abcdeffedbcaabcdef...  In the examples `_' shows where a
+# space would be printed.
+#
+#   Small2 Small3 ... Small9 Small10 Small12 Small14 Small16
+#       A single line containing as much of the count will fit, eg:
+#            Small5    3456*
+#            Small8    _*123456
+#       The objectid is included if more than one character of of it
+#       will fit without makign the output ambiguous:
+#            Small9    ab*123456
+#
+#   Small2S Small4S ... Small16S
+#   Small3T Small9T Small12T
+#       Same as Small but split into two lines (S)
+#       or three lines (T).  Eg:
+#            Small4S    *4       Small6T   _*
+#                       56                 34
+#                                          56
+#   Git2 Git3 ... Git9 Git10 Git12 Git14 Git16
+#   Git4S Git6S ... Git16S
+#   Git6T Git9T Git12T
+#       Just the commit object hash, in one, two (S) or three (T)
+#       lines.  E.g.:
+#            Git5    abcd*
+#
+#   Full4 Full6 ... Full20:
+#       The commit object hash plus the commit count, on
+#       separate lines, eg:
+#            Full12   abcdef     Full16    abcdeffe
+#                     *23456               _*123456
+#
+#   Full6T Full9T ... Full30T
+#       As Full but the commit object id is split over two lines
+#       producing a 3-line layout, eg:
+#            Full9T    abc       Full21T   abcdeff
+#                      de*                 edbcaa*
+#                      456                 _123456
+#
+# Other LAYOUTs
+# -------------
+#
+#   FontDemo
+#
+#       A demonstration of the built-in 18-character font
+#
+#   Arg0 Arg1, ...
+#
+#       Strings passed on command line (without -t, or bare -t,
+#       rather than with -tLAYOUT).
+#
+#   LAYOUT
+#
+#       Generated by passing -tLAYOUT on the command line.
+#
+
+
+# COPYRIGHT, LICENCE AND LACK-OF-WARRANTY INFORMATION
+# ===================================================
+#
+# This program is Free Software and a Free Cultural Work.
+#
+#   You can redistribute it and/or modify it under the terms of the
+#   GNU General Public License as published by the Free Software
+#   Foundation, either version 3 of the License, or (at your option)
+#   any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Alternatively, at your option:
+#
+#   This work is licensed under the Creative Commons
+#   Attribution-ShareAlike 4.0 International License.
+#
+#   There is NO WARRANTY.
+
+
+use strict;
+
+$SIG{__WARN__} = sub { die @_; };
+
+our $debug=0;
+
+if (@ARGV && $ARGV[0] =~ m/^-(D+)$/) {
+    $debug = length $1;
+    shift @ARGV;
+}
+
+sub p { print @_ or die $!; }
+
+sub p_debug { print STDERR @_ if $debug; }
+
+p <<'END';
+// *** AUTOGENERATED - DO NOT EDIT *** //
+function Commitid_pixelsz() =
+  ($Commitid_pixelsz       ? $Commitid_pixelsz       : 0.8) *
+  ($Commitid_scale         ? $Commitid_scale         : 1.0);
+function Commitid_depth() =
+  ($Commitid_depth         ? $Commitid_depth         : Commitid_pixelsz()/2) *
+  ($Commitid_depth_scale   ? $Commitid_depth_scale   : 1.0);
+function Commitid__scale() =
+  Commitid_pixelsz() / 0.2;
+END
+
+sub chrmodname ($) {
+    my ($chr) = @_;
+    my $chrx = sprintf '%#x', ord $chr;
+    return "Commitid__chr_$chrx";
+}
+
+our $gtm_demo_i = -1;
+our $gtm_demo_j;
+our @gtm_demo_o;
+
+sub gentextmodule_demo_start_batch () {
+    $gtm_demo_j = 0;
+    $gtm_demo_i++;
+}
+
+sub argl_formal (@) { join ', ', @_; }
+sub argl_actual (@) { join ',', map { m/=/ ? $` : $_ } @_; }
+
+sub gen3dmodule ($@) {
+    my ($modb,$size,@argl) = (@_);
+    $size ||= "${modb}_sz()";
+    p "module ${modb}_M_2D(".argl_formal(@argl)."){\n";
+    p "  translate([${size}[0],0])\n";
+    p "    mirror([1,0,0])\n";
+    p "    ${modb}_2D(".argl_actual(@argl).");\n";
+    p "};\n";
+    foreach my $mir ('','_M') {
+       my $mm = "${modb}${mir}";
+       p "module ${mm}(".argl_formal(@argl)."){\n";
+       p " d=Commitid_depth();\n";
+       p " translate([0,0,-d]) linear_extrude(height=d*2)\n";
+       p "  ${mm}_2D(".argl_actual(@argl).");\n";
+       p "}\n";
+    }
+}
+
+sub gentextmodule ($@) {
+    my ($form, @lines) = @_;
+    my $modb = "Commitid_$form";
+    p "module ${modb}_2D(){\n";
+    p " // |$_|\n" foreach @lines;
+    p " scale(Commitid__scale()){\n";
+    my $y = @lines;
+    my $cols = 1;
+    foreach my $line (@lines) {
+       $y--;
+       my $x = 0;
+       foreach my $chr (split //, $line) {
+           p sprintf "  translate([%d * 0.8, %d * 1.2]) %s();\n",
+               $x, $y, chrmodname $chr
+               if $chr =~ m/\S/;
+           $x++;
+       }
+       $cols = $x if $x > $cols;
+    }
+    p " }\n";
+    p "}\n";
+    gen3dmodule($modb,'');
+
+    p sprintf "function %s_sz() = Commitid__scale() * 0.1 * [ %d, %d ];\n",
+       $modb, 2 * ($cols * 4 - 1), 2 * (@lines * 6 - 1);
+
+    push @gtm_demo_o, <<END;
+ translate([$gtm_demo_i * st[0], $gtm_demo_j * st[1]]) {
+  difference(){
+   color("blue") translate([-e,-e]) square(${modb}_sz() + 2*[e,e]);
+   square(${modb}_sz());
+  }
+  ${modb}_2D();
+}
+END
+    $gtm_demo_j++;
+}
+
+our @demo;
+
+
+our $prcount;
+
+sub debug_simplify_begin ($) {
+    my ($chr) = @_;
+
+    return unless $debug;
+
+    open S, ">commitid-DEBUG-simplify-$chr.ps";
+    print S "%!\n";
+    print S "(Courier-Bold) findfont 15 scalefont setfont\n";
+
+    $prcount=0;
+}
+
+sub debug_simplify_done () {
+    return unless $debug;
+    print S "showpage\n";
+    close S or die $!;
+}
+
+sub debug_simplify_pr ($$$) {
+    my ($chr,$polys,$why) = @_;
+
+    return unless $debug;
+
+    print STDERR "PR $chr $why\n";
+    my $ct_x = 10000 * ($prcount % 6);
+    my $ct_y = 18000 * int($prcount / 6);
+    printf S "0 setgray\n";
+    printf S "%d %d moveto\n", map {$_/100 + 10} $ct_x,$ct_y;
+    printf S "(%s) show\n", $why;
+    my $pr_recur;
+
+    $pr_recur = sub {
+       my ($tpolys, @levels) = @_;
+       return unless @$tpolys;
+       foreach my $i (0..$#$tpolys) {
+           printf STDERR "P@levels %02d :", $i;
+           my $pinfo =  $tpolys->[$i];
+           my $p = $pinfo->{E};
+           printf STDERR "@$p\n";
+           my $lw = 5 - 4*($i / ($#$tpolys || 1));
+           my $pp = sub {
+               my $spec = $p->[$_[0]];
+               $spec =~ m/^\d{5}/;
+               sprintf "%d %d",map { $_/100 }
+                   1000 + $ct_x + $&,
+                   5000 + $ct_y + $';
+           };
+           printf S "%s setrgbcolor\n", (@levels==0 ? '0 0 0' :
+                                         @levels==1 ? '0 0 1'
+                                         : '1 1 0');
+           foreach my $eai (0..$#$p) {
+               my $ebi = ($eai + 1) % @$p;
+               printf S <<END, $lw, $pp->($eai), $pp->($ebi);
+ %f setlinewidth
+ %s moveto
+ %s lineto
+ stroke
+END
+           }
+           $pr_recur->($pinfo->{Holes}, @levels, $i);
+       }
+    };
+    $pr_recur->($polys,0);
+
+    $prcount++;
+}
+
+sub simplify ($$) {
+    my ($chr,$polys) = @_;
+    use Data::Dumper;
+
+    return unless @$polys;
+
+    my $count=0;
+    my $pr = sub { };
+
+    if ($debug) {
+       debug_simplify_begin($chr);
+    }
+
+    $pr->("start");
+
+  AGAIN: while(1) {
+       my %edges;
+       my $found_hole;
+
+       foreach my $pi (0..$#$polys) {
+           my $p = $polys->[$pi]{E};
+           foreach my $ei (0..$#$p) {
+               my $e = $p->[$ei].$p->[($ei+1) % @$p];
+               die if $edges{$e};
+               $edges{$e} = [ $p, $pi, $ei ];
+           }
+       }
+       p_debug "AGAIN $count\n";
+       my $merge = sub {
+           my ($pa, $pai, $eai, $pb, $pbi, $ebi) = @_;
+           p_debug "# merging $pai:$eai.. $pbi:$ebi..\n";
+           splice @$pa, $eai, 1,
+               ((@$pb)[$ebi+1..$#$pb], (@$pb)[0..$ebi-1]);
+           @$pb = ( );
+       };
+       foreach my $pai (0..$#$polys) {
+           my $painfo = $polys->[$pai];
+           my $pa = $painfo->{E};
+           foreach my $eai (0..$#$pa) {
+               my $ear = $pa->[ ($eai+1) % @$pa ].$pa->[$eai];
+               my $ebi = $edges{$ear};
+               next unless $ebi;
+               my ($pb,$pbi);
+               ($pb, $pbi, $ebi) = @$ebi;
+               # $pai:($eai+1)..$eai and $pbi:$ebi..($ebi+1) are identical
+               # so we want to remove them.
+               if ($pai==$pbi) {
+                   # we're making a hole!  we make an assumption:
+                   # holes have fewer line segments than the
+                   # outlines.  This is almost always true because of
+                   # the way we construct our figures.
+                   if (($ebi - $eai + @$pa) % @$pa > @$pa/2) {
+                       # We arrange that $eai..$ebi is the hole
+                       ($ebi,$eai) = ($eai,$ebi);
+                   }
+                   p_debug "HOLE $eai $ebi\n";
+                   # we want to make the smallest hole, to avoid
+                   # making a hole that itself needs simplifying
+                   my $holesz = ($ebi - $eai + @$pa) % @$pa;
+                   $found_hole = [ $pa,$pai,$eai, $ebi, $holesz ]
+                       unless $found_hole && $found_hole->[4] < $holesz;
+               } else {
+                   $merge->($pa,$pai,$eai,$pb,$pbi,$ebi);
+                   debug_simplify_pr($chr,$polys,"after $count");
+                   next AGAIN;
+               }
+           }
+           # we process hole joining last, so that the whole of the
+           # edge of the hole must be part of the same polygon
+           if ($found_hole) {
+               p_debug "HOLE DOING @$found_hole\n";
+               my ($pa,$pai,$eai,$ebi) = @$found_hole;
+               # simplify the indexing
+               @$pa = ((@$pa)[$eai..$#$pa], (@$pa)[0..$eai-1]);
+               $ebi -= $eai; $ebi += @$pa; $ebi %= @$pa;
+               $eai = 0;
+               push @{ $painfo->{Holes} }, {
+                    E => [ (@$pa)[$eai+1..$ebi-1] ],
+                    Holes => [ ],
+                };
+               splice @$pa, $eai, $ebi-$eai+1;
+               debug_simplify_pr($chr,$polys,"hole $count");
+               next AGAIN;
+           }
+       }
+        last;
+    }
+
+    debug_simplify_done();
+}
+
+sub p_edgelist ($$$) {
+    my ($points,$vecs,$p) = @_;
+    my @vec;
+    foreach my $pt (@$p) {
+       $pt =~ s{\d{5}}{$&,};
+       $pt =~ s{\b\d}{$&.}g;
+       push @$points, "[$pt]";
+       push @vec, $#$points;
+    }
+    push @$vecs, \@vec;
+}
+
+sub parsefont () {
+    my %cellmap;
+    for (;;) {
+       $_ = <DATA> // die;
+       last if %cellmap && !m/\S/;
+       next unless m/\S/;
+       chomp;
+       s{^(.) }{};
+       $cellmap{$1} = $_;
+    }
+    my %chrpolys;
+    # $chrs{$chr}[$poly] = $poly
+    # $poly->{E} = [ "012345012345", ... ]
+    # $poly->{Holes} = $poly2
+    while (<DATA>) {
+       next unless m/\S/;
+       chomp;
+       my @chrs = split / /, $_;
+       <DATA> !~ m/\S/ or die;
+       foreach my $row (reverse 0..4) {
+           $_ = <DATA>;
+           chomp;
+           s{^}{ };
+           $_ .= ' ' x 8;
+           m{\S/\S} and die;
+           s{/(?=\s)}{L}g;
+           s{/(?=\S)}{r}g;
+           s{\\(?=\s)}{l}g;
+           s{\\(?=\S)}{R}g;
+           p "// $_\n";
+           foreach my $chr (@chrs) {
+               s{^ }{} or die "$chr $_ ?";
+               foreach my $col (0..2) {
+                   my @verts;
+                   if (s{^ }{}) {
+                   } elsif (s{^\S}{}) {
+                       my $f = $cellmap{$&};
+                       die unless $f;
+                       $f =~ s/\b\d/ sprintf '%05d', $col*2000 + $&*1000 /ge;
+                       $f =~ s/\d\b/ sprintf '%05d', $row*2000 + $&*1000 /ge;
+                       push @{ $chrpolys{$chr} }, { E => [ split / /, $f ] };
+                   } else {
+                       die "$_ ?";
+                   }
+               }
+           }
+           die "$_ ?" if m{\S};
+       }    
+    }
+
+    my $demo = '';
+    my $democols = 6;
+    foreach my $chr (sort keys %chrpolys) {
+
+       my $polys = $chrpolys{$chr};
+       $_->{Holes} = [] foreach @$polys;
+
+       simplify($chr,$polys);
+
+       my $mod = chrmodname $chr;
+       p "module $mod () {\n";
+       foreach my $poly (@$polys) {
+           p " polygon(";
+           my $holes = $poly->{Holes};
+           my (@points, @vecs);
+           p_edgelist(\@points, \@vecs, $poly->{E});
+           foreach my $hole (@$holes) {
+               p_edgelist(\@points, \@vecs, $hole->{E});
+           }
+           p "points=[".(join ",",@points)."],";
+           if (@$holes) {
+               p ",paths=[".(join ",",
+                            map { "[".(join ",",@$_)."]" }
+                            @vecs)."],";
+           }
+           p "convexity=4);\n";
+       }
+       p "}\n";
+       $demo .= $chr;
+    }
+    @demo = reverse $demo =~ m{.{1,$democols}}go;
+}
+
+parsefont();
+
+our $do_git; # contains may chars 'c' (count) and/or 'o' (object)
+our $do_git_untracked = 1;
+our $argcounter;
+
+our @forms;
+our %included; # 0 = not at all; 1 = truncated; 2 = full
+
+sub rjustt ($$$;$) {
+    # right justify and truncate (ie, pad and truncate at left)
+    # always includes prefix
+    # sets $included{$what}
+    my ($sz, $what, $whole, $prefix) = @_;
+    $prefix //= '';
+    my $lw = length $whole;
+    my $spare = $sz - $lw - (length $prefix);
+    $included{$what}= 1 + ($spare > 0);
+    return
+       ($spare > 0 ? (' ' x $spare) : '').
+       $prefix.
+       substr($whole, ($spare < 0 ? -$spare : 0));
+}
+
+sub ljustt ($$$;$) {
+    my ($sz, $what, $whole, $suffix) = @_;
+    $suffix //= '';
+    $sz -= length $suffix;
+    $included{$what} = 1 + ($sz >= length $whole);
+    return sprintf "%-${sz}.${sz}s%s", $whole, $suffix;
+}
+
+sub genform_prep() {
+    $included{$_}=0 foreach qw(Objid Count);
+}
+
+sub genform ($@) {
+    my ($form, @lines) = @_;
+    gentextmodule($form, @lines);
+    my $f = {
+       Form => $form,
+       Chars => (length join '', @lines),
+       Lines => (scalar @lines),
+       Ambiguous => ($form =~ m/Full/ && !grep { m/\W/ } @lines),
+       Included => { %included },
+    };
+    push @forms, $f;
+}
+
+sub genform_q ($$$) {
+    my ($form, $s, $lines) = @_;
+    $gtm_demo_j++;
+    my $l = length $s;
+    return if $l % $lines;
+    my $e = $l/$lines;
+    return if $e < 2;
+    $gtm_demo_j--;
+    genform($form, $s =~ m/.{$e}/g);
+}
+
+sub genform_plusq ($$) {
+    my ($form, $s) = @_;
+    genform($form, $s);
+    genform_q("${form}S", $s, 2);
+    genform_q("${form}T", $s, 3);
+}
+
+our @gcmd;
+
+sub gitrun_start () {
+    open F, "-|", @gcmd or die "$gcmd[0]: start: $!";
+}
+
+sub gitrun_done (;$) {
+    my ($errok) = @_;
+    $?=0; $!=0;
+    return if close F;
+    return if $errok;
+    die $! if $!;
+    die "@gcmd failed ($?)\n";
+}
+
+sub gitoutput (@) {
+    (@gcmd) = (qw(git), @_);
+    gitrun_start;
+    $_ = <F>;
+    gitrun_done;
+    defined or die "@gcmd produced no output";
+    chomp or die "@gcmd produced no final newline";
+    $_;
+}
+
+sub do_git () {
+    return unless $do_git;
+
+    @gcmd = qw(git status --porcelain);
+    push @gcmd, qw(--untracked=no) unless $do_git_untracked;
+
+    my $git_dirty = '';
+    gitrun_start;
+    while (<F>) {
+       if (m/^\?\?/ && $do_git_untracked) {
+           $git_dirty = '+';
+           next;
+       }
+       $git_dirty = '*';
+       last;
+    }
+    gitrun_done($git_dirty eq '*');
+
+    my $git_count;
+    my $git_object;
+
+    if ($do_git =~ m/c/) {
+       $git_count = gitoutput qw(rev-list --first-parent --count HEAD);
+    }
+    if ($do_git =~ m/o/) {
+       $git_object = gitoutput qw(rev-parse HEAD);
+    }
+    print STDERR join ' ', map { $_ // '?' }
+       "-- commitid", $git_object, $git_dirty, $git_count, "--\n";
+
+    foreach my $sz (2..10, qw(12 14 16)) {
+       gentextmodule_demo_start_batch();
+
+       if (defined($git_count)) {
+           genform_prep();
+           my $smallstr = rjustt($sz, 'Count', $git_count, $git_dirty);
+           my $forgitobj = $sz - length($git_count) - 1;
+           if (defined($git_object) && $forgitobj >= 2) {
+               $smallstr = ljustt($forgitobj, 'Objid', $git_object).
+                   ($git_dirty || ' ').
+                   $git_count;
+           }
+           genform_plusq("Small$sz", $smallstr);
+       }
+
+       genform_prep();
+       genform_plusq("Git$sz", ljustt($sz, 'Objid', $git_object, $git_dirty))
+           if defined $git_object;
+
+       if (defined $git_count && defined $git_object && $sz<=10) {
+           genform_prep();
+           genform("Full".($sz*2),
+                   ljustt($sz, 'Objid', $git_object),
+                   rjustt($sz, 'Count', $git_count, $git_dirty));
+
+           genform_prep();
+           my $e = $sz;
+           genform("Full".($e*3)."T",
+                   ljustt($e*2, 'Objid', $git_object, $git_dirty)
+                   =~ m/.{$e}/g,
+                   rjustt($e, 'Count', $git_count));
+       }
+    }
+}
+
+sub do_some_best ($$) {
+    my ($bestwhat, $formre) = @_;
+    my $modname = "Best$bestwhat";
+    my $fullmodname = "Commitid_${modname}_2D";
+    my @argl = qw(max_sz margin=Commitid_pixelsz());
+    p "module $fullmodname(".argl_formal(@argl).") {\n";
+    my $mbs = '$Commitid_max_best_scale';
+    p " sc_max = $mbs ? $mbs : 2;\n";
+    p " sz = max_sz - 2*[margin,margin];\n";
+    my @do;
+    foreach my $f (
+        sort {
+           $b->{Included}{$bestwhat} <=> $a->{Included}{$bestwhat} or
+           $b->{Chars} <=> $a->{Chars} or
+           $a->{Lines} <=> $b->{Chars}
+        }
+        grep {
+           $_->{Form} =~ m/$formre/ &&
+           !$_->{Ambiguous}
+       }
+        @forms
+    ) {
+       my $form = $f->{Form};
+       p " sz_$form = Commitid_${form}_sz();\n";
+       foreach my $rot (qw(0 1)) {
+           my $id = "${form}_r${rot}";
+           p " sc_$id = min(sc_max";
+           foreach my $xy (qw(0 1)) {
+               p ",sz[$xy]/sz_$form","[",(($xy xor $rot)+0),"]";
+           }
+           p ");\n";
+           push @do, " if (sc_$id >= 1.0";
+           push @do, " && sc_$id >= sc_${form}_r1" if !$rot;
+           push @do, ") {\n";
+           push @do, "  translate([margin,margin]) scale(sc_$id)\n";
+           push @do, "   rotate(90) translate([0,-sz_$form"."[1]])\n" if $rot;
+           push @do, "   Commitid_${form}_2D();\n";
+           push @do, " } else";
+       }
+    }
+    push @do, <<END;
+ {
+  echo("$fullmodname could not fit anything in", max_sz);
+ }
+END
+    p $_ foreach @do;
+    p "}\n";
+
+    gen3dmodule "Commitid_$modname", 'max_sz', @argl;
+}
+
+sub do_git_best () {
+    return unless $do_git;
+
+    # Auto-computer for `best fit'
+    #
+    # We have two best fit approaches: with count, and git-object-id-only
+    #
+    # For `with count', we only ever include the git object id if the
+    # result would be unambigous.  That means that at least one space
+    # or punctuation was generated.
+    #
+    # We sort the options by firstly number of characters
+    # (decreasing), and then by number of lines (increasing) and
+    # try each one both ways round.
+
+    do_some_best('Count', 'Small|Full') if $do_git =~ m/c/;
+    do_some_best('Objid', 'Git|Full') if $do_git =~ m/o/;
+}
+
+while (@ARGV) {
+    $_ = shift;
+    if (m/^--(no)?-git$/) {
+       $do_git = $1 ? '' : 'co';
+    } elsif (m/^---git=objid$/i) {
+       $do_git = 'o';
+    } elsif (m/^-i$/) {
+       $do_git_untracked = 0;
+    } elsif (m/^-t(.*)$/) {
+       my $form = $1;
+       die "bad usage: -t needs string argument\n";
+       $_ = shift;
+       gentextmodule($form, split /\n/, $_);
+       $argcounter //= 0;
+    } elsif (m/^[^-]/) {
+       gentextmodule("Arg$argcounter", $_);
+       $argcounter++;
+    } else {
+       die "bad usage: unknown option \`$_'\n";
+    }
+}
+
+$do_git //= defined($argcounter) ? '' : 'co';
+
+gentextmodule_demo_start_batch();
+gentextmodule('FontDemo', @demo);
+
+do_git();
+do_git_best();
+
+p "module Commitid_2DDemo(){\n";
+p " st = Commitid__scale() * [ 10, 5 ];\n";
+p " e  = Commitid_pixelsz();\n";
+p $_ foreach @gtm_demo_o;
+p "}\n";
+
+flush STDOUT or die $!;
+close STDOUT or die $!;
+
+__DATA__
+
+# 00 20 22 02
+l 00 20 02
+r 00 20 22
+L 00 22 02
+R 20 22 02
+> 00 20 22 02 11
+< 00 20 11 22 02
+
+0 1 2 3 4 5 6 7 8 9
+
+/#\  r  /#\ ##\ # # ### /#/ ### /#\ /#\
+# # /#    #   # # # #   #     # # # # #
+# #  #  /#/ ##< \## ##\ ##\  // >#< \##
+# #  #  #     #   #   # # #  #  # #   #
+\#/ /#\ ### ##/   # ##/ \#/  #  \#/ ##/
+
+a b c d e f
+
+    #         #     /##
+    #   /##   # /#\ #
+/## ##\ #   /## #r# ###
+# # # # #   # # #/  #
+\## ##/ \## \## \#/ #
+
++ *
+
+    # #
+ #  \#/
+### ###
+ #  /#\
+    # #
diff --git a/crossbar-computer-led-mount.scad b/crossbar-computer-led-mount.scad
new file mode 100644 (file)
index 0000000..535c680
--- /dev/null
@@ -0,0 +1,340 @@
+// -*- C -*-
+
+led_dia = 5 + 0.6;
+led_depth = 5;
+
+led_tip_height_above_crossbar = 70;
+led_angle = -60;
+crossbar_dia = 25; // fixme
+
+vert_space_inside = 8;
+backfront_space_inside = 12;
+width_space_inside = 10;
+
+backfront_mate_size = 25;
+tower_frontheight = 10;
+tower_base_height = 20;
+tower_slot_width = 3;
+
+cableclamp_ctie_width = 4.0 + 1.0;
+cableclamp_ctie_thick = 2.5 + 0.5;
+
+lidclamp_ctie_width = 4.0 + 1.0;
+lidclamp_ctie_thick = 2.5 + 0.5;
+
+base_ctie_width = 4.0 + 1.0;
+base_ctie_thick = 2.5 + 0.5;
+
+tube_ctie_width = 4.0 + 1.0;
+tube_ctie_thick = 2.5 + 0.5;
+
+// tuning
+
+tower_over_angle = 45;
+tower_wall_thick = 1.6;
+tower_forehead_angle = 30;
+lid_wall_thick = 1.6;
+lid_slop = 0.75;
+//cableclamp_ctie_anchor = 5;
+lidclamp_cableclamp_ctie_between = 0;
+base_ctie_anchor = 5;
+tube_ctie_anchor = 5;
+protrusion_size = 2;
+protrusion_none_frontback = 10;
+protrusion_slop = 0.25;
+cableclamp_ctie_z = tower_frontheight/2;
+
+towerleg_backfront = 5;
+towerleg_width = 3;
+towerleg_foot_gap = 2;
+towerleg_foot_backfront = 20;
+towerleg_foot_width = 40;
+towerleg_foot_height = 10;
+towerleg_yslope = 0.7;
+towerleg_xslope = 0.3;
+echo(sqrt(towerleg_yslope*towerleg_yslope+towerleg_xslope*towerleg_xslope));
+
+//--- tests ---
+
+test_width = 24;
+test_height = 24;
+
+test_thicks = [9,14,21];
+
+module Tests(){ ////toplevel
+  for (thicki=[0:len(test_thicks)-1]) {
+    translate([thicki*test_width-0.5, 0, 0]) {
+      difference(){
+       cube([test_width,
+             test_thicks[thicki] + led_depth,
+             test_height]);
+       translate([test_width/2, -1, test_height/2])
+         rotate([-90,0,0])
+         cylinder(r=led_dia/2, h=led_depth+1, $fn=30);
+      }
+    }
+  }
+}
+
+//Tests();
+
+//--- real thing ---
+
+// calculated
+
+tower_overhang = led_dia * 2.5;
+tower_width = width_space_inside + tower_wall_thick*2;
+
+tower_over_max_y = tower_overhang * sin(tower_over_angle);
+tower_over_max_z = tower_frontheight + tower_overhang * cos(tower_over_angle);
+tower_total_max_z = tower_over_max_z + vert_space_inside + led_depth;
+tower_rearwall_y = -(backfront_space_inside + tower_wall_thick);
+led_head_y = tower_over_max_y/2;
+led_head_z = tower_frontheight + tower_overhang*sin(tower_over_angle)/2;
+backfront_mate_extra = (backfront_mate_size - (-tower_rearwall_y));
+
+tower_height_contribution = led_head_z + tower_base_height;
+
+base_ctie_anchor_eff = base_ctie_anchor+base_ctie_thick/2;
+tube_ctie_anchor_eff = tube_ctie_anchor+tube_ctie_thick/2;
+
+base_width = 0.7 * crossbar_dia;
+base_backfront = backfront_mate_extra - tower_rearwall_y;
+base_height = led_tip_height_above_crossbar - tower_height_contribution;
+
+protrusion_frontback = base_backfront - protrusion_none_frontback;
+
+echo(tower_height_contribution, base_height);
+
+module TowerWallCrossSection(){
+  // generates a 2D shape - a polygon
+  // x is what is going to be -y
+  // y is what is going to be z
+  polygon([[0,                  0],
+          [0,                  tower_frontheight],
+          [-tower_over_max_y,  tower_over_max_z],
+          [-tower_over_max_y
+           + tan(tower_forehead_angle) * (vert_space_inside + led_depth),
+           tower_total_max_z],
+          [-tower_rearwall_y,  tower_total_max_z],
+          [-tower_rearwall_y,  0],
+          [-tower_rearwall_y, -tower_base_height],
+          [-backfront_mate_extra, -tower_base_height]],
+         convexity=5);
+}
+
+module TowerWallSomeEdge(front){
+  minkowski(){
+    difference(){
+      TowerWallCrossSection();
+      translate([front ? 0.10 : -0.10, 0])
+       TowerWallCrossSection();
+    }
+    circle(r=tower_wall_thick, $fn=8);
+  }
+}
+
+module TowerBulkCrossSection(){
+  intersection(){
+    TowerWallCrossSection();
+    union(){
+      translate([-led_head_y, led_head_z])
+       circle(r = led_depth);
+      TowerWallSomeEdge(true);
+      translate([-50, -50])
+       square([100, 50]);
+    }
+  }
+}
+
+module TowerRearWallCrossSection(){
+  intersection(){
+    TowerWallCrossSection();
+    union(){
+      intersection(){
+       translate([0,-10]) square([100, 10+led_head_z]);
+       TowerWallSomeEdge(false);
+      }
+      TowerBulkCrossSection();
+    }
+  }
+}
+
+
+module TowerCrossSectionDemo(){
+  %TowerWallCrossSection();
+  //TowerBulkCrossSection();
+  TowerRearWallCrossSection();
+}
+
+module TowerMain(){
+  for (mir=[0,1])
+    mirror([mir,0,0]) rotate([90,0,-90]) {
+      translate([0,0, tower_width/2-tower_wall_thick])
+       linear_extrude(height=tower_wall_thick) {
+       TowerWallCrossSection();
+      }
+      translate([0,0,-1])
+       linear_extrude(height=tower_width/2+0.9)
+       union(){
+         TowerBulkCrossSection();
+         hull(){
+           intersection(){
+             TowerWallCrossSection();
+             translate([-30, -30])
+               square([30 + 0.1, 30 + tower_frontheight]);
+           }
+         }
+        }
+      translate([0,0, tower_slot_width/2])
+       linear_extrude(height=(tower_width - tower_slot_width)/2 - 0.2)
+       TowerRearWallCrossSection();
+    }
+}
+
+module LedHole(){
+  translate([0, led_head_y, led_head_z])
+    rotate([90 + led_angle, 0, 0])
+    translate([0,0,-10])
+    cylinder(r=led_dia/2, h=led_depth+1+10, $fn=26, $fa=10);
+}
+
+module TowerProper(){
+  difference(){
+    TowerMain();
+    LedHole();
+    // passages for cable ties
+    translate([0,
+              tower_rearwall_y/2,
+              cableclamp_ctie_z
+              + cableclamp_ctie_width/2 + lidclamp_ctie_thick/2
+              + lidclamp_cableclamp_ctie_between])
+      cube([50, lidclamp_ctie_width, lidclamp_ctie_thick], center=true);
+    translate([0,
+               (backfront_mate_extra+tower_rearwall_y)/2,
+               -tower_base_height
+              + max(protrusion_size + protrusion_slop + 0.1,
+                    base_ctie_anchor_eff)])
+      cube([50, base_ctie_width, base_ctie_thick], center=true);
+//    for (extra_y=[0, -(cableclamp_ctie_thick + cableclamp_ctie_anchor)]) {
+//      translate([-tower_width/2,
+//              -cableclamp_ctie_thick/2 - tower_wall_thick + extra_y,
+//              cableclamp_ctie_z])
+//     cube([tower_wall_thick+2,
+//           cableclamp_ctie_thick,
+//           cableclamp_ctie_width], center=true);
+//    }
+    for (mir=[0,1])
+     mirror([mir,0,0]) {
+       translate([tower_width/4, 20, cableclamp_ctie_z])
+         cube([cableclamp_ctie_thick,
+               tower_wall_thick*2+1+40,
+               cableclamp_ctie_width],
+              center=true);
+      }
+    translate([0, tower_rearwall_y, -tower_base_height])
+      BaseRegistrationProtrusion(protrusion_slop);
+  }
+}
+
+module Tower(){ ////toplevel
+  TowerProper();
+  for (mir=[0,1]) {
+    mirror([mir,0,0]){
+      translate([0,
+                tower_rearwall_y + 0.1,
+                -1])
+       mirror([0,0,1])
+       multmatrix([[1,0, towerleg_xslope,0],
+                   [0,1,-towerleg_yslope,0],
+                   [0,0,1,0],
+                   [0,0,0,1]])
+       cube([towerleg_width, towerleg_backfront, tower_base_height-2]);
+    }
+  }
+  translate([-towerleg_foot_width/2,
+            tower_rearwall_y - towerleg_foot_gap,
+            -tower_base_height])
+    mirror([0,1,0])
+    cube([towerleg_foot_width, towerleg_foot_backfront, towerleg_foot_height]);
+}
+
+module TowerMainHull(){
+  hull(){ TowerMain(); }
+}
+
+module Lid(){
+  intersection(){
+    difference(){
+      minkowski(){
+       TowerMainHull();
+       sphere(r=lid_wall_thick+lid_slop, $fn=8);
+      }
+      minkowski(){
+       TowerMainHull();
+       sphere(r=lid_slop, $fn=6);
+      }
+    }
+    translate([-50,-50,led_head_z]) cube([100,100,100]);
+  }
+}
+
+module LidT(){ ////toplevel
+  rotate([180,0,0]) Lid();
+}
+
+module BaseRegistrationProtrusion(extra){
+  size = protrusion_size + extra;
+  translate([0, base_backfront/2, 0]){
+    hull(){
+      translate([0,0, -0.5])
+       cube([protrusion_size*2, protrusion_frontback, 1.0], center=true);
+      translate([0, 0, protrusion_size-0.5])
+       cube([0.05, protrusion_frontback-protrusion_size*2, 1.0], center=true);
+    }
+  }
+}
+
+module Base(){
+  difference(){
+    mirror([0,0,1]){
+      hull(){
+       translate([-tower_width/2, 0, 0])
+         cube([tower_width, base_backfront, 0.1]);
+       translate([-base_width/2, 0, base_height])
+         cube([base_width, base_backfront, crossbar_dia/2]);
+      }
+    }
+    translate([0, base_backfront/2, -base_ctie_anchor_eff])
+      cube([100, base_ctie_width, base_ctie_thick], center=true);
+    translate([0, base_backfront/2, -base_height + tube_ctie_anchor_eff])
+      cube([100, tube_ctie_width, tube_ctie_thick], center=true);
+    translate([0, -1, -(base_height + crossbar_dia/2)])
+      rotate([-90,0,0])
+      cylinder(r=crossbar_dia/2, h=101);
+  }
+  BaseRegistrationProtrusion(0.0);
+}
+
+module BaseT(){ ////toplevel
+  rotate([90,0,0]) Base();
+}
+
+module Demo(){
+  Tower();
+  %Lid();
+  translate([0,0, 25]) Lid();
+  translate([0, tower_rearwall_y, -(tower_base_height+5)]) Base();
+}
+
+//TowerCrossSectionDemo();
+//TowerWallSomeEdge(false);
+//TowerWallFrontEdge();
+//TowerMainHull();
+//LidT();
+//Tower();
+//Lid();
+//BaseRegistrationProtrusion();
+//Base();
+//BaseT();
+//Demo();
diff --git a/dell-psu-glow-lampshade.scad b/dell-psu-glow-lampshade.scad
new file mode 100644 (file)
index 0000000..8733948
--- /dev/null
@@ -0,0 +1,16 @@
+// -*- C -*-
+
+india = 11.01 + 0.50;
+t = 0.9;
+l = 7.5;
+
+$fa = 3;
+$fs = 0.2;
+
+linear_extrude(height=l, convexity=10) {
+  difference(){
+    circle(r = india/2 + t);
+    circle(r = india/2);
+  }
+}
+
diff --git a/deore-crank-remover.scad b/deore-crank-remover.scad
new file mode 100644 (file)
index 0000000..1b0a738
--- /dev/null
@@ -0,0 +1,34 @@
+// -*- C -*-
+
+outdia=15.1;
+india=13.0;
+depth=10;
+
+eoutrad = outdia/2 + 1.0;
+einrad = india/2 - 1.0;
+edepth = depth + 3;
+
+handledepth = 5;
+handlewidth = 20;
+handlelength = 70;
+
+module FlatSplines(){
+  for (rot=[0:7]) {
+    rotate([0,0, rot*360/8])
+      for (m=[0,1]) {
+       mirror([m,0,0])
+         polygon([[-0.1, 0],
+                  [-0.01, eoutrad],
+                  [einrad * sin(22.5), einrad * cos(22.5)],
+                  [einrad * sin(22.5), einrad * cos(22.5) - 3],
+                  [1, 0]]);
+      }
+  }
+}
+
+translate([0,0,-1])
+  linear_extrude(height=edepth+1)
+  FlatSplines();
+
+translate([0,0,-handledepth/2])
+  cube([handlelength,handlewidth,handledepth], center=true);
diff --git a/distort-stl b/distort-stl
new file mode 100755 (executable)
index 0000000..68290c4
--- /dev/null
@@ -0,0 +1,296 @@
+#!/usr/bin/perl -w
+#
+# usage:
+#   ./distort-stl <INPUT >OUTPUT DISTORTION [PARAMS...] ...
+#
+# DISTORTIONs:
+#
+#   project-cylinder RADIUS
+#       projects the X-Z plane onto the cylinder of
+#           radius RADIUS with axis [0, 0, t]
+#       origin becomes [0, -RADIUS, 0]
+#       other planes of the input are projected onto smaller
+#           or larger cylinders accordingly
+#       probably a bad idea if
+#           object has any Y > RADIUS
+#           object has any |X| > tau / RADIUS
+#       technically, treats input as if it were
+#       polar-rectangular coords:
+#          Z' = Z; R' = Y + RADIUS; theta' = X / RADIUS
+#       and then converts back into cartesian
+#       honours fa but not fs or fn
+#
+#   set-fa $FA
+
+use strict;
+use autodie;
+
+use List::Util;
+use POSIX;
+use File::Temp ();
+use Data::Dumper;
+
+sub TAU () { M_PI * 2; }
+
+our $debug = $ENV{DISTORT_DEBUG} // 0 ;
+
+my $ps = $ENV{DISTORT_PS};
+if ($ps) {
+    open PS, "> $ps" or die $!;
+    print PS "%!\n";
+}
+
+our $fa = 10;
+
+our $triangles;
+our $output;
+
+sub shift_arg () {
+    die unless @ARGV;
+    scalar shift @ARGV;
+}
+
+#no warnings qw(recursion);
+
+sub sprintf_triangle ($) {
+    my ($t) = @_;
+
+    return '' unless $debug;
+
+    if ($ps && $t->[3] =~ m/$ENV{DISTORT_PS_RE}/) {
+       printf PS <<'END',
+ %20.16g %20.16g %20.16g moveto
+ %20.16g %20.16g %20.16g lineto
+ %20.16g %20.16g %20.16g lineto
+ closepath stroke
+END
+               $t->[0][0], $t->[0][1], $t->[0][2],
+               $t->[1][0], $t->[1][1], $t->[1][2],
+               $t->[2][0], $t->[2][1], $t->[2][2],
+               or die $!;
+       flush PS or die $!;
+    }
+
+    sprintf
+       "%11.6f,%11.6f,%11.6f / ".
+       "%11.6f,%11.6f,%11.6f / ".
+       "%11.6f,%11.6f,%11.6f  %-40s ",
+               $t->[0][0], $t->[0][1], $t->[0][2],
+               $t->[1][0], $t->[1][1], $t->[1][2],
+               $t->[2][0], $t->[2][1], $t->[2][2],
+               $t->[3];
+}
+
+sub maybe_subdivide_triangle ($$$$) {
+    my ($t, $ok, $changed, $edge_need_subdivide_fn) = @_;
+
+    print STDERR sprintf_triangle $t if $debug;
+
+    my (@longest) = qw(-1);
+
+    foreach my $ix (0..2) {
+       my $jx = ($ix+1) % 3;
+       next unless $edge_need_subdivide_fn->($t->[$ix], $t->[$jx]);
+       my $l2 = 0;
+       foreach my $ci (0..2) {
+           my $d = $t->[$ix][$ci] - $t->[$jx][$ci];
+           $l2 += $d*$d;
+       }
+       next unless $l2 > $longest[0];
+       @longest = ($l2, $ix, $jx);
+    }
+    if ($longest[0] < 0) {
+       push @$ok, $t;
+       printf STDERR "OK nok=%d nchanged=%d\n",
+           (scalar @$ok), (scalar @$changed)
+           if $debug;
+       print STDERR Dumper(\@$ok) if $debug>=2;
+       return;
+    }
+    my ($dummy,$ix,$jx) = @longest;
+    my $kx = ($ix+2) % 3;
+
+    printf STDERR
+       " S i=%d j=%d k=%d ",
+       $ix, $jx, $kx
+       if $debug;
+    my @midp;
+    foreach my $ci (0..2) {
+       push @midp, 0.5 * ($t->[$ix][$ci] + $t->[$jx][$ci]);
+    }
+
+    printf STDERR
+       " midp %11.6f,%11.6f,%11.6f\n",
+       @midp
+       if $debug;
+
+    # triangle i-j-k, splitting edge i-m
+    # gives    i-m-k, k-m-j
+    my $gensplit = sub {
+       my ($ixjx, $xwhat) = @_;
+       my $n = [ @$t ];
+       $n->[$ixjx] = \@midp;
+       $n->[3] = "$t->[3]$xwhat";
+       printf STDERR "%s\n", sprintf_triangle $n if $debug;
+       unshift @$changed, $n;
+    };
+    $gensplit->($ix, "a$ix$jx");
+    $gensplit->($jx, "b$ix$jx");
+    return;
+}
+
+sub maybe_subdivide ($) {
+    my ($edge_need_subdivide_fn) = @_;
+    
+    my @small_enough = ();
+    while (my $t = shift @$triangles) {
+       maybe_subdivide_triangle $t, \@small_enough, $triangles,
+           $edge_need_subdivide_fn;
+    }
+
+    $triangles = \@small_enough;
+}
+
+sub append_triangle ($) {
+    my ($t) = @_;
+    push @$output, $t;
+}
+
+#---------- set-fa ----------
+
+sub op__set_fa () {
+    $fa = shift_arg;
+}
+
+#---------- project-cylinder ----------
+
+our $project_cylinder_radius;
+our $project_cylinder_max_d_theta;
+
+sub project_cylinder_edge_need_subdivide ($$) {
+    my @thetas = map { $_->[0] / $project_cylinder_radius } @_;
+    return abs($thetas[0] - $thetas[1]) > $project_cylinder_max_d_theta;
+}
+
+sub project_cylinder_tri {
+    my ($t) = @_;
+
+    #print STDERR 'PROJECT', Dumper($t);
+
+    my $radius = $project_cylinder_radius;
+
+    my @ot;
+    foreach my $p (@$t[0..2]) {
+       my ($x,$y,$z) = @$p;
+       my $r = $radius - $y;
+       my $theta = $x / $radius;
+       push @ot, [ $r * sin($theta),
+                   -$r * cos($theta),
+                   $z ];
+    }
+    push @ot, $t->[3].'P';
+    append_triangle \@ot;
+}
+
+sub op__project_cylinder () {
+    $project_cylinder_radius = shift_arg;
+    $project_cylinder_max_d_theta = $fa * TAU/360;
+
+    maybe_subdivide \&project_cylinder_edge_need_subdivide;
+    
+    $output = [];
+    foreach my $t (@$triangles) {
+       project_cylinder_tri $t;
+    }
+    $triangles = $output;
+}
+
+#---------- main program ----------
+
+our $raw;
+
+while (@ARGV && $ARGV[0] =~ m/^-/) {
+    $_ = shift @ARGV;
+    last if m/^--$/;
+    if (s/^--raw$//) {
+       $raw = 1;
+    } else {
+       die "$_ ?";
+    }
+}
+
+my $itmp;
+my $otmp;
+
+my $admesh_stdout = '--write-ascii-stl /dev/fd/3 3>&1 >/dev/null';
+
+if ($raw) {
+    open I, "<& STDIN";
+    $otmp = *STDOUT;
+} else {
+    $itmp = new File::Temp;
+    $otmp = new File::Temp;
+
+    system "cat >$itmp";
+
+    open I, "admesh $admesh_stdout $itmp |";
+}
+
+my $triangle;
+
+while (<I>) {
+    s/^\s*//;
+    if (m/^outer\s+loop/) {
+       die if $triangle;
+       $triangle = [];
+    } elsif (s/^vertex\s+//) {
+       my $lhs = $&;
+       s/\s+$//;
+       my @xyz = split /\s+/, $_;
+       die unless $triangle;
+       push @$triangle, \@xyz;
+    } elsif (m/^endloop/) {
+       die unless @$triangle == 3;
+       push @$triangle, $.;
+       push @$triangles, $triangle;
+       undef $triangle;
+    } elsif (m/^(?:solid|facet\s+normal|endfacet|endsolid)\s/) {
+    } else {
+       die "$_ ?";
+    }
+}
+
+close I;
+<I> if 0; # suppresses Name "main::I" used only once
+
+while (@ARGV) {
+    my $op = shift_arg;
+    $op =~ y/-/_/;
+    &{ ${*::}{"op__$op"} };
+}
+
+select $otmp;
+
+print "solid distort-stl\n";
+
+foreach my $t (@$triangles) {
+    print "  facet normal 0 0 0\n";
+    print "    outer loop\n";
+    die unless @$t==4;
+    foreach my $p (@$t[0..2]) {
+       die unless @$p==3;
+       print "      vertex";
+       printf " %.18g", $_ foreach @$p;
+       print "\n";
+    }
+    print "    endloop\n";
+    print "  endfacet\n";
+}
+
+print "endsolid distort-stl\n";
+
+flush $otmp;
+
+if (!$raw) {
+    system "admesh --normal-values $admesh_stdout $otmp";
+}
diff --git a/diziet-utils/COPYING b/diziet-utils/COPYING
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
similarity index 100%
rename from README.md
rename to diziet-utils/README.md
diff --git a/doveclip.scad b/doveclip.scad
new file mode 100644 (file)
index 0000000..0536c1c
--- /dev/null
@@ -0,0 +1,161 @@
+// -*- C -*-
+//
+// doveclip.scad
+//
+// 3D design for a fastener suitable for Reprapss
+// Copyright 2012,2016 Ian Jackson
+//
+// This work is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This work 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 work.  If not, see <http://www.gnu.org/licenses/>.
+
+
+toothheight = 1.2;
+webthick = 1.8;
+height = 7;
+pinlengthfact = 1.2;
+nomrad = height/2 - toothheight;
+minrad = nomrad - 0.75;
+maxrad = nomrad + 0.25;
+jawthick = 1.5;
+
+webgap = 0.4;
+basepinclear = 1.0;
+
+toothgap = webthick + webgap*2;
+basethick = toothheight;
+
+module DoveClipPin(h=height) {
+  pinh = h * pinlengthfact;
+  pinheight = nomrad*2 + jawthick*2;
+  translate([0,0, pinheight/2]) intersection(){
+    union(){
+      for (m=[0,1]) {
+       mirror([0,0,m]) translate([0,0,pinheight/2]) rotate([90,0,0])
+         cylinder($fn=20, r1=minrad, r2=maxrad, h=pinh);
+      }
+      translate([-webthick/2, -pinh, -pinheight/2-1])
+       cube([webthick, pinh, pinheight+2]);
+    }
+    translate([-maxrad-1, -pinh-1, -pinheight/2])
+      cube([maxrad*2+2, pinh+2, pinheight]);
+  }
+}
+
+function DoveClip_depth() =
+  basethick + nomrad*2 + toothheight;
+
+module DoveClipEnd(baseextend=1, h=7) {
+  cubex = nomrad*2 + jawthick*2;
+  cube0y = -basethick-nomrad*2-toothheight;
+  centrey = -basethick-nomrad;
+  difference(){
+    translate([-cubex/2, cube0y, 0])
+      cube([cubex, -cube0y+baseextend, h]);
+    translate([0, centrey, -1])
+      cylinder($fn=20, r=nomrad, h=h+2);
+    translate([-toothgap/2, cube0y-1, -1])
+      cube([toothgap, toothheight+nomrad+1, h+2]);
+  }
+}
+
+module DoveClipPair(baseextend=1, h=7) {
+  delta =  nomrad*2 + jawthick*2 + toothgap;
+  for (x=[-delta/2,delta/2])
+    translate([x,0,0])
+      DoveClipEnd(baseextend=baseextend, h=h);
+}
+
+module DoveClipPairBase(baseextend=0.1, h=7, count=2) {
+  delta = nomrad*2 + jawthick;
+  intrude = nomrad + basethick - basepinclear;
+  for (i=[0:count-1]) {
+    translate([(i - (count-1)/2) * delta, 0, 0])
+      DoveClipEnd(baseextend=baseextend, h=h);
+  }
+  translate([-delta * count/2, -intrude, 0])
+    cube([delta * count, intrude+0.1, h]);
+}
+
+module DoveClipPairSane(baseextend=0, h=7, count=2) {
+  rotate([0,0,90])
+    translate([0, DoveClip_depth(), 0])
+    DoveClipPairBase(baseextend=baseextend, h=h, count=count);
+}
+
+function DoveClipPairSane_width(count=2) =
+  2 * (nomrad + jawthick + ((nomrad*2 + jawthick) * (count-1)/2));
+
+module ExtenderPillar(length, height,
+                     pillarw=3.5, pillarslope=0.75, webthick=1) {
+  pillarr=pillarw/2;
+  d = 0.25;
+
+  intangle = atan(pillarslope);
+  polyjx = sin(intangle)*pillarr;
+  polyjy = cos(intangle)*pillarr;
+  polyex = -tan(intangle+90)*pillarr;
+  webmidy = height/2+d;
+
+  for (xmir=[0,1])
+    translate([0,0,height/2]) mirror([0,0,xmir])
+      translate([0,0,-height/2]) {
+      intersection() {
+       translate([-1, -pillarr-5, 0.01])
+         cube([length+2, height+pillarr*2+10, height]);
+       mirror([1,0,0]) rotate([0,-90,0])
+         linear_extrude(height=length) union(){
+         circle(r=pillarr, $fn=20);
+         polygon([[polyjx,polyjy-0.1], [polyex, 0],
+                  [polyjx,-(polyjy-0.1)]]);
+         polygon([[0,-webthick/2], [0,webthick/2],
+                  [webmidy,webthick/2], [webmidy,-webthick/2]]);
+       }
+      }
+    }
+}
+
+module ExtenderPillars(length, width, height,
+                      pillarw=3.5, pillarslope=0.75, webthick=1,
+                      baseweb=false, basewebthick=1) {
+  pilesw = width - pillarw;
+
+  for (ymir=[0,1]) mirror([0,ymir,0]) translate([0,-pilesw/2,0]) {
+      ExtenderPillar(length, height, pillarw, pillarslope, webthick);
+    }
+
+  if (baseweb) {
+    translate([0, -pilesw/2, 0])
+      cube([length, pilesw, basewebthick]);
+  }
+}
+
+module DoveClipExtender(length, ha=7, hb=7, counta=2, countb=2,
+                       pillarw=3.5, pillarslope=0.75, webthick=1) {
+
+  mirror([1,0,0])
+    DoveClipPairSane(h=ha, count=counta);
+  translate([length,0,0])
+    DoveClipPairSane(h=hb, count=countb);
+  pillarlen = length - DoveClip_depth() * 2 + 2;
+
+  pilesw = min(DoveClipPairSane_width(counta), DoveClipPairSane_width(countb))
+    - 0.5;
+  pilesh = min(ha, hb) - 0.5;
+
+  translate([DoveClip_depth() - 1, 0, 0])
+    ExtenderPillars(pillarlen, pilesw, pilesh,
+                   pillarw=pillarw, pillarslope=pillarslope,
+                   webthick=webthick);
+}
+
+//DoveClipExtender(length=100, ha=16, hb=20, counta=3, countb=4);
diff --git a/dovecliptest.scad b/dovecliptest.scad
new file mode 100644 (file)
index 0000000..e7e075c
--- /dev/null
@@ -0,0 +1,15 @@
+// -*- C -*-
+
+include <doveclip.scad>
+
+for (y=[0,-15]) translate([0,y,0]) {
+  DoveClipPair();
+
+  translate([-8,0,0])
+    cube([16,5,7]);
+  translate([15,0,0])
+    DoveClipPin();
+}
+
+translate([0,20,0])
+  DoveClipPairBase();
diff --git a/dungeonquest-cone.scad b/dungeonquest-cone.scad
new file mode 100644 (file)
index 0000000..b56ff91
--- /dev/null
@@ -0,0 +1,19 @@
+// -*- C -*-
+
+basesz=12;
+height=14.7;
+topsz=0.5;
+dsz=0;
+
+module One(){ ////toplevel
+  cylinder(h=height, r1=basesz/2-dsz, r2=topsz/2-dsz, $fn=50);
+}
+
+module Four(){ ////toplevel
+  for (x=[0,1]) {
+    for (y=[0,1]) {
+      translate([x*(basesz+3), y*(basesz+3), 0])
+       One();
+    }
+  }
+}
diff --git a/dungeonquest-cone.slic3r b/dungeonquest-cone.slic3r
new file mode 100644 (file)
index 0000000..b7d882f
--- /dev/null
@@ -0,0 +1,2 @@
+bed_temperature = 80
+temperature = 205
diff --git a/earring-stand.scad b/earring-stand.scad
new file mode 100644 (file)
index 0000000..85c28a8
--- /dev/null
@@ -0,0 +1,345 @@
+// -*- C -*-
+
+include <commitid.scad>
+
+front_height = 80;
+front_width = 120;
+front_setback = 30;
+front_thick = 2.4;
+
+front_hex_stride = 12.5;
+front_hex_dia = 9.5;
+
+front_hex_y_fudge = -0.65;
+
+front_surround_lr =3;
+
+back_thick = 3;
+back_pillarw = 6;
+
+base_thick = 2.4;
+
+eclip_inner_rad = 2.5;
+eclip_gap_rad = 0.1;
+eclip_prong_th = 2.25;
+eclip_outer_strt = 0.5;
+eclip_inner_xstrt = 0.5;
+
+eclip_ult_angle = 44;
+eclip_base_epsilon = 0.5;
+
+eclip_each_len = 6;
+eclip_each_every = 29;
+
+test_alpha = 10;
+test_main_th = 1.5;
+test_eclips = 5;
+test_base_th = 2.5;
+test_len = eclip_each_len + eclip_each_every*(test_eclips-1);
+
+num_eclips = 5;
+
+// calculated
+
+include <utils.scad>
+
+eclip_inner_strt = eclip_outer_strt + eclip_inner_xstrt;
+
+r0 = eclip_inner_rad;
+r1 = r0 + eclip_gap_rad;
+r2 = r1 + eclip_prong_th;
+r2e = r1 + eclip_base_epsilon;
+
+ppxl = -(r0 / sqrt(2)) + (eclip_inner_strt / sqrt(2));
+
+rgap = eclip_gap_rad;
+
+eclip_base_offset = r1;
+eclip_wall_offset = -ppxl;
+
+eclip_ra_offset = r2 - 0.1;
+
+eclip_recept_height = r2;
+
+eclip_rhs_offset = ppxl + rgap + eclip_prong_th;
+// does not include main_th
+
+$fn=70;
+
+module EclipLPlanCore(alpha){
+  FArcSegment(0,0, r1,r2,
+             180-eclip_ult_angle, eclip_ult_angle-alpha +1);
+
+  difference(){
+    hull(){
+      intersection(){
+       circle(r2);
+       rotate(-alpha) mirror([1,1]) square([r2e, 50]);
+     }
+      rotate(-alpha) mirror([1,1]) square([r2e, r2]);
+    }
+    circle(r1);
+  }
+}
+
+module EclipRPlan(alpha, main_th){
+  intersection(){
+    rotate(alpha)
+      translate([ppxl + main_th + rgap, -r2*2])
+      square([eclip_prong_th, r2*(2 + 1/sqrt(2))]);
+    translate([-r2, -r2e])
+      square([r2*3, eclip_base_epsilon + r2*4]);
+  }
+}
+
+module EclipLPlan(alpha){
+  rotate(alpha) EclipLPlanCore(alpha);
+}
+
+module EclipPPlan(main_th){
+  intersection(){
+    hull(){
+      circle(r0);
+      rotate(90-eclip_ult_angle) square([r0,r0]);
+    }
+    translate([-(r0+.1), -(r0+.1)])
+      square([(r0+.1) + main_th + ppxl, r2*2]);
+  }
+  translate([ppxl, 0]) square([main_th, r2]);
+}
+
+module TestBase(){ ////toplevel
+  translate([0,0, eclip_base_offset]){
+    for (i=[1 : 2: test_eclips-2]) {
+      translate([0, i*eclip_each_every])
+       rotate([90,0,0])
+       linear_extrude(height=eclip_each_len)
+       EclipLPlan(test_alpha);
+    }
+    for (j=[0 : 2: test_eclips-1]) {
+      translate([0, j*eclip_each_every])
+       rotate([90,0,0])
+       linear_extrude(height=eclip_each_len)
+       EclipRPlan(test_alpha, test_main_th);
+    }
+  }
+  translate([-r2, -eclip_each_len, -test_base_th]){
+    difference(){
+      cube([r2*2,
+           test_len,
+           test_base_th]);
+      mirror([0,0,1]) Commitid_BestCount_M([r2*2, test_len]);
+    }
+  }
+}
+
+module TestProtr(){ ////toplevel
+  difference(){
+    translate([0,0, test_main_th - eclip_wall_offset])
+      rotate([0,90,0])
+      linear_extrude(height=test_len)
+      EclipPPlan(test_main_th);
+    mirror([0,0,1]) Commitid_BestCount_M([test_len, r2]);
+  }
+}
+
+module TestRAProtr(){ ////toplevel
+  rotate([-90,0,0]) TestProtr();
+  mirror([1,0,0])
+    translate([-test_len,
+              -r2,
+              -(eclip_ra_offset + test_base_th)])
+    cube([test_len,
+         r2*2,
+         test_base_th]);
+}
+
+module TestPlanDemo(){
+  color("red") EclipLPlan(test_alpha);
+  color("blue") rotate(test_alpha) EclipPPlan(test_main_th);
+  color("green") EclipRPlan(test_alpha, test_main_th);
+}
+
+beta = asin(front_setback / front_height);
+
+uf = [-sin(beta), cos(beta)];
+ur = [ -uf[1], uf[0]];
+
+pp = [0, 0];
+pq = pp + uf*front_height + ur*eclip_ra_offset;
+pr = [ pq[0] - eclip_base_offset - eclip_wall_offset,
+       0 ];
+
+echo("uf ur P Q R", uf, ur, pp, pq, pr);
+
+module Sketch(){
+  polygon([pq, pp, pr]);
+}
+
+thicks = [ base_thick, front_thick, back_thick ];
+
+module Joins(alpha, objnum, objnum_f, objnum_m) {
+  pitch = (front_width - eclip_each_len) / (num_eclips-1);
+  
+  thm = thicks[objnum_m];
+  stride = (front_width - eclip_each_len) / (num_eclips-1);
+
+  if (objnum==objnum_f) {
+    for (i=[ 1 : 2 : num_eclips-1 ]) {
+      translate([0, i*stride + eclip_each_len, 0]) {
+       rotate([90,0,0])
+       linear_extrude(height=eclip_each_len)
+         EclipLPlan(alpha);
+      }
+    }
+    for (i=[ 0 : 2 : num_eclips-1 ]) {
+      translate([0, i*stride + eclip_each_len, 0]) {
+       rotate([90,0,0])
+       linear_extrude(height=eclip_each_len)
+         EclipRPlan(alpha, thm);
+      }
+    }
+  }
+  if (objnum==objnum_m)
+    mirror([0,1,0])
+      rotate([90,0,0])
+      linear_extrude(height=front_width)
+      rotate(alpha)
+      EclipPPlan(thm);
+}
+
+function r3(pc) = [ pc[0], 0, pc[1] ];
+
+module ObjectJoins(objnum){
+  translate(r3(pp))                   Joins(beta, objnum, 0,1);
+  translate(r3(pr)) mirror([1,0,0])   Joins(0,    objnum, 0,2);
+  translate(r3(pq)) rotate([0,90,0]) mirror([1,0,0]) Joins(-beta, objnum, 2,1);
+}
+
+module Base(){
+  xmin = pr[0] - eclip_rhs_offset - thicks[2];
+  xmax = pp[0] + eclip_rhs_offset + thicks[1]
+    + eclip_prong_th * (1/cos(beta) - 1)
+    + eclip_base_offset * tan(beta);
+  intersection(){
+    ObjectJoins(0);
+    translate([xmin,
+              -1,
+              -50])
+      cube([xmax - xmin,
+           front_width + 2,
+           300]);
+  }
+  translate([xmin,
+            0,
+            -eclip_base_offset - thicks[0]]){
+    difference(){
+      cube([xmax - xmin,
+           front_width,
+         thicks[0]]);
+      translate([xmax-xmin, front_width]/2)
+       rotate([0,0,270])
+       Commitid_Full16_M();
+    }
+  }
+}
+
+module FrontPattern(){
+  totalh = front_height - eclip_wall_offset + thicks[1];
+
+  ystride = front_hex_stride;
+  xstride = front_hex_stride * cos(30) * 2;
+
+  difference(){
+    square([front_width, totalh]);
+    translate([ front_surround_lr,
+               eclip_recept_height ])
+      square([ front_width - front_surround_lr*2,
+              totalh - eclip_recept_height*2
+              ]);
+  }
+    
+  difference(){
+    square([front_width, totalh]);
+    for (xi=[ -5 : 5 ]) {
+      translate([front_width/2 +
+                xi * xstride,
+                0]) {
+       for (yi=[ 0 : 10 ]) {
+         //echo(yi);
+         translate([0, yi * ystride +
+                    front_hex_dia*front_hex_y_fudge]) {
+           for (dv=[ [0,0],
+                     [-xstride/2, -ystride/2]
+                     ])
+             translate(dv)
+               circle(r= front_hex_dia/2, $fn=6);
+         }
+       }
+      }
+    }
+  }
+}
+
+module Front(){
+  ObjectJoins(1);
+  rotate([0, 90-beta, 0])
+    translate([0, 0, ppxl])
+    rotate([0,0,90]) {
+    linear_extrude(height=thicks[1])
+      FrontPattern();
+  }
+}
+
+module Back(){
+  ObjectJoins(2);
+
+  zmin = pr[1];
+  zmax = pq[1] + eclip_prong_th;
+  height = zmax - zmin;
+
+  translate([pr[0] + eclip_wall_offset - thicks[2],
+            0, 0])
+    rotate([0,90,0])
+    rotate([0,0,90]) {
+    difference(){
+      cube([front_width,
+           height,
+           thicks[2]]);
+      translate([back_pillarw,
+                eclip_recept_height,
+                -10])
+       cube([front_width - back_pillarw*2,
+             height - eclip_recept_height*2 - eclip_prong_th,
+             20]);
+    }
+  }
+}
+
+module BackPrint(){ ////toplevel
+  rotate([0,-90,0]) Back();
+}
+
+module FrontPrint(){ ////toplevel
+  rotate([0, 90+beta, 0]) Front();
+}
+
+module BasePrint(){ ////toplevel
+  Base();
+}
+
+module Demo(){ ////toplevel
+  color("red") Base();
+  color("blue") Front();
+  color("black") Back();
+}
+
+//PlanDemo();
+//TestBase();
+//TestProtr();
+//TestRAProtr();
+//Sketch();
+//Demo();
+//BackPrint();
+//FrontPrint();
+//BasePrint();
diff --git a/electron-token.scad.pl b/electron-token.scad.pl
new file mode 100755 (executable)
index 0000000..dc24c5a
--- /dev/null
@@ -0,0 +1,140 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Math::Trig;
+use Math::Vector::Real;
+use IO::File;
+use Data::Dumper;
+use constant tau => pi*2;
+
+my $ellipse = 60 / 2;
+my $circle = 3.5 / 2;
+my $xscale = 33 / 100;
+my $N = 180; # around ellipse
+my $M = 80; # around each circle
+
+my $NMdiv = $ENV{'ELECTRONTOKEN_COARSE'} || 1;
+
+$M /= $NMdiv;
+$N /= $NMdiv;
+
+print <<END;
+// -*- C -*-
+// *** AUTOGENERATED - DO NOT EDIT ***
+END
+
+print "torusyup = ", ($circle / sqrt(2)), ";\n";
+
+our @ellipse = map {
+    my $theta = tau * $_ / $N;
+    V( cos($theta) * $ellipse * $xscale, sin($theta) * $ellipse, 0 )
+} 0..($N-1);
+
+#print Dumper(\@ellipse);
+
+our @alongs = map {
+    my $i = $_;
+    $ellipse[ ($i+1) % $N ] - $ellipse[ ($i-1) % $N];
+} 0..($N-1);
+
+our @circles = map {
+    my $i = $_;
+    my $centre = $ellipse[$i];
+    my $axis = $alongs[$i]->versor();
+    my $rad0 = $axis x V(0,0,1);
+    my $rad1 = $rad0 x $axis;
+    [ map {
+       my $theta = tau * $_ / $M;
+       $centre + $circle * ($rad0 * cos($theta) + $rad1 * sin($theta));
+    } 0..($M-1) ];
+} 0..($N-1);
+
+sub scadvec ($) {
+    my ($v) = @_;
+    return "[ ".(join ", ", @$v)." ]"
+}
+
+sub torusy () {
+    print "module Torusy(){ polyhedron(points=[";
+    my $ptix = 0;
+    my @cirpt;
+    foreach my $i (0..$N-1) {
+       foreach my $j (0..$M-1) {
+           print "," if $ptix;
+           print "\n";
+           print "    ",(scadvec $circles[$i][$j]);
+           $cirpt[$i][$j] = $ptix++;
+       }
+    }
+    print "\n  ],\n";
+
+    print "  faces=[";
+    foreach my $i (0..$N-1) {
+       my $i2 = ($i+1) % $N;
+       foreach my $j (0..$M-1) {
+           my $j2 = ($j+1) % $M;
+           print "," if $i || $j;
+           print "\n";
+           print "   [ ", (join ", ",
+                           $cirpt[ $i  ][ $j  ],
+                           $cirpt[ $i  ][ $j2 ],
+                           $cirpt[ $i2 ][ $j  ],
+                          ), " ],";
+           print "   [ ", (join ", ",
+                           $cirpt[ $i  ][ $j2 ],
+                           $cirpt[ $i2 ][ $j2 ],
+                           $cirpt[ $i2 ][ $j  ],
+                          ), " ]";
+       }
+    }
+    print "\n  ]);\n}\n";
+}
+
+torusy();
+
+
+our @distances;
+push @distances, 0;
+foreach my $i (1..$N) {
+    my $dist = $distances[ $i-1 ];
+    $dist += abs($ellipse[$i % $N] - $ellipse[$i-1]);
+    $distances[$i] = $dist;
+}
+
+sub infodistprop ($) {
+    my ($distprop) = @_;
+    # returns
+    #   ( $ellipse_centreline_point,
+    #     $along_vector )
+    my $dist = $distprop * $distances[$N];
+    foreach my $i (0..$N-1) {
+       my $prorata =
+           ($dist - $distances[$i]) /
+           ($distances[$i+1] - $distances[$i]);
+       next unless 0 <= $prorata && $prorata <= 1;
+       print "// infodistprop $distprop => #$i=$ellipse[$i] $prorata $ellipse[$i+1]\n";
+       return (
+               (1-$prorata) * $ellipse[$i] + ($prorata) * $ellipse[$i+1],
+               $alongs[$i],
+              );
+    }
+    die "$distprop ?";
+}
+
+while (<DATA>) { print };
+
+STDOUT->error and die $!;
+STDOUT->flush or die $!;
+
+__DATA__
+module Token(){
+    difference(){
+       for (rot=[ 0,120,240 ])
+           rotate([0,0, rot])
+           translate([0,0,torusyup])
+           Torusy();
+       translate([-200,-200,-50])
+           cube([400,400,50]);
+    }
+}
+Token();
diff --git a/experimental-prefix-nonworking b/experimental-prefix-nonworking
new file mode 100644 (file)
index 0000000..1b679f3
--- /dev/null
@@ -0,0 +1,5 @@
+G28 ; home all axes
+G1 F500
+G1 Y10
+M83 ; extruder relative
+G1 F60
diff --git a/fairphone-battery-case.scad b/fairphone-battery-case.scad
new file mode 100644 (file)
index 0000000..557271d
--- /dev/null
@@ -0,0 +1,190 @@
+// -*- C -*-
+
+include <utils.scad>
+
+mainwall_th = 3.0;
+smallwall_th = 2.0;
+
+seal_th = 0.3 + 0.6 + 0.6 - 0.4 - 0.4 + 0.2; // total gap for seal etc.
+behind_recess = 1.5;
+
+recess_gap_end = 0.4;
+
+lid_edge_th = 0.5;
+
+battery_len = 66.55 + 1.25 -.55;
+battery_th = 6.55 + 0.75 - .60;
+battery_wdth = 44.38 + 0.75 -.55;
+
+battery_base_indent = 0.94 + 0.50;
+battery_base_indent_fromside_outside = 4;
+battery_base_indent_fromside_inside = 10;
+
+handle_height = 3.5;
+handle_inward = 10;
+handle_len = 5;
+
+pushhole_ell_sz = 4.75;
+pushhole_ell_th = 1.75;
+pushhole_circle_dia = 4.0;
+
+// for testing:
+//battery_len = 3;
+//battery_wdth = 15;
+//battery_base_indent_fromside_inside = 6;
+
+// calculated
+
+bpp0 = [0,0];
+bpp1 = bpp0 + [ 0, mainwall_th - behind_recess ];
+lppA = bpp1 + [ seal_th, -recess_gap_end ];
+lppB = lppA + [ lid_edge_th, 0 ];
+bpp2 = [ lppB[0], bpp1[1] ];
+bpp3 = [ bpp2[0] + (bpp1 - bpp0)[1], bpp0[1] ];
+bpp4 = [ bpp3[0], bpp0[1] + mainwall_th ];
+lppC = bpp3 + [ 0, -recess_gap_end ];
+
+lppF = lppC + [ handle_height, 0 ];
+
+s0 = battery_wdth/2;
+s0i = s0 - battery_th/2;
+s1 = s0 + smallwall_th;
+
+l1 = s1 - handle_inward;
+l0 = l1 - handle_len;
+
+echo(
+     bpp0,
+     bpp1,
+     bpp2,
+     bpp3,
+     bpp4,
+     bpp5,
+     bpp6,
+     bpp7,
+     bpp8
+);
+
+echo(
+     lppA,
+     lppB,
+     lppC,
+     lppD,
+     lppE
+);
+
+bpp8 = bpp0 + [ -battery_len,0 ];
+bpp5 = [ bpp8[0] - smallwall_th, bpp4[1] ];
+bpp9 = [ bpp0[0], bpp0[1] - battery_th/2 - 1.0 ];
+bpp7 = [ bpp8[0], bpp9[1] ];
+bpp6 = [ bpp5[0], bpp9[1] ];
+lppE = [ lppA[0], bpp9[1] ];
+lppD = [ lppC[0], bpp9[1] ];
+
+module BaseHalfPlan(indent=0){
+  polygon([ bpp0,
+           bpp1,
+           bpp2,
+           bpp3,
+           bpp4,
+           bpp5,
+           bpp6,
+           bpp7 + indent * [1,0],
+           bpp8 + indent * [1,0]
+           ]);
+}
+
+module SideHalfPlan(){
+  polygon([ bpp5,
+           bpp6,
+           bpp9,
+           bpp1
+           ]);
+}
+
+module LidHalfPlan(){
+  polygon([ lppA,
+           lppE,
+           lppD,
+           lppC,
+           lppB
+           ]);
+}
+
+module HandleHalfPlan(){
+  translate(lppE)
+    square(lppF - lppE);
+}
+
+module ExtrudePlan(from,to){
+  rotate([0,-90,0])
+  for (mj=[0,1]) {
+    mirror([0,0,mj]) translate([0,0,from]){
+      linear_extrude(height= to-from, convexity=5){
+       for (mi=[0,1]) {
+         mirror([0,mi])
+           translate([0, battery_th/2])
+           children(0);
+       }
+      }
+    }
+  }
+}
+
+module PushHolePlan(){ ////toplevel
+  translate(-(pushhole_ell_th * 0.10 +
+             pushhole_ell_sz * 0.10) * [1,1]) {
+    for (r=[0,90])
+      rotate(r)
+       translate(-pushhole_ell_th * 0.5 * [1,1])
+       square([ pushhole_ell_sz, pushhole_ell_th ]);
+  }
+  circle(pushhole_circle_dia/2, $fn=40);
+}
+
+module PlanDemo(){ ////toplevel
+  color("blue") BaseHalfPlan();
+  color("red") LidHalfPlan();
+  translate([0,0,-1]) color("lightblue") SideHalfPlan();
+}
+
+module Base(){ ////toplevel
+  difference(){
+    ExtrudePlan(0,s1) BaseHalfPlan();
+    linextr(-(10+battery_len), battery_len+10, convexity=5) PushHolePlan();
+  }
+  difference(){
+    union(){
+      ExtrudePlan(s0i, s1) SideHalfPlan();
+      ExtrudePlan(s0 - battery_base_indent_fromside_inside,
+                 s0 - battery_base_indent_fromside_outside
+                 ) BaseHalfPlan(indent = battery_base_indent);
+    }
+    for (m=[0,1])
+      mirror([m,0,0])
+       translate([s0i, 0, bpp7[0] - 0.1])
+       cylinder(r= battery_th/2, h=100, $fs=0.5);
+  }
+}
+
+module BaseHalfTest(){ ////toplevel
+  intersection(){
+    Base();
+    translate([-100,0,-100])
+      cube([200,200,200]);
+  }
+}
+
+module Lid(){ ////toplevel
+  ExtrudePlan(0,s1) LidHalfPlan();
+  ExtrudePlan(l0,l1) HandleHalfPlan();
+}
+
+module Demo(){ ////toplevel
+  %Base();
+  Lid();
+}
+
+//PlanDemo();
+//Demo();
+//Base();
diff --git a/fairphone-case-mounted.scad b/fairphone-case-mounted.scad
new file mode 100644 (file)
index 0000000..8999348
--- /dev/null
@@ -0,0 +1,14 @@
+// -*- C -*-
+
+include <bike-phone-mount.scad>
+
+module CaseMounted(){ ////toplevel
+  Case();
+  translate([ phone_width/2,
+             -phone_height/2, epp3[1] - case_th_bottom ])
+    Mount();
+}
+
+//// toplevels-from:
+include <fairphone-case.scad>
+$suppress_forward_holes = true;
diff --git a/fairphone-case.scad b/fairphone-case.scad
new file mode 100644 (file)
index 0000000..2998b54
--- /dev/null
@@ -0,0 +1,1755 @@
+// -*- C -*-
+
+// Hard case for Fairphone 2
+//
+//  Copyright 2018 Ian Jackson.  There is NO WARRANTY.
+//  See below for full licensing and disclaimer.
+//
+// Instructions
+//
+//  1. You will want to git clone this repository.
+//
+//  2. Decide about the notification LED aperture. See the variable
+//     led_window_style, below.  The default here is "ad-hoc
+//     multi-colour", which can produces a translucent (clear-ish)
+//     window set into the lid, even on a single-nozzle printer.
+//     See "Ad-hoc multi-colour", below.
+//
+//  3. use "make" to generate the necessary files:
+//
+//     make -j8 fairphone-case.auto.scads `for f in   \
+//        HingePrint        \
+//        LidWindowPrint    \
+//        LidPrint          \
+//        OneKeeperPrint    \
+//        Case              \
+//     ; do echo fairphone-case,$f.auto.stl; done`
+//
+//  4. Print them.  Case and OneKeeperPrint should probably be
+//     the same colour.
+//
+//     For Lid and LidWindowPrint, if you are doing ad-hoc
+//     multi-colour:
+//        i.   Set up for clear filament
+//        ii.  Print LidWindowPrint.  Wait for it to finish.
+//             It won't take long.  As soon as it finishes, tell
+//             your printer to warm up (so that in fact it does
+//             not cool down).
+//        iii. Leaving the output so far on the printbed, reload
+//             your printer with the main lid colour.
+//        iv.  Print LidPrint.  You can let this go unattended.
+//
+//  5. Assemble the hinge.  You will need 4x M2 12mm machine screws
+//     and 8x M2 full nuts.
+//
+//     Make sure you get the hinge the right way round.  If you're not
+//     sure, run
+//         openscad fairphone-case,DemoHinge.auto.scad
+//     to see an assembly diagram.
+//
+//     The nuts recess into the hinge.  You will want very fine
+//     pliers.  As you screw each screw in, add the second nut when
+//     the screw thread emerges from the first - this will be a
+//     locknut.  Screw each screw to an appropriate tightness for the
+//     hinge stiffness.  You want the lid-side hinge to be stiffer as
+//     that makes closing the case work better.
+//
+//     When you have the stiffness right, tighten the locknuts onto
+//     each first nut.
+//
+//  6. In use:
+// 
+//      - To put the phone in, drop its RH side into the RH side of
+//        the case.  Then feed the keeper through the small hole.
+//        Feed it right through.
+//
+//      - The optional prop can be used to prop the phone up (in
+//        portrait orientation only right now).  See
+//            openscad fairphone-case,DemoPropAngles.auto.scad
+//
+// Ad-hoc multi-colour
+//     
+//  This file is set up to let you make a translucent window using a
+//  single-extruder printer, using a "two print run" technique.  This
+//  works well with our Lulzbot TAZ 5 and Aleph Objects' version of
+//  Cura.  If you are using a different printer, you may need to
+//  adjust the parameters or try a different technique.  In particular,
+//      initial_layer_thick
+//         set so that the window is one layer thick
+//      initial_layer_width
+//         set so that the slicer draws a rectangle around the whole
+//         object, rather than putting a "skirt" or anything inside
+//
+//  If you have a dual-extruder printer, you can set led_window_style
+//  to 2 and do a single print of LidPrint and LidWindowPrint.
+//
+//  Alternatively you can set it to 1 (just a hole) or 0 (no hole).
+//
+//  Thanks to Clare Boothby for the ad-hoc multi-colour technique (and
+//  the parameters for our Lulzbot TAZ 5 and Aleph Objects's Cura).
+//
+// Other phones
+//
+//  It might well be possible to adapt this file for other phones.
+//  If you do, let me know how you get on.
+//
+//
+// AUTHORSHIP, COPYRIGHT, LICENCE, AND LACK OF WARRANTY
+//
+//   Copyright (C)2018 Ian Jackson.
+//
+//    This program for generating a 3D model is free software: you can
+//    redistribute it and/or modify it under the terms of the GNU
+//    General Public License as published by the Free Software
+//    Foundation, either version 3 of the License, or (at your option)
+//    any later version.
+//
+//    This program is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public
+//    License along with this program.  If not, see
+//    <http://www.gnu.org/licenses/>.
+//
+//  In particular DO NOT BLAME ME IF THIS CASE DOES NOT ADEQUATELY
+//  PROTECT YOUR PHONE !  It is your responsibility to decide whether
+//  this case will meet your needs.
+
+include <utils.scad>
+include <funcs.scad>
+
+phone = [ 75.0, 145.0 ];
+
+prop_buildout_less = 3;
+
+prop_angles = [ 15, 30, 45, 60 ];
+
+bumper = [ 0.250, -0.025 ];
+// ^ One side.  Overall size is increased by twice this.
+// If no bumpers, is the gap around the phone.
+
+enable_support = 1;
+
+led_window_style = 3;
+// 0: no window
+// 1: simply an opening
+// 2: opening with separate cover model, for printing in clear (two colour)
+// 3: like 2 but one-layer window for ad-hoc multi-colour
+
+initial_layer_thick = 0.400; // ^ needed for mode 3 only
+initial_layer_width = 0.750; // ^ needed for mode 3 only
+multicolour_gap = 0.15; // each side
+
+phone_cnr_rad = 6.0;
+phone_rim_depth = 0.80; // includes allowance for a screen protector
+
+button_cutout_depth = 9;
+
+phone_edge_thick = 9.0;
+phone_total_thick = 12.0;
+phone_backside_slope_inner = 1.5; // larger means shallower
+phone_backside_slope_outer = 1.0; // larger means shallower
+
+camera_pos_tl = [  6.450, 12.750 ]; // measured from tl corner
+camera_pos_br = [ 22.300, 37.600 ]; // tl/br as seen from back
+
+jack_pos = [ 13.83, 8.485 ];
+jack_dia = 10.64 + .5; // some jack I had lying around
+
+led_pos = [ 13.98, 10.00 ];
+led_aperture = 9;
+led_window_ledge = 0.75; // each side
+
+noisecancelmic_pos = [ 19.54, 7.37 ];   // from rhs
+noisecancelmic_dia = 4.00;
+
+//fingerpushhole_dias = [ 15, 18 ];
+fingerpushhole_dias = [];
+
+lanyard_half_dia = 1.15;
+lanyard_entry_rel_breadth = 2;
+lanyard_channel_len = 15;
+rearspeaker_pos_bl = [ 12.64, 18.72 ];
+rearspeaker_size   = [  3.76,  7.36 ];
+
+microusb_above = 3.27 - 0.25;
+microusb_below = 0.0;
+microusb_width = 16.12 + 1.25;
+
+case_th_bottom = 2.5;
+case_th_lid = 3.0;
+case_th_side = 2.3;
+case_th_lip = 1.2;
+
+lid_screen_gap_extra = .66;
+
+case_struts_count = 6;
+case_struts_solid_below = 1.00;
+case_struts_solid_above = 0.75;
+case_struts_width = 0.10;
+
+keeper_th_z = 0.75;
+keeper_th_x = 0.75;
+keeper_inner_width = 2.75;
+keeper_inner_height = 2.75;
+keeper_slant_slope = 2; // larger means steeper
+
+keeper_gap_z_top = 0.25;
+keeper_gap_z_bot = 0.75;
+keeper_gap_x     = 0.25;
+keeper_gap_x_holes = 0.75;
+
+keeper_side = 0; // 0 = lhs; 1 = rhs
+
+case_lip = 1.25;
+
+lid_gap_x = 0.25;
+lid_gap_z = 0.25;
+lid_lip = 1.75;
+lid_edgepart_width = 5.0;
+lid_buttoncover_thick = 1.3;
+lid_buttoncover_reinf = 0.65;
+
+foldover_gap = 0.50;
+foldover_lever_gap = 0.50;
+
+// properties of the hinge fasteners
+hingescrew_shaft_dia = 2.0 + 0.25; // M2 x 12mm machine screw
+hingescrew_shaft_len = 12;
+hingescrew_fasteners_extra_thick = 0.40;
+// ^ amount of thread protruding if everything was completely nominal
+//   and we are using two nuts
+hingescrew_nut_access_dia = 4.72 + 0.50;
+// ^ washer is 4.72 dia
+//   also, want to get pliers or tiny spanner in to do up locknut
+hingescrew_nut_across = 3.92 + 0.25; // incl. slop around recess slop
+hingescrew_nut_thick = 1.93;
+hingescrew_head_th = 1.38 + 0.75;
+hingescrew_head_dia = 3.92;
+
+hingescrew_nut_recess_portion = 2/3; // portion of nut in recess
+
+lever_cover_th = 0.75;
+hingemount_th = 2.5;
+hingemount_wd = 4.8725;
+
+$fa = 5;
+$fs = 0.1;
+
+button_l_fudge = 4.4;
+buttonishleg_default_l_is_fudge = 10;
+
+hinge_base_slope = 1.5; // bigger is steeper
+
+strut_min_at_end = 1.5;
+
+hinge_x_gap = 0.125;
+hinge_x_postscrew_gap = 0.75;
+hinge_x_arms_gap = 0.35;
+hinge_r_arms_gap = 0.55;
+
+rearspeaker_gap    = [ 2.0, 2.0 ]; // each side
+
+thumbrecess_depth = 1.3;
+thumbrecess_width = 16.5;
+thumbrecess_topcurve_r = 5.0;
+
+prop_recess_under = 0.50;
+prop_recess_slop = 0.200; // each side
+prop_end_dia = 0.5;
+prop_main_th = 3;
+prop_taper_len = 6;
+prop_main_width = 4;
+prop_side_gap = 0.75; // each side
+prop_lidrecess_behind = 0.75;
+prop_caserecess_behind = 0.75;
+prop_caserecess_taper = 0.45; // one side only
+prop_prop_gap = 0.5;
+prop_prong_heel_slope = 0.5;
+
+lid_fold_clearance_antislop = 0.5;
+
+$button_leg_only = false;
+$suppress_forward_holes = false;
+
+// ---------- calculated ----------
+
+phone_width =  (phone + bumper*2)[0];
+phone_height = (phone + bumper*2)[1];
+
+inside_br = [phone_width, -phone_height];
+
+prop_prong_h = prop_main_th;
+
+//echo(camera_pos_tl + bumper,
+//     camera_pos_br + bumper);
+
+// ----- could be changed -----
+lid_buttoncover_gap = lid_gap_x;
+lid_buttoncover_overlap = case_th_lip + keeper_gap_z_top;
+
+phone_backside_slope_thick = phone_total_thick - phone_edge_thick;
+
+//prop_lidrecess_depth = case_th_lid - prop_recess_under;
+
+//prop_nose_len = case_th_lid - prop_recess_under;
+//prop_recess_slope = tan(prop_max_angle); // bigger means steeper
+//prop_recess_width = prop_main_th / cos(prop_max_angle) + prop_backfwd_gap;
+
+
+//lid_lip_overlap_width xxx bad name = ;
+//lid_lip_inner_slope = [ 5, 5 ]; // xxx
+
+epp0 = [0,0];
+epp1 = [0, -phone_edge_thick];
+epp2i = epp1 + phone_backside_slope_thick * [ phone_backside_slope_inner, -1 ];
+epp2o = epp1 + phone_backside_slope_thick * [ phone_backside_slope_outer, -1 ];
+epp3 = epp2i + [10, 0];
+epp5 = epp0 + [0,1] * (keeper_th_z + keeper_gap_z_top + case_lip);
+epp4 = epp5 + [-1,0] * case_th_side;
+
+kppe = [0,0];
+kppd = kppe + [1,0] * keeper_inner_width;
+kppc = kppd + [0,1] * keeper_th_z;
+kppb = [ kppe[0] - keeper_th_x, kppc[1] ];
+kppf = kppe - [0,1] * keeper_inner_height;
+kppa = [ kppb[0], kppf[1] ];
+
+lpp10 = [ epp5[0] + lid_gap_x, kppc[1] + lid_gap_z ];
+lpp11 = [ lpp10[0],            epp5[1] + lid_gap_z ];
+
+lpp14 = lpp10 + [1,0] * max(keeper_inner_width, lid_edgepart_width);
+// exact x posn not very important; must extend past end of keeper
+
+lpp15 = [ lpp14[0],
+         epp0[1] - phone_rim_depth + 1/2.5 * case_th_lid
+         + lid_screen_gap_extra ];
+// ^ beam theory says to maximise force before contact,
+//   the gap below the `beam' (the lid) must be 1/3
+//   the thickness (ie the lid thickness) if the beam
+//   is solid, or 1/2 if it has a top and bottom only.
+//   ours is mostly solid.
+
+lp_r12 = max(case_th_lid - (lpp11[1] - lpp15[1]),
+            case_th_lip);
+
+lpp12 = [ epp4[0] + lp_r12,    lpp11[1] ];
+lpp13 = [ lpp12[0],            lpp12[1] + lp_r12 ];
+
+case_bottom_z = epp2o[1] - case_th_bottom;
+
+// button profile
+bppM = epp4 + [0,5];
+bppN = [ bppM[0] + lid_buttoncover_thick, bppM[1] ];
+bppR = [ bppN[0] + lid_buttoncover_gap, -button_cutout_depth ];
+bppS = [ epp1[0], bppR[1] ];
+bppQ = [ bppM[0], bppR[1] - lid_buttoncover_overlap ];
+bppP = bppQ + [0,1] * lid_buttoncover_gap;
+bppO = [ bppN[0], bppP[1] ];
+bppL = lpp10 + [5,0];
+bppK = [ bppL[0], bppN[1] ];
+bppJ = [ bppN[0], bppL[1] ];
+bppU = [ bppJ[0], lpp12[1] ];
+bppV = lpp11;
+bppW = lpp10;
+
+echo("BUTTON COVER TH", bppO[0] - bppP[0]);
+
+// notification led aperture
+
+nla_r0 = led_aperture/2;
+nla_r1 = nla_r0 + led_window_ledge;
+nla_r2 = nla_r1 + multicolour_gap;
+nla_t =
+  led_window_style >= 3 ? initial_layer_thick :
+  led_window_style >= 2 ? led_window_ledge : 0;
+
+
+// hinge plan
+hp_rn = hingescrew_nut_access_dia/2;
+hp_r2_min = hp_rn + lever_cover_th;
+hp_rs = hingescrew_shaft_dia/2;
+hp_r1_min = hp_rs + hingemount_th;
+
+hp_r1 = max(hp_r1_min, hp_r2_min);
+hp_r2 = hp_r1;
+
+hppU = lpp13;
+hppS = epp2o + [0,-1] * case_th_bottom;
+hp_k = 0.5 * (hppU[1] - hppS[1] + foldover_gap);
+
+hppM = [ epp4[0] - foldover_lever_gap - hp_r2,
+        0.5 * (hppU + hppS)[1] ];
+hppT = [ hppM[0], hppU[1] - hp_r1 ];
+hppB = hppT + [0,-1] * hp_k;
+
+hppE_y = epp2o[1] - case_th_bottom + hp_r1;
+hppE_x = hppB[0] + (hppB[1] - hppE_y) * hinge_base_slope;
+hppE = [ hppE_x, hppE_y ];
+
+// hinge elevation x coords
+
+hex20 = max(epp2o[0],
+           phone_cnr_rad,
+           kppd[0] + hingescrew_head_th + keeper_gap_x_holes);
+hex21 = hex20 + hingemount_wd;
+hex22 = hex21 + hinge_x_gap;
+hex27 = hex20 + hingescrew_shaft_len;
+hex24 = hex27 + hinge_x_postscrew_gap;
+hex23 = hex27 - (hingescrew_nut_thick*2
+                + hingescrew_fasteners_extra_thick);
+hex26 = hex23 + hingescrew_nut_thick * 2/3;
+
+echo(hex20, hex21, hex22, hex23, hex24);
+//  6, 10.8725, 10.9975, 13.74, 18.75
+module chk(act,exp) {
+  if (abs(act-exp) > 1e-9) echo("WRONG", act, exp);
+  else echo("ok", act);
+}
+chk(hex20, 6);
+chk(hex21, 10.8725);
+chk(hex22, 10.9975);
+chk(hex23, 13.74);
+chk(hex24, 18.75);
+
+lid_fold_clearance_skew =
+  (lpp10[1] - hppB[1]) /
+  (lpp10[0] - hppB[0]);
+
+echo("SK",lid_fold_clearance_skew);
+
+// thumb recess (used to be "catch" hence cpp*
+
+cppA = epp4 + [thumbrecess_depth, 0];
+cppB = [ cppA[0], epp1[1] ];
+
+// lanyard
+
+ly_r = lanyard_half_dia / 2;
+ly_rc = ly_r * 2;
+
+ly_theta = -atan2vector(epp2i - epp1);
+ly_o = epp2i + 3 * ly_r * unitvector2d(epp1 - epp2i);
+
+max_case_bottom_edge_thickness =
+  case_th_bottom
+  + sin(ly_theta) * (epp2i-epp2o)[0];
+
+ly_q_z = -(ly_rc + ly_r);
+ly_re = max_case_bottom_edge_thickness - (-ly_q_z);
+
+ly_oec_y = lanyard_entry_rel_breadth * ly_r;
+
+// prop recess in case
+
+prop_x_pos = phone_width/2;
+
+prop_recess_hw = 0.5 * prop_main_width + prop_side_gap;
+
+prc_r1 = prop_end_dia/2;
+prc_r3 = prc_r1 + prop_recess_slop;
+
+prcp2 = [ epp4[0] + prop_buildout_less,
+         case_bottom_z ];
+
+prop_caserecess_buildout_r = -1; // prcp2[0] - epp2o[0];
+
+prcp1 = [ epp2o[0] + prc_r3 + prop_caserecess_behind,
+         epp2i[1] - prc_r3 - prop_recess_under];
+
+// prop recess in lid
+
+prl_r10 = prop_end_dia/2;
+prl_r10o = prl_r10 + prop_recess_slop;
+
+prlp10 = lpp10 + [1,1] * prl_r10o
+  + [1,0] * prop_lidrecess_behind
+  + [0,1] * prop_recess_under;
+
+// prop
+
+$prpp10 = [0,0];
+$prpp11 = [0, prop_taper_len];
+
+$prp_r10 = prl_r10;
+
+// ---------- modules ----------
+
+module AdhocMultiprintFrame(phase, z0, zs) {
+  // from z0 to z0 + zs*layer
+  extra = phase * (initial_layer_width + multicolour_gap) + 5;
+  xextra = extra + -epp4[0];
+  xrange = [ 0, phone_width ] + [-1,+1] * xextra;
+  yextra = extra + -epp4[0];
+  yrange = [ -phone_height + +hppB[0] - hp_r2, 0 ] + [-1,+1] * yextra;
+  p0 = [ xrange[0], yrange[0] ];
+  p1 = [ xrange[1], yrange[1] ];
+  echo(p0, p1);
+  translate([0,0, z0])
+    mirror([0,0, zs<0 ? 1 : 0])
+    linear_extrude(height= initial_layer_thick)
+    difference(){
+      rectfromto(p0 - [1,1] * initial_layer_width,
+                p1 + [1,1] * initial_layer_width);
+      rectfromto(p0, p1);
+    }
+}
+
+module KeeperProfile(slant=0){
+  use_e = kppe + [0,-1] * slant * keeper_inner_width / keeper_slant_slope;
+  polygon([use_e, kppd, kppc, kppb, kppa, kppf]);
+}
+
+module EdgeProfile(){
+  difference(){
+    hull(){
+      translate(epp3) square(case_th_bottom*2, center=true);
+      circleat(epp2o, r=case_th_bottom);
+      circleat(epp1, r=case_th_side);
+      rectfromto(epp0, epp4);
+    }
+    polygon([ epp5 + [0,10],
+             epp1,
+             epp2i,
+             epp3 + [10,0] ]);
+  }
+}
+
+module LanyardLanyardProfile(entry=false){
+  hull(){
+    for (xs=[-1,+1] * (entry ? lanyard_entry_rel_breadth : 1))
+      translate(xs * 0.5 * lanyard_half_dia * [1,0])
+       circle(r= lanyard_half_dia/2);
+  }
+}
+
+module LanyardCurveChannelProfile(){
+  translate([0, -ly_r])
+    LanyardLanyardProfile();
+}  
+
+module LanyardEntryChannelProfile(){
+  LanyardLanyardProfile(true);
+}  
+
+module LanyardMainChannelProfile(){
+  LanyardCurveChannelProfile();
+  difference(){
+    square(center=true, ly_r * [6, 2]);
+    for (xs=[-1,+1])
+      translate(ly_r * [3 * xs, -1])
+       circle(r = ly_r);
+  }
+}
+
+module LanyardEntryOuterProfile(){
+  circleat([ly_re + ly_r, 0], ly_re);
+}
+
+module LanyardEntry(){
+  q_z = ly_q_z;
+  oec_y = ly_oec_y;
+
+  d_x = -ly_rc;
+
+  translate([d_x, 0, q_z]) {
+    intersection(){
+      rotate([90,0,0])
+       rotate_extrude(convexity=10)
+       rotate(90)
+       translate([0, -q_z])
+       LanyardCurveChannelProfile();
+      translate([0,-10,0])
+       cube([20,20,20]);
+    }
+  }
+
+  mirror([0,0,1])
+    translate([0,0,-1])
+    linear_extrude(height=20)
+    rotate(-90)
+    LanyardEntryChannelProfile();
+
+  translate([0, ly_r*2, 0])
+    rotate([90,0,0])
+    linear_extrude(height = ly_r*4){
+    difference(){
+      rectfromto([d_x, q_z], [ly_r, 0]);
+      circleat([d_x, q_z], ly_rc);
+    }
+  }
+
+  translate([0,0,q_z]){
+    for (my=[0,1])
+      mirror([0,my,0]){
+       translate([0, oec_y, 0]){
+         difference(){
+           translate(ly_re * [-1,0,-2])
+             cube(ly_re * [2,1,2]);
+           rotate_extrude(convexity=10)
+             LanyardEntryOuterProfile();
+         }
+       }
+      }
+    difference(){
+      translate([-ly_re, -(oec_y + 0.01), -2*ly_re])
+       cube([ly_re*2, 2*(oec_y + 0.01), 2*ly_re]);
+      for (mx=[0,1])
+       mirror([mx,0,0])
+         rotate([90,0,0])
+         translate([0,0,-10])
+         linear_extrude(height=20)
+         LanyardEntryOuterProfile();
+    }
+  }
+}
+
+module LanyardCutout(l){
+  rotate([0,-90,0])
+    linear_extrude(height=l)
+    rotate(-90)
+    LanyardMainChannelProfile();
+
+  for (ee=[0,1]){
+    translate(ee * l * [-1,0])
+      mirror([ee,0,0])
+      LanyardEntry();
+  }
+}
+
+module LidEdgeProfile(){
+  polygon([ lpp10,
+           lpp11,
+           lpp12,
+           lpp13,
+           lpp13 + [10, 0],
+           lpp15 + [10, 0],
+           lpp15,
+           lpp14,
+           ]);
+  intersection(){
+    circleat(lpp12, r=lp_r12);
+    rectfromto( lpp12 + [-10,   0],
+               lpp12 + [+10, +10] );
+  }
+}
+
+module LidEdgeFoldClearanceProfile(){
+  translate([-lid_fold_clearance_antislop, 0])
+    polygon([ lpp10,
+             lpp11,
+             lpp11 + [-20,  0],
+             lpp11 + [-20, 20],
+             lpp11 + [+20, 20],
+             lpp10 + [+20,  0] ]);
+}
+
+module ButtonCoverProfile(){
+  intersection(){
+    polygon(concat([ bppM, bppP, bppO, bppJ ],
+                  (enable_support && !$button_suppress_over_keeper
+                   ? [ bppU, bppV, bppW ] : []),
+                  [ bppL, bppK ]));
+    hull(){
+      EdgeProfile();
+      LidEdgeProfile();
+    }
+  }
+}
+
+module ButtonPlan(l, deep, cut){
+  epsilon =
+    (cut  ? 0 : lid_buttoncover_gap);
+
+  delta =
+    (deep ? lid_buttoncover_overlap : 0);
+
+  C = [0,0]; // by definition
+  T = [ 0, epp4[1] ];
+  G = T + [0,10];
+
+  B0 = C + [0,-1] * button_cutout_depth;
+  B1 = B0 + [0,1] * epsilon;
+
+  r0 = 0.5 * (T[1] - B0[1]);
+  A = [  -(l + button_l_fudge)/2 + r0, 0.5 * (T[1] + B0[1]) ];
+  H = A + [0,-1] * delta;
+
+  D = A + [-2,0] * r0;
+  F = D + [0,10];
+
+  E0 = 0.5 * (D + A);
+  E1 = E0 + [1,0] * epsilon;
+
+  I0 = [ E0[0], H[1] ];
+  I1 = [ E1[0], H[1] ];
+
+  hull(){
+    for (m=[0,1]) mirror([m,0])
+      circleat(H, r0 - epsilon);
+  }
+  for (m=[0,1]) mirror([m,0]) {
+    difference(){
+      polygon([ E1,
+               I1,
+               H,
+               B1,
+               G,
+               F,
+               D
+               ]);
+      circleat(D, r0 + epsilon);
+    }
+  }
+}
+
+module ButtonCoverReinf(){ ////toplevel
+  minkowski(){
+    rotate([90,0,0])
+      linear_extrude(height=0.01)
+      intersection(){
+        ButtonCoverProfile();
+       translate([bppJ[0] + 0.1, -50]) mirror([1,0])
+         square([100,100]);
+    }
+    mirror([0,0,1]) linear_extrude(height=0.01) intersection(){
+      circle(r= lid_buttoncover_reinf);
+      translate([-20,0]) square(40, center=true);
+    }
+  }
+}
+
+module ThumbRecessCutProfile(){
+  difference(){
+    polygon([ cppA + [-10,0],
+             cppB + [-10,0],
+             cppB,
+             cppA ]);
+    circleat(epp1, r=case_th_side);
+  }
+}
+
+module Flip_rhs(yn=[0,1]) {
+  for ($rhsflip=yn) {
+    translate([phone_width/2, 0, 0])
+      mirror([$rhsflip,0,0])
+      translate([-phone_width/2, 0, 0])
+      children();
+  }
+}
+
+module Flip_bot(yn=[0,1]) {
+  for ($botflip=yn) {
+    translate([0, -phone_height/2, 0])
+      mirror([0, $botflip, 0])
+      translate([0, phone_height/2, 0])
+      children();
+  }
+}  
+
+module AroundEdges(fill_zstart, fill_th, fill_downwards=0){
+  // sides
+  Flip_rhs(){
+    translate([0, -phone_cnr_rad, 0])
+      rotate([90,0,0])
+      linear_extrude(height = phone_height - phone_cnr_rad*2)
+      children(0);
+  }
+  // corners
+  Flip_rhs() Flip_bot() {
+    translate([+1,-1] * phone_cnr_rad)
+      intersection(){
+       rotate_extrude()
+         intersection(){
+           mirror([1,0,0])
+             translate([-1,0] * phone_cnr_rad)
+             children(0);
+           rectfromto([0,-20],[10,20]);
+         }
+       translate([-10, 0, -20] + 0.01 * [+1,-1, 0] )
+         cube([10,10,40]);
+      }
+  }
+  // top and bottom
+  Flip_bot(){
+    translate([ phone_width - phone_cnr_rad, 0,0 ])
+      rotate([90,0,-90])
+      linear_extrude(height = phone_width - phone_cnr_rad*2)
+      children(0);
+  }
+  // fill
+  translate([0,0, fill_zstart])
+    mirror([0,0, fill_downwards])
+    linear_extrude(height = fill_th)
+    rectfromto([+1,-1] * phone_cnr_rad,
+              [phone_width, -phone_height] + [-1,+1] * phone_cnr_rad);
+}
+
+module CaseAperture(pos, dia, $fn) {
+  theta = 180/$fn;
+  translate([ pos[0] + bumper[0],
+             -epp2i[0],
+             -pos[1] ])
+    rotate([-90, theta, 0])
+    cylinder(r = dia/2 / cos(theta),
+            h = 60);
+}
+
+module SideButton(y, y_ref_sign, l, suppress_over_keeper=0){
+  // y_ref_sign:
+  //   +1  measured from top    of actual phone to top    of button
+  //   -1  measured from bottom of actual phone to bottom of button
+  //    0  y is centre of button in coordinate system
+  $button_l= l;
+  $button_suppress_over_keeper= suppress_over_keeper;
+  eff_y = y_ref_sign > 0 ?         -bumper [1] -y -l/2 :
+         y_ref_sign < 0 ? (-phone -bumper)[1] +y +l/2 :
+         y;
+  //echo(eff_y);
+  translate([0, eff_y, 0])
+    children();
+}
+
+module LidButtonishLeg(y, y_ref_sign, l=buttonishleg_default_l_is_fudge) {
+  $button_leg_only = true;
+  SideButton(y, y_ref_sign, l) children();
+}
+
+module Buttons(){
+  Flip_rhs(1) SideButton(15.580, +1, 8.9     ) children(); // power
+  Flip_rhs(1) SideButton(48.700, -1, 8.920   ) children(); // camera
+  Flip_rhs(0) SideButton(30.800, +1, 21.96, 1) children(); // volume
+  Flip_rhs(   ) LidButtonishLeg(14, -1) children();
+//  Flip_rhs(0) LidButtonishLeg(20, +1, 20) children();
+}
+
+module Struts(x_start, z_min, th){
+  // if th is negative, starts at z_min and works towards -ve z
+  // and object should then be printed other way up
+  for (i= [1 : 1 : case_struts_count]) {
+    translate([0,
+              0,
+              z_min])
+      mirror([0,0, th<0 ? 1 : 0])
+      translate([0,
+                -phone_height * i / (case_struts_count+1),
+                case_struts_solid_below])
+      linear_extrude(height= abs(th)
+                    -(case_struts_solid_below+case_struts_solid_above))
+      rectfromto([               x_start, -0.5 * case_struts_width ],
+                [ phone_width - x_start, +0.5 * case_struts_width ]);
+  }
+}
+
+module OrdinaryRearAperture(rhs,bot, pos){
+  Flip_rhs(rhs) Flip_bot(bot)
+    linextr(-20, 20)
+    mirror([0,1])
+    translate(pos + bumper)
+    children();
+}
+
+module MicroUSB(){
+  Flip_bot(1){
+    rotate([90,0,0])
+      mirror([0,0,1])
+      linextr(-epp2i[0], 60)
+      translate([0.5 * phone_width, 0, 0])
+      rectfromto([-microusb_width/2, epp2i[1] + microusb_below],
+                [+microusb_width/2, epp0[1] + -microusb_above]);
+  }
+}
+
+module OrdinaryRearApertures(){
+  // rear speaker
+  OrdinaryRearAperture(1,1, rearspeaker_pos_bl)
+    rectfromto(-rearspeaker_gap,
+              rearspeaker_size + rearspeaker_gap);
+
+  // finger hole to remove phone
+  if (len(fingerpushhole_dias))
+    OrdinaryRearAperture(1,0, [ fingerpushhole_dias[0]/2 + epp2i[0],
+                               phone[1]/2 ])
+    scale(fingerpushhole_dias)
+    circle(r= 0.5 );
+}
+
+module RearCameraAperture(){
+  Flip_rhs(1)
+    mirror([0, 0, 1])
+    linear_extrude(height = 20)
+    mirror([0, 1, 0])
+    translate(bumper)
+    rectfromto(camera_pos_tl, camera_pos_br);
+}
+
+module HingeLidProfile(){
+  hull(){
+    circleat(hppT, hp_r1);
+    circleat(lpp12, lp_r12);
+    polygon([lpp10,
+            lpp13 + [2,0],
+            lpp12,
+            hppT]);
+  }
+}
+
+module HingeBaseProfile(){
+  difference(){
+    hull(){
+      circleat(hppB, hp_r1);
+      circleat(hppE, hp_r1);
+      circleat(epp2o, case_th_bottom);
+      circleat(hppB + [10,0], hp_r1);
+    }
+    polygon([epp5, epp1, epp2i, epp3, bppL]);
+  }
+}
+
+module HingeLeverOuterProfile(){
+  hull(){
+    circleat(hppT, hp_r2);
+    circleat(hppB, hp_r2);
+  }
+}
+
+module HingeLeverInnerProfile(){
+  for (s = [-1,+1]) {
+    c = s > 0 ? hppT : hppB;
+    translate(c)
+      mirror([0,0,s>0])
+      rotate(s<0 ? -40 : 0)
+      hull()
+      for (x=[-20,20])
+       for (y=[0, s * 10])
+         translate([x,y])
+           circle(hp_rn);
+  }
+}
+
+module HingeLeverNutProfile(){
+  for (c= [hppB, hppT]) {
+    translate(c)
+      circle($fn=6, r= 0.5 * hingescrew_nut_across / cos(30));
+  }
+}
+
+module Flip_hinge(doflip=1){
+  hinge_origin = [0, -(phone_height - hppB[0]), hppB[1]];
+  translate(hinge_origin)
+    rotate([doflip*180,0,0])
+    translate(-hinge_origin)
+    children();
+}
+
+module HingePortion(x0,x1){
+  Flip_rhs() Flip_bot(1)
+    translate([x0,0,0])
+    mirror([1,0,0])
+    rotate([90,0,-90])
+    linear_extrude(height=x1-x0)
+    children();
+}
+
+module ThumbRecessApply(ztop){
+  width = thumbrecess_width;
+  w = width + thumbrecess_topcurve_r*2 + 1;
+  translate([phone_width/2, 0,0]){
+    difference(){
+      rotate([90,0,-90])
+       linextr(-w/2, w/2)
+       children(0);
+      translate([0, 50, 0])
+       rotate([90,0,0])
+       linear_extrude(height=100){
+       for (m=[0,1]) mirror([m,0,0]) {
+         hull(){
+           translate([w/2, ztop - thumbrecess_topcurve_r])
+             circle(thumbrecess_topcurve_r);
+           translate([w/2, -50])
+             square(thumbrecess_topcurve_r*2, center=true);
+         }
+       }
+      }
+    }
+  }
+}
+
+module CaseBase(){
+  AroundEdges(epp3[1], case_th_bottom, 1)
+    EdgeProfile();
+}
+
+function prop_x(gamma) = hp_k / (2 * sin(gamma/2)) - hppT[0];
+
+module PropProfileAssignments(gamma){
+  // https://en.wikipedia.org/wiki/Solution_of_triangles#Two_sides_and_the_included_angle_given_(SAS)
+  x = prop_x(gamma);
+  p = phone_height + prlp10[0] - hppB[0];
+  b = p + x;
+
+  q = phone_height - hppT[0] - prcp1[0]; // $prpp7[0] is 0 by definition
+  a = q + x;
+  c = sqrt(a*a + b*b - 2*a*b*cos(gamma));
+  $prp_alpha = acos( (b*b + c*c - a*a) / (2*b*c) );
+
+  $prp_theta = 90 - $prp_alpha;
+  beta = 180 - $prp_alpha - gamma;
+  psi = 90 - beta;
+
+  //echo("abc", a,b,c);
+
+  v1 = [ [ cos(psi), -sin(psi) ],    // x
+        [ sin(psi),  cos(psi) ] ];  // y
+
+  $prpp7 = [0, c + (lpp13[1] - $prpp10[1] - hp_k) ];
+
+  $prp_r1 = prc_r1;
+  $prp_r11 = prop_main_th/2;
+
+  $prpp1 = $prpp7 + [1,0] *
+    // this is approximate, but will do
+    (prop_main_th/2 + prop_prop_gap + prcp1[0] - cppA[0]);
+  $prpp3 = $prpp1 +
+    v1[0] * -$prp_r1 +
+    v1[1] * ((prcp2[1] - prcp1[1]) - prop_prop_gap);
+  $prpp12 = $prpp3 + v1[0] *
+    (prop_end_dia + prop_caserecess_taper * ($prpp1[1] - $prpp3[1]));
+  $prp_r8 = prop_main_th;
+  $prpp4 = [ prop_main_th/2, $prpp3[1] ];
+  $prp_r5 = $prp_r8;
+  $prpp5 = [ $prpp12[0] - $prp_r5,
+           $prpp3[1] - prop_prong_h + $prp_r5 ];
+  $prpp6 = $prpp4 + [0,-1] * (prop_prong_h +
+         prop_prong_heel_slope * ($prpp5[0] - $prpp4[0]));
+  $prpp8 = $prpp4 + [0,-1] * $prp_r8;
+  $prpp9 = $prpp8 + [-1,0] * $prp_r8;
+
+  children();
+}
+
+module PropProfile(gamma, cut=0, rot=0){
+  PropProfileAssignments(gamma){
+
+    //#circleat($prpp3,1);
+    //#circleat($prpp12,1);
+
+    if (!cut) {
+      hull(){
+       translate($prpp8)
+         intersection(){
+           circle($prp_r8);
+           polygon([[-20,-0], [20,20], [0,0]]);
+         }
+       rectfromto($prpp6, $prpp9);
+       translate($prpp5) intersection(){
+         circle($prp_r5);
+         polygon([[-10,-10], [0,0], [10,0]]);
+       }
+       rectfromto($prpp12 + [0,-0.1], $prpp3);
+      }
+      hull(){
+       circleat($prpp1, $prp_r1);
+       rectfromto($prpp12 + [0,-0.1], $prpp3);
+      }
+    }
+    // main shaft
+    rotate([0,0, rot*-$prp_theta]){
+      hull(){
+       extra = cut ? prop_recess_slop : 0;
+       rectfromto($prpp6, $prpp9);
+       circleat($prpp11, $prp_r11 + extra);
+       circleat($prpp10, $prp_r10 + extra);
+      }
+    }
+  }
+}
+
+module PropAggregateProfile(){
+  for (angle = prop_angles)
+    PropProfile(angle, 0,0);
+}
+
+module Prop(){ ////toplevel
+  hw = prop_main_width/2;
+  linextr(-hw, +hw)
+    PropAggregateProfile();
+}
+
+module Case(){ ////toplevel
+  difference(){
+    union(){
+      CaseBase();
+
+      // ledge (fixed keeper)
+      Flip_rhs(1-keeper_side) intersection(){
+       rotate([90, 0, 0])
+         linear_extrude(height = phone_height + phone_cnr_rad * 2)
+         KeeperProfile(1);
+
+       // outline of the whole case, to stop it protruding
+       translate([0,0, -25])
+         linear_extrude(height = 50)
+         hull()
+         Flip_bot()
+         circleat([+1,-1] * phone_cnr_rad, phone_cnr_rad + case_th_side/2);
+      }
+
+      // hinge
+      HingePortion(hex20, hex21) HingeBaseProfile();
+
+      // buildout for prop recess
+      if (prop_caserecess_buildout_r > 0) Flip_rhs(1)
+       linextr(case_bottom_z, epp2i[1])
+       hull() {
+         for (dxs = [-1,+1])
+           circleat([ prop_x_pos + dxs * prop_caserecess_buildout_r,
+                      -epp2o[0] ],
+                    r = epp2o[0] - prcp2[0]);
+        }
+    }
+
+    // slot for keeper
+    Flip_rhs(keeper_side)
+      translate([0, -phone_cnr_rad, 0])
+      rotate([90, 0, 0])
+      linear_extrude(height = phone_height + phone_cnr_rad * 2)
+      minkowski(){
+        KeeperProfile();
+       rectfromto([ -keeper_gap_x,    -keeper_gap_z_bot ],
+                  [ keeper_gap_x_holes,    +keeper_gap_z_top ]);
+      }
+
+    // front camera
+    RearCameraAperture();
+
+    // struts (invisible, because they're buried in the case)
+    Struts(epp2i[0], epp2i[1] - case_th_bottom, case_th_bottom);
+
+    Buttons(){
+      mirror([1,0,0])
+       rotate([90,0,90]) {
+         if (!($button_leg_only && enable_support))
+         intersection(){
+           translate([0,0,-10])
+             linear_extrude(height= 20)
+             ButtonPlan($button_l, 0,1);
+           if ($button_leg_only)
+             rotate([-90,90,0])
+               translate([phone_width/2, -400, kppe[1]])
+               mirror([1-abs($rhsflip - keeper_side),0,0])
+               cube([400, 800, 50]);
+           if (enable_support && !$button_suppress_over_keeper)
+             rotate([-90,90,0])
+             translate([-400, -400, kppd[1]])
+               mirror([0,0,1])
+               cube([800,800,100]);
+         }
+         translate([0,0, -bppR[0]])
+           linear_extrude(height= 20)
+           ButtonPlan($button_l, 1,1);
+        }
+      
+    }
+
+    // apertures along top edge
+    if (!$suppress_forward_holes) {
+      CaseAperture(jack_pos, jack_dia, 8);
+      Flip_rhs(1)
+       CaseAperture(noisecancelmic_pos, noisecancelmic_dia, 8);
+    }
+
+    OrdinaryRearApertures();
+
+    MicroUSB();
+
+    // gaps for the lid's hinge arms
+    HingePortion(hex20 - hinge_x_arms_gap,
+                hex21 + hinge_x_arms_gap)
+      minkowski(){
+        HingeLidProfile();
+       circle(r= hinge_r_arms_gap, $fn= 8);
+      }
+
+    // screw holes in the hinge arms
+    HingeScrews();
+
+    // thumb recess
+    ThumbRecessApply(epp4[1])
+      ThumbRecessCutProfile();
+
+    // lanyard
+    Flip_bot(1)
+      translate([ly_o[0], -(phone_cnr_rad + ly_re), ly_o[1]])
+      rotate([0, ly_theta, 0])
+      rotate([0,0,90])
+      LanyardCutout(lanyard_channel_len);
+
+    // prop recess
+    Flip_rhs(1)
+      translate([prop_x_pos,0,0])
+      mirror([0,1,0])
+      rotate([90,0,90])
+      linextr(-prop_recess_hw, +prop_recess_hw)
+      hull(){
+        for (d=[ [0,0], [0,-1], [+1,-1/prop_caserecess_taper] ])
+         circleat(prcp1 + 20*d,
+                  prc_r3);
+      }
+  }
+}
+
+module LidAdhocMultiprintFrame(phase){
+  if (led_window_style >= 3) {
+    AdhocMultiprintFrame(phase, lpp13[1], -1);
+  }
+}
+
+module LidAroundEdges(){
+  AroundEdges(lpp15[1], lpp13[1] - lpp15[1], 0)
+    children();
+}
+
+module Lid(){ ////toplevel
+  skew_centre = [0, lpp11[0], lpp11[1]];
+  difference(){
+    union(){
+      intersection(){
+       LidAroundEdges()
+         LidEdgeProfile();
+
+       translate(skew_centre)
+         multmatrix([[ 1, 0, 0, 0 ],
+                     [ 0, 1, -lid_fold_clearance_skew, 0 ],
+                     [ 0, 0, 1, 0 ],
+                     [ 0, 0, 0, 1 ]])
+         translate(-skew_centre)
+         LidAroundEdges()
+         LidEdgeFoldClearanceProfile();
+      }
+
+      // button covers
+      Buttons(){
+       intersection(){
+         rotate([90,0,90])
+           translate([0,0,-10])
+           linear_extrude(height= 20)
+           ButtonPlan($button_l, 1,0);
+         union(){
+           rotate([90,0,0])
+             translate([0,0,-100])
+             linear_extrude(height= 200)
+             ButtonCoverProfile();
+           hull()
+             for (y= [-1,+1] * (($button_l + button_l_fudge)/2
+                                - lid_buttoncover_reinf))
+               translate([0,y,0])
+                 ButtonCoverReinf();
+         }
+       }
+      }
+
+      // hinge arms
+      HingePortion(hex20, hex21) {
+       LidEdgeProfile();
+       HingeLidProfile();
+      }
+    }
+    Struts(lpp10[0] + strut_min_at_end, lpp13[1], -case_th_lid);
+
+    // screw holes in the hinge arms
+    HingeScrews();
+
+    // prop recess
+    translate([prop_x_pos, -prlp10[0], prlp10[1]])
+      mirror([0,1,0])
+      rotate([90,0,90])
+      linextr(-prop_recess_hw, +prop_recess_hw)
+      hull()
+      for (pa = prop_angles)
+       PropProfile(pa, 1,1);
+
+    // notification led aperture
+    if (led_window_style)
+      translate([led_pos[0], -led_pos[1], lpp13[1]]) {
+       translate([0,0,-10])
+         cylinder(r=nla_r0, h=20);
+       if (led_window_style >= 2)
+         translate([0,0, -nla_t])
+           cylinder(r=nla_r2, height=20);
+      }
+
+    }
+
+  LidAdhocMultiprintFrame(1);
+}
+
+module HingeLever(){ ////toplevel
+  difference() {
+    // outer body, positive
+    HingePortion(hex22, hex22 + phone_width/2)
+      HingeLeverOuterProfile();
+
+    // space for the screws
+    HingePortion(hex26, hex24)
+      HingeLeverInnerProfile();
+
+    // recesses for the nuts
+    HingePortion(hex23, hex26+1)
+      HingeLeverNutProfile();
+
+    // bores for the screws
+    HingeScrews();
+
+    // space for the charging cable
+    MicroUSB();
+    Flip_hinge() MicroUSB();
+  }
+}
+
+module LidWindow(){ ////toplevel
+  translate([led_pos[0], -led_pos[1], lpp13[1]])
+    mirror([0,0,1])
+    cylinder(r= nla_r1, h=nla_t);
+  LidAdhocMultiprintFrame(0);
+}
+
+module LidWindowPrint(){ ////toplevel
+  rotate([0,180,0])
+    LidWindow();
+}
+
+module DemoLidWindowSelect(){
+  translate([led_pos[0], led_pos[1], -100]) {
+    translate([0, -30, 0]) cube([400, 400, 200]);
+  }
+}
+
+module DemoLidWindow(){ ////toplevel
+  %Lid();
+  LidWindow();
+  translate([0,40,0]){
+    color("blue") intersection(){ Lid(); DemoLidWindowSelect(); }
+    color("red") intersection(){ LidWindow(); DemoLidWindowSelect(); }
+  }
+}
+
+module HingeLeverPrint(){ ////toplevel
+  rotate([-90,0,0])
+    translate([-phone_width/2, phone_height, 0])
+    HingeLever();
+}
+
+module TestSelectLength(){
+  translate([-30, -200, -20])
+    cube([30 + 15, 250, 40]);
+}
+
+module TestLength(){ ////toplevel
+  intersection(){
+    Case();
+    TestSelectLength();
+  }
+}
+
+module TestLengthRight(){ ////toplevel
+  intersection(){
+    Case();
+    Flip_rhs(1)
+      TestSelectLength();
+  }
+}
+
+module TestSelectWidth(){
+  translate([-30, -(phone_height - 25), -20])
+    mirror([0, 1, 0])
+    cube([200, 50, 40]);
+}
+
+module TestWidth(){ ////toplevel
+  intersection(){
+    Case();
+    TestSelectWidth();
+  }
+}
+
+module TestLidWidthPrint(){ ////toplevel
+  rotate([0,180.0]) intersection(){
+    Lid();
+    TestSelectWidth();
+  }
+}
+
+module TestSelectRearAperture(){
+  minkowski(){
+    union() children();
+    translate([20, 0,0])
+      cube([42, 2, 1], center=true);
+  }
+}
+
+module TestSelectCamera(){
+  minkowski(){
+    TestSelectRearAperture()
+      RearCameraAperture();
+    cube([0.1, 50, 0.1]);
+  }
+}
+
+module TestSelectOrdinaryRearApertures(){
+  TestSelectRearAperture()
+    OrdinaryRearApertures();
+}
+
+module TestCamera(){ ////toplevel
+  intersection(){
+    Case();
+    TestSelectCamera();
+  }
+}
+
+module TestLidByCamera(){ ////toplevel
+  intersection(){
+    Lid();
+    TestSelectCamera();
+  }
+}
+
+module TestLidByCameraPrint(){ ////toplevel
+  rotate([180,0,0]) TestLidByCamera();
+}
+
+module DemoByCamera(){ ////toplevel
+  color("blue") TestLidByCamera();
+  color("red")  TestCamera();
+}
+
+module OneKeeper(){ ////toplevel
+  translate([0, -phone_cnr_rad, 0])
+    rotate([90, 0, 0])
+    linear_extrude(height = phone_height - phone_cnr_rad * 2)
+    KeeperProfile();
+}
+
+module OneKeeperPrint(){ ////toplevel
+  rotate([0,180,0])
+    OneKeeper();
+}
+
+module LidPrint(){ ////toplevel
+  rotate([0,180,0])
+    Lid();
+}
+
+module TestSelectFrame(){
+  include = [1,-1] * (epp2i[0] + 4);
+
+  difference(){
+    cube(1000, center=true);
+    translate([0,0, -100])
+      linear_extrude(height=200)
+      rectfromto(include,  inside_br - include);
+  }
+}
+
+module TestSelectLidFrame(){
+  TestSelectFrame();
+  translate([led_pos[0], -led_pos[1], -50])
+    cylinder(r= nla_r2+3, h=100);
+}
+
+module TestFrameCase(){ ////toplevel
+  intersection(){
+    Case();
+    union(){
+      TestSelectFrame();
+      TestSelectCamera();
+      TestSelectOrdinaryRearApertures();
+    }
+  }
+}
+
+module TestSelectTopApertures(){
+  translate([-100, -35, -100])
+    cube([400, 100, 200]);
+  LidAdhocMultiprintFrame(0);
+  LidAdhocMultiprintFrame(1);
+}
+
+module TestTopApertures(){ ////toplevel
+  intersection(){
+    Case();
+    TestSelectFrame();
+    TestSelectTopApertures();
+  }
+}
+
+module TestLidTopAperturesPrint(){ ////toplevel
+  rotate([0,180,0]) intersection(){
+    Lid();
+    TestSelectLidFrame();
+    TestSelectTopApertures();
+  }
+}
+
+module TestLidWindowTopAperturesPrint(){ ////toplevel
+  rotate([0,180,0]) intersection(){
+    LidWindow();
+    TestSelectTopApertures();
+  }
+}
+
+module TestFrameLidPrint(){ ////toplevel
+  rotate([0,180,0]) intersection(){
+    Lid();
+    TestSelectLidFrame();
+  }
+}
+
+module ButtonPlanForDemo(z, deep, cut){
+  translate([0,0,z])
+    ButtonPlan(8, deep, cut);
+}
+
+module HingeScrews(){
+  Flip_rhs() Flip_bot(1){
+    for (c= [ hppT, hppB ])
+      translate([ hex20,
+                 -c[0],
+                 c[1] ]){
+       rotate([0,90,0])
+         translate([0,0,-.2])
+         cylinder( r= hingescrew_shaft_dia/2,
+                   h = hingescrew_shaft_len+0.2 );
+       rotate([0,-90,0])
+         translate([0,0,+.1])
+         cylinder( r= hingescrew_head_dia/2, h = hingescrew_head_th );
+      }
+  }
+}
+
+module DemoPropAngleSelect(c){
+  color(c) difference(){
+    union(){ children(); }
+    translate([ prop_x_pos, -400, -200 ])
+      cube([ 400,800,400 ]);
+  }
+}
+
+module DemoPropAngle(ang){
+  hL = [0, -(phone_height - hppT[0]), hppT[1] - hp_k*2];
+  hC = [0, -(phone_height - hppB[0]), hppB[1]];
+
+  translate(hL)
+    rotate([ang/2,0,0])
+    translate(-hL)
+    translate(hC)
+    rotate([ang/2,0,0])
+    translate(-hC) {
+      DemoPropAngleSelect("red") Case();
+
+      color("orange")
+       translate([prop_x_pos, -prcp1[0], prcp1[1]])
+       PropProfileAssignments(ang) {
+          echo($prpp1);
+         rotate([-$prp_theta, 0, 0])
+         translate([0, $prpp1[0], -$prpp1[1]])
+         rotate([90,0,-90])
+         Prop();
+        }
+    }
+
+  translate([0,0, -hp_k*2])
+    DemoPropAngleSelect("blue")
+    Lid();
+}
+
+module DemoPropAngles(){ ////toplevel
+  for (i=[0 : len(prop_angles)-1])
+    translate(i * [0, -100, 100])
+    DemoPropAngle(prop_angles[i]);
+}
+
+module DemoHingeAngle(ang1,ang2){
+  hL = [0, -(phone_height - hppT[0]), hppT[1]];
+  hC = [0, -(phone_height - hppB[0]), hppB[1]];
+
+  translate(hL)
+    rotate([ang2,0,0])
+    translate(-hL)
+    translate(hC)
+    rotate([ang1,0,0])
+    translate(-hC) {
+      color("red") Lid();
+    }
+
+  color("blue") intersection(){
+    Case();
+    union(){
+      translate([bppJ[0], -400, -200])
+       mirror([1,0,0])
+       cube([400, 800, 400]);
+      translate([10, -400, -200])
+       cube([10, 800, 400]);
+    }
+  }
+}
+
+module DemoHingeAngles(){ ////toplevel
+  angles = [ 0, 4, 8, 12 ];
+  echo("angles",angles);
+  for (i=[0 : len(angles)-1]) {
+    translate(i * [0, 0, 30]) {
+      DemoHingeAngle(0,angles[i]);
+      translate([0, 200, 0])
+       DemoHingeAngle(angles[i],0);
+    }
+  }
+}
+
+module DemoSelectAdhocLeftRight(right=0) {
+  translate([phone_width/2, -400, -100]) // , -15, -100  to cross-section
+    mirror([1-right, 0,0])
+    cube([400, 800, 200]);
+}
+
+module DemoLeft(){ ////toplevel
+  color("red")  intersection(){ Case(); DemoSelectAdhocLeftRight(); }
+  color("blue") intersection(){ Lid();  DemoSelectAdhocLeftRight(); }
+}
+
+module DemoFrame(){ ////toplevel
+  color("red") TestFrameCase();
+  color("blue") intersection(){ Lid(); TestSelectLidFrame(); }
+  color("black") HingeScrews();
+  %HingeLever();
+}
+
+module DemoLanyardCutout(){ ////toplevel
+  LanyardCutout(25);
+}
+
+module DemoHingedFrame(){ ///toplevel
+  color("red") TestFrameCase();
+  translate([0,0, -2*hp_k])
+  color("blue") intersection(){ Lid(); TestSelectLidFrame(); }
+
+  Flip_hinge(){
+    color("orange") HingeLever();
+    color("black") HingeScrews();
+  }
+}
+
+module DemoHinge(){ ////toplevel
+  translate([ -0.5*phone_width, phone_height, hp_k*3 ]) {
+    DemoFrame();
+    translate([0,0, -hp_k*3])
+      DemoHingedFrame();
+  }
+}
+
+module DemoProfiles(){ ////toplevel
+  LidEdgeProfile();
+  %EdgeProfile();
+  KeeperProfile();
+  translate([0,0,-1]) color("black") KeeperProfile(1);
+  translate(ly_o){
+    rotate(-ly_theta){
+      translate([0,0,+1]) color("purple") LanyardMainChannelProfile();
+      translate([0,0,+2]) color("red") LanyardCurveChannelProfile();
+      translate([0, ly_q_z]){
+       translate([0,0,-1]) color("blue") LanyardEntryChannelProfile();
+       translate([ly_oec_y,0,-2]) color("black") LanyardEntryOuterProfile();
+      }
+    }
+  }
+  translate([0,0,-5]) color("white") translate(epp2i)
+    rotate(-ly_theta)
+    rectfromto([-15, 0],
+              [+15, -max_case_bottom_edge_thickness]);
+
+  translate([0,20]) {
+    LanyardMainChannelProfile();
+    translate([0,0,1]) color("purple") LanyardCurveChannelProfile();
+    translate([0,0,-1]) color("red") LanyardEntryChannelProfile();
+  }
+
+  translate([20,0]) {
+    LidEdgeProfile();
+    %EdgeProfile();
+
+    demopoint_QR = [ bppS[0], bppQ[1] - 0.1];
+  
+    color("blue") ButtonCoverProfile();
+    color("red") {
+      rectfromto(bppQ, demopoint_QR);
+      rectfromto(bppR, demopoint_QR);
+    }
+  }
+
+  translate([-20,0]) {
+    color("black") ButtonPlanForDemo(-2, 0,1);
+    color("red" )  ButtonPlanForDemo(-4, 1,1);
+    color("blue")  ButtonPlanForDemo(-6, 1,0);
+  }
+
+  translate([0, -30]) {
+    %LidEdgeProfile();
+    %EdgeProfile();
+    color("blue") HingeLidProfile();
+    color("red")  HingeBaseProfile();
+    color("black") translate([0,0,-2]) HingeLeverOuterProfile();
+  }
+
+  for (f=[0,1]) {
+    translate([-30, -60 + 30*f]) {
+      translate([0,0,-4]) EdgeProfile();
+      %translate([0,0,-10]) HingeBaseProfile();
+      translate([0,-2] * f * hp_k) {
+       translate([0,0,-4]) LidEdgeProfile();
+       %translate([0,0,-10]) %HingeLidProfile();
+      }
+      translate(+hppB) rotate([0,0,180*f]) translate(-hppB) {
+       translate([0,0,-2]) color("black") HingeLeverOuterProfile(); 
+       translate([0,0,0]) color("red") difference(){
+         HingeLeverOuterProfile();
+         HingeLeverInnerProfile();
+       }
+       translate([0,0,3]) color("yellow") HingeLeverNutProfile();
+      }
+    }
+  }
+
+  translate([20,-30]) {
+    %EdgeProfile();
+    %LidEdgeProfile();
+    //translate([0,0,1]) ThumbRecessCutProfile();
+    translate([0,0,+1]) color("red")
+      difference(){ EdgeProfile(); ThumbRecessCutProfile(); }
+  }
+
+  translate([40,-30]) {
+    difference(){
+      LidEdgeProfile();
+      translate(prlp10)
+       PropProfile(10, 1, 0);
+    }
+    translate(prlp10)
+      PropProfile(15, 0);
+  }
+  translate([60,-30]) {
+    PropAggregateProfile();
+  }
+}
+
+//EdgeProfile();
+//KeeperProfile();
+//CaseBase();
+//%Case();
+//Keeper();
+//LidEdgeProfile();
+//KeeperProfile();
+//DemoProfiles();
+//PropRecess();
diff --git a/fairphone4-case-coarse.scad b/fairphone4-case-coarse.scad
new file mode 100644 (file)
index 0000000..e5ea9a1
--- /dev/null
@@ -0,0 +1,7 @@
+// -*- C -*-
+
+//// toplevels-from:
+include <fairphone4-case.scad>
+
+$fa = 20;
+$fs = 2;
diff --git a/fairphone4-case-mounted.scad b/fairphone4-case-mounted.scad
new file mode 100644 (file)
index 0000000..fecff5a
--- /dev/null
@@ -0,0 +1,15 @@
+// -*- C -*-
+
+include <bike-phone-mount.scad>
+
+module CaseMounted(){ ////toplevel
+  Case();
+  translate([ phone_width/2,
+             -phone_height/2, epp3[1] - case_th_bottom ])
+    Mount();
+}
+
+//// toplevels-from:
+include <fairphone4-case.scad>
+$suppress_forward_holes = true;
+$suppress_hinge = true;
diff --git a/fairphone4-case-tripod.scad b/fairphone4-case-tripod.scad
new file mode 100644 (file)
index 0000000..b244673
--- /dev/null
@@ -0,0 +1,37 @@
+// -*- C -*-
+
+include <camera-mount.scad>
+
+tr_cube_offset = 20;
+tr_cube_sz = [20, 20, 15];
+tr_around = 10;
+
+module Mount(){
+  translate([0,  - tr_cube_sz[1], 0])
+  difference(){
+    translate([0, tr_cube_sz[1]/2 - tr_cube_offset/2, tr_cube_sz[2]/2])
+      cube(tr_cube_sz + [0, tr_cube_offset, 0], center=true);
+    translate([0, tr_cube_sz[1]/2 - tr_cube_offset, 0])
+      rotate([180,0,0])
+      render() CameraMountThread(tr_cube_sz[2] + 1);
+  }
+}
+
+module CaseMounted(){ ////toplevel
+  difference(){
+    render() Case();
+    translate([ phone_width/2, -phone_height/2 ])
+      linextr(-50, 50)
+      square([phone_width, phone_height] - tr_around * 2 * [1,1],
+            center=true);
+  }
+  translate([ phone_width,
+             -phone_height + tr_cube_sz[0] * 0.7,
+             epp3[1] - case_th_bottom ])
+    rotate([0,0,90])
+    Mount();
+}
+
+//// toplevels-from:
+include <fairphone4-case.scad>
+$suppress_hinge = true;
diff --git a/fairphone4-case.scad b/fairphone4-case.scad
new file mode 100644 (file)
index 0000000..0a4b63d
--- /dev/null
@@ -0,0 +1,1801 @@
+// -*- C -*-
+
+// Hard case for Fairphone 2
+//
+//  Copyright 2018 Ian Jackson.  There is NO WARRANTY.
+//  See below for full licensing and disclaimer.
+//
+// Instructions
+//
+//  1. You will want to git clone this repository.
+//
+//  2. <deleted>
+//
+//  3. use "make" to generate the necessary files:
+//
+//     make -j8 fairphone-case.auto.scads `for f in   \
+//        HingeLeverPrint   \
+//        LidPrint          \
+//        OneKeeperPrint    \
+//        Case              \
+//     ; do echo fairphone-case,$f.auto.stl; done`
+//
+//  4. Print them.  Case and OneKeeperPrint should probably be
+//     the same colour.
+//
+//  5. Assemble the hinge.  After placing the parts in the appropirate
+//     relative placement:
+//
+//        Use long bit of wire to ensure holes are lined up and proper
+//        Cut four short bits of wire, using above as a guage
+//
+//        Push two short bits into two holes on same side
+//        Use long bit of wire to ensure properly in holes
+//        Keep that side up so they don't fall out!
+//
+//        For each of the two holes
+//          Use 20-30cm hunk of 2.85mm PLA
+//          Use gas flame to melt end until it catches fire (!)
+//          Remove from flame, wave to extinguish, and quickly:
+//          Dab end onto where hole is
+//          As it congeals, use sidecutters to cut off by hole
+//
+//        Repeat for two holes on other side
+//        When cool, file down rough edges
+//
+//  6. In use:
+// 
+//      - To put the phone in, drop its RH side into the RH side of
+//        the case.  Then feed the keeper through the small hole.
+//        Feed it right through.
+//
+//      - The optional prop can be used to prop the phone up (in
+//        portrait orientation only right now).  See
+//            openscad fairphone-case,DemoPropAngles.auto.scad
+//
+// Other phones
+//
+//  It might well be possible to adapt this file for other phones.
+//  If you do, let me know how you get on.
+//
+//
+// AUTHORSHIP, COPYRIGHT, LICENCE, AND LACK OF WARRANTY
+//
+//   Copyright (C) 2018-2022 Ian Jackson.
+//
+//    This program for generating a 3D model is free software: you can
+//    redistribute it and/or modify it under the terms of the GNU
+//    General Public License as published by the Free Software
+//    Foundation, either version 3 of the License, or (at your option)
+//    any later version.
+//
+//    This program is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public
+//    License along with this program.  If not, see
+//    <http://www.gnu.org/licenses/>.
+//
+//  In particular DO NOT BLAME ME IF THIS CASE DOES NOT ADEQUATELY
+//  PROTECT YOUR PHONE !  It is your responsibility to decide whether
+//  this case will meet your needs.
+
+include <utils.scad>
+include <funcs.scad>
+
+phone = [ 75.86, 162.0 ];
+
+prop_buildout_less = 3;
+
+prop_angles = [ 15, 30, 45, 60 ];
+
+bumper = [ 0.250, -0.025 ];
+// ^ One side.  Overall size is increased by twice this.
+// If no bumpers, is the gap around the phone.
+
+enable_support = 1;
+
+led_window_style = 0;
+// 0: no window
+// 1: simply an opening
+// 2: opening with separate cover model, for printing in clear (two colour)
+// 3: like 2 but one-layer window for ad-hoc multi-colour
+
+initial_layer_thick = 0.400; // ^ needed for mode 3 only
+initial_layer_width = 0.750; // ^ needed for mode 3 only
+multicolour_gap = 0.15; // each side
+
+phone_cnr_rad = 7.0; // actuall 8.mumble, but smaller is fine
+phone_rim_depth = 0.01; // includes allowance for a screen protector
+
+button_cutout_depth = 9;
+
+phone_edge_thick = 11.25;
+
+camera_pos_tl = [  5.600,  5.750 ]; // from tl corner (as seen from back)
+camera_edge_rad = 9.750 + 0.700;
+camera_sz = 32.920 + .750 + 1.000;
+
+// this is disabled, FP4 doesn't have one
+jack_pos = [ 13.83, 8.485 ];
+jack_dia = 10.64 + .5; // some jack I had lying around
+
+// this led stuff, is irrelevant, we have disabled it as it doesn't have one
+led_pos = []; // [ 13.98, 10.00 ];
+led_aperture = 9;
+led_window_ledge = 0.75; // each side
+
+noisecancelmic_pos = [ 15.08 + .720, 4.35 ];   // from rhs, from top edge
+noisecancelmic_dia = 4.00;
+
+mainmic_pos = [ 21.0, 4.65 ];   // from lhs, from top edge
+mainmic_dia = 4.00;
+
+lhshole_pos = [ phone[1]/2 + 0.40, 4.35 ];
+
+fingerpushhole_dias = [];
+//fingerpushhole_dias = [ 15, 18 ]; // this is for testing
+
+lanyard_half_dia = 1.15;
+lanyard_entry_rel_breadth = 2;
+lanyard_channel_len = 8;
+//rearspeaker_pos_bl = [ 12.64, 18.72 ];
+//rearspeaker_size   = [  3.76,  7.36 ];
+
+bottomspeaker_size = [ 11.35, 1.90 ] + [1,1] * 0.5;
+bottomspeaker_pos = [ 17.55, 5.17 ]; // from rhs, from top
+
+microusb_above = 1.64 - 0.25;
+microusb_below = 2.42;
+microusb_width = 12.16 + 2.0 + 1.25;
+
+case_th_bottom = 2.5;
+case_th_lid = 3.0;
+case_th_side = 2.6;
+case_th_lip = 1.2;
+
+lid_screen_gap_extra = .66;
+
+case_struts_count = 6;
+case_struts_solid_below = 1.00;
+case_struts_solid_above = 0.75;
+case_struts_width = 0.10;
+
+keeper_th_z = 0.75;
+keeper_th_x = 0.75;
+keeper_inner_width = 2.75;
+keeper_inner_height = 2.75;
+keeper_slant_slope = 2; // larger means steeper
+
+keeper_gap_z_top = 0.25;
+keeper_gap_z_bot = 0.75;
+keeper_gap_x     = 0.25;
+keeper_gap_x_holes = 0.75;
+keeper_fatter = 0.45;
+keeper_fatter_hole = 1.20;
+keeper_stubbier = 0.0;
+
+keeper_side = 0; // 0 = lhs; 1 = rhs
+
+case_lip = 1.25;
+
+lid_gap_x = 0.25;
+lid_gap_z = 0.25;
+lid_lip = 1.75;
+lid_edgepart_width = 5.0;
+lid_buttoncover_thick = 1.3;
+lid_buttoncover_reinf = 0.95;
+
+foldover_gap = 0.50;
+foldover_lever_gap = 0.50;
+
+// properties of the hinge fasteners
+hingescrew_shaft_dia = 1.600 + 0.45; // beading wire
+hingescrew_shaft_len = 10;
+hingescrew_fasteners_extra_thick = 0.40;
+// ^ amount of thread protruding if everything was completely nominal
+//   and we are using two nuts
+hingescrew_nut_access_dia = 4.72 + 0.50;
+// ^ washer is 4.72 dia
+//   also, want to get pliers or tiny spanner in to do up locknut
+hingescrew_nut_across = 3.92 + 0.25; // incl. slop around recess slop
+hingescrew_nut_thick = 1.93;
+hingescrew_head_th = 1.38 + 0.75;
+hingescrew_head_dia = 3.92;
+
+hingescrew_nut_recess_portion = 2/3; // portion of nut in recess
+
+lever_cover_th = 0.75;
+hingemount_th = 2.5;
+hingemount_wd = 4.8725;
+
+$fa = 5;
+$fs = 0.1;
+
+button_l_fudge = 4.4;
+buttonishleg_default_l_is_fudge = 10;
+
+hinge_base_slope = 1.5; // bigger is steeper
+
+strut_min_at_end = 1.5;
+
+hinge_x_gap = 0.125;
+hinge_x_postscrew_gap = 0.75;
+hinge_x_arms_gap = 0.35;
+hinge_r_arms_gap = 0.55;
+hinge_over_nut_plate = -0.50; // bodge apropos slope
+
+// there isn't one of these, speaker is by hinge
+// rearspeaker_gap    = [ 2.0, 2.0 ]; // each side
+
+thumbrecess_depth = 1.3;
+thumbrecess_width = 16.5;
+thumbrecess_topcurve_r = 5.0;
+
+prop_recess_under = 0.50;
+prop_recess_slop = 0.200; // each side
+prop_end_dia = 0.5;
+prop_main_th = 3;
+prop_taper_len = 6;
+prop_main_width = 4;
+prop_side_gap = 0.75; // each side
+prop_lidrecess_behind = 0.75;
+prop_caserecess_behind = 0.75;
+prop_caserecess_taper = 0.45; // one side only
+prop_prop_gap = 0.5;
+prop_prong_heel_slope = 0.5;
+
+lid_fold_clearance_antislop = 0.5;
+
+$button_leg_only = false;
+$suppress_forward_holes = false;
+$suppress_hinge = false;
+
+// ---------- calculated ----------
+
+phone_total_thick = phone_edge_thick;
+
+phone_width =  (phone + bumper*2)[0];
+phone_height = (phone + bumper*2)[1];
+
+inside_br = [phone_width, -phone_height];
+
+prop_prong_h = prop_main_th;
+
+//echo(camera_pos_tl + bumper,
+//     camera_pos_br + bumper);
+
+// ----- could be changed -----
+lid_buttoncover_gap = lid_gap_x;
+lid_buttoncover_overlap = case_th_lip + keeper_gap_z_top;
+
+//prop_lidrecess_depth = case_th_lid - prop_recess_under;
+
+//prop_nose_len = case_th_lid - prop_recess_under;
+//prop_recess_slope = tan(prop_max_angle); // bigger means steeper
+//prop_recess_width = prop_main_th / cos(prop_max_angle) + prop_backfwd_gap;
+
+
+epp0 = [0,0];
+epp1 = [0, -phone_edge_thick];
+epp2i = epp1; // conflated for FP4
+epp2o = epp2i;
+epp3 = epp2i + [10, 0];
+epp5 = epp0 + [0,1] * (keeper_th_z + keeper_gap_z_top + case_lip);
+epp4 = epp5 + [-1,0] * case_th_side;
+
+kppe = [0,0];
+kppd = kppe + [1,0] * keeper_inner_width;
+kppc = kppd + [0,1] * keeper_th_z;
+kppb = [ kppe[0] - keeper_th_x, kppc[1] ];
+kppf = kppe - [0,1] * keeper_inner_height;
+kppa = [ kppb[0], kppf[1] ];
+
+lpp10 = [ epp5[0] + lid_gap_x, kppc[1] + lid_gap_z ];
+lpp11 = [ lpp10[0],            epp5[1] + lid_gap_z ];
+
+lpp14 = lpp10 + [1,0] * max(keeper_inner_width, lid_edgepart_width);
+// exact x posn not very important; must extend past end of keeper
+
+lpp15 = [ lpp14[0],
+         epp0[1] - phone_rim_depth + 1/2.5 * case_th_lid
+         + lid_screen_gap_extra ];
+// ^ beam theory says to maximise force before contact,
+//   the gap below the `beam' (the lid) must be 1/3
+//   the thickness (ie the lid thickness) if the beam
+//   is solid, or 1/2 if it has a top and bottom only.
+//   ours is mostly solid.
+
+lp_r12 = max(case_th_lid - (lpp11[1] - lpp15[1]),
+            case_th_lip);
+
+lpp12 = [ epp4[0] + lp_r12,    lpp11[1] ];
+lpp13 = [ lpp12[0],            lpp12[1] + lp_r12 ];
+
+case_bottom_z = epp2o[1] - case_th_bottom;
+
+// button profile
+bppM = epp4 + [0,5];
+bppN = [ bppM[0] + lid_buttoncover_thick, bppM[1] ];
+bppR = [ bppN[0] + lid_buttoncover_gap, -button_cutout_depth ];
+bppS = [ epp1[0], bppR[1] ];
+bppQ = [ bppM[0], bppR[1] - lid_buttoncover_overlap ];
+bppP = bppQ + [0,1] * lid_buttoncover_gap;
+bppO = [ bppN[0], bppP[1] ];
+bppL = lpp10 + [5,0];
+bppK = [ bppL[0], bppN[1] ];
+bppJ = [ bppN[0], bppL[1] ];
+bppU = [ bppJ[0], lpp12[1] ];
+bppV = lpp11;
+bppW = lpp10;
+
+echo("BUTTON COVER TH", bppO[0] - bppP[0]);
+
+// notification led aperture
+
+nla_r0 = led_aperture/2;
+nla_r1 = nla_r0 + led_window_ledge;
+nla_r2 = nla_r1 + multicolour_gap;
+nla_t =
+  led_window_style >= 3 ? initial_layer_thick :
+  led_window_style >= 2 ? led_window_ledge : 0;
+
+
+// hinge plan
+hp_rn = hingescrew_nut_access_dia/2;
+hp_r2_min = hp_rn + lever_cover_th;
+hp_rs = hingescrew_shaft_dia/2;
+hp_r1_min = hp_rs + hingemount_th;
+
+hp_r1 = max(hp_r1_min, hp_r2_min);
+hp_r2 = hp_r1;
+
+hppU = lpp13;
+hppS = epp2o + [0,-1] * case_th_bottom;
+hp_k = 0.5 * (hppU[1] - hppS[1] + foldover_gap);
+
+hppM = [ epp4[0] - foldover_lever_gap - hp_r2,
+        0.5 * (hppU + hppS)[1] ];
+hppT = [ hppM[0], hppU[1] - hp_r1 ];
+hppB = hppT + [0,-1] * hp_k;
+
+hppE_y = epp2o[1] - case_th_bottom + hp_r1;
+hppE_x = hppB[0] + (hppB[1] - hppE_y) * hinge_base_slope;
+hppE = [ hppE_x, hppE_y ];
+
+// hinge elevation x coords
+
+hex20 = max(epp2o[0],
+           phone_cnr_rad,
+           kppd[0] + hingescrew_head_th + keeper_gap_x_holes);
+hex21 = hex20 + hingemount_wd;
+hex22 = hex21 + hinge_x_gap;
+hex27 = hex20 + hingescrew_shaft_len;
+hex24 = hex27 + hinge_x_postscrew_gap;
+hex23 = hex27 - (hingescrew_nut_thick*2
+                + hingescrew_fasteners_extra_thick);
+hex26 = hex23 + hingescrew_nut_thick * 2/3;
+
+//echo(hex20, hex21, hex22, hex23, hex24);
+////  6, 10.8725, 10.9975, 13.74, 18.75
+//module chk(act,exp) {
+//  if (abs(act-exp) > 1e-9) echo("WRONG", act, exp);
+//  else echo("ok", act);
+//}
+//chk(hex20, 6);
+//chk(hex21, 10.8725);
+//chk(hex22, 10.9975);
+//chk(hex23, 13.74);
+//chk(hex24, 18.75);
+
+lid_fold_clearance_skew =
+  (lpp10[1] - hppB[1]) /
+  (lpp10[0] - hppB[0]);
+
+echo("SK",lid_fold_clearance_skew);
+
+// thumb recess (used to be "catch" hence cpp*
+
+cppA = epp4 + [thumbrecess_depth, 0];
+cppB = [ cppA[0], epp1[1] ];
+
+// lanyard
+
+ly_r = lanyard_half_dia / 2;
+ly_rc = ly_r * 2;
+
+ly_theta = 90;
+ly_o = epp2i + 3 * ly_r * [0,1];
+
+max_case_bottom_edge_thickness =
+  case_th_bottom;
+
+ly_q_z = -(ly_rc + ly_r);
+ly_re = max_case_bottom_edge_thickness - (-ly_q_z);
+
+ly_oec_y = lanyard_entry_rel_breadth * ly_r;
+
+// prop recess in case
+
+prop_x_pos = phone_width/2;
+
+prop_recess_hw = 0.5 * prop_main_width + prop_side_gap;
+
+prc_r1 = prop_end_dia/2;
+prc_r3 = prc_r1 + prop_recess_slop;
+
+prcp2 = [ epp4[0] + prop_buildout_less,
+         case_bottom_z ];
+
+prop_caserecess_buildout_r = -1; // prcp2[0] - epp2o[0];
+
+prcp1 = [ epp2o[0] + prc_r3 + prop_caserecess_behind,
+         epp2i[1] - prc_r3 - prop_recess_under];
+
+// prop recess in lid
+
+prl_r10 = prop_end_dia/2;
+prl_r10o = prl_r10 + prop_recess_slop;
+
+prlp10 = lpp10 + [1,1] * prl_r10o
+  + [1,0] * prop_lidrecess_behind
+  + [0,1] * prop_recess_under;
+
+// prop
+
+$prpp10 = [0,0];
+$prpp11 = [0, prop_taper_len];
+
+$prp_r10 = prl_r10;
+
+// ---------- modules ----------
+
+module AdhocMultiprintFrame(phase, z0, zs) {
+  // from z0 to z0 + zs*layer
+  extra = phase * (initial_layer_width + multicolour_gap) + 5;
+  xextra = extra + -epp4[0];
+  xrange = [ 0, phone_width ] + [-1,+1] * xextra;
+  yextra = extra + -epp4[0];
+  yrange = [ -phone_height + +hppB[0] - hp_r2, 0 ] + [-1,+1] * yextra;
+  p0 = [ xrange[0], yrange[0] ];
+  p1 = [ xrange[1], yrange[1] ];
+  echo(p0, p1);
+  translate([0,0, z0])
+    mirror([0,0, zs<0 ? 1 : 0])
+    linear_extrude(height= initial_layer_thick)
+    difference(){
+      rectfromto(p0 - [1,1] * initial_layer_width,
+                p1 + [1,1] * initial_layer_width);
+      rectfromto(p0, p1);
+    }
+}
+
+module KeeperProfile(fatter=0, slant=0, stubbier=0){
+  use_e = kppe + [0,-1] * slant * keeper_inner_width / keeper_slant_slope;
+  polygon([use_e + [+1,-1] * fatter,
+          kppd  + [ 0,-1] * fatter - stubbier * [1,0],
+          kppc                     - stubbier * [1,0],
+          kppb,
+          kppa                     + stubbier * [0,1],
+          kppf  + [+1, 0] * fatter + stubbier * [0,1]
+          ]);
+}
+
+module EdgeProfile(){
+  difference(){
+    hull(){
+      translate(epp3) square(case_th_bottom*2, center=true);
+      circleat(epp2o, r=case_th_bottom);
+      circleat(epp1, r=case_th_side);
+      rectfromto(epp0, epp4);
+    }
+    polygon([ epp5 + [0,10],
+             epp1,
+             epp3 + [10,0] ]);
+  }
+}
+
+module LanyardLanyardProfile(entry=false){
+  hull(){
+    for (xs=[-1,+1] * (entry ? lanyard_entry_rel_breadth : 1))
+      translate(xs * 0.5 * lanyard_half_dia * [1,0])
+       circle(r= lanyard_half_dia/2);
+  }
+}
+
+module LanyardCurveChannelProfile(){
+  translate([0, -ly_r])
+    LanyardLanyardProfile();
+}  
+
+module LanyardEntryChannelProfile(){
+  LanyardLanyardProfile(true);
+}  
+
+module LanyardMainChannelProfile(){
+  LanyardCurveChannelProfile();
+  difference(){
+    square(center=true, ly_r * [6, 2]);
+    for (xs=[-1,+1])
+      translate(ly_r * [3 * xs, -1])
+       circle(r = ly_r);
+  }
+}
+
+module LanyardEntryOuterProfile(){
+  circleat([ly_re + ly_r, 0], ly_re);
+}
+
+module LanyardEntry(){
+  q_z = ly_q_z;
+  oec_y = ly_oec_y;
+
+  d_x = -ly_rc;
+
+  translate([d_x, 0, q_z]) {
+    intersection(){
+      rotate([90,0,0])
+       rotate_extrude(convexity=10)
+       rotate(90)
+       translate([0, -q_z])
+       LanyardCurveChannelProfile();
+      translate([0,-10,0])
+       cube([20,20,20]);
+    }
+  }
+
+  mirror([0,0,1])
+    translate([0,0,-1])
+    linear_extrude(height=20)
+    rotate(-90)
+    LanyardEntryChannelProfile();
+
+  translate([0, ly_r*2, 0])
+    rotate([90,0,0])
+    linear_extrude(height = ly_r*4){
+    difference(){
+      rectfromto([d_x, q_z], [ly_r, 0]);
+      circleat([d_x, q_z], ly_rc);
+    }
+  }
+
+  translate([0,0,q_z]){
+    for (my=[0,1])
+      mirror([0,my,0]){
+       translate([0, oec_y, 0]){
+         difference(){
+           translate(ly_re * [-1,0,-2])
+             cube(ly_re * [2,1,2]);
+           rotate_extrude(convexity=10)
+             LanyardEntryOuterProfile();
+         }
+       }
+      }
+    difference(){
+      translate([-ly_re, -(oec_y + 0.01), -2*ly_re])
+       cube([ly_re*2, 2*(oec_y + 0.01), 2*ly_re]);
+      for (mx=[0,1])
+       mirror([mx,0,0])
+         rotate([90,0,0])
+         translate([0,0,-10])
+         linear_extrude(height=20)
+         LanyardEntryOuterProfile();
+    }
+  }
+}
+
+module LanyardCutout(l){
+  rotate([0,-90,0])
+    linear_extrude(height=l)
+    rotate(-90)
+    LanyardMainChannelProfile();
+
+  for (ee=[0,1]){
+    translate(ee * l * [-1,0])
+      mirror([ee,0,0])
+      LanyardEntry();
+  }
+}
+
+module LidEdgeProfile(){
+  polygon([ lpp10,
+           lpp11,
+           lpp12,
+           lpp13,
+           lpp13 + [10, 0],
+           lpp15 + [10, 0],
+           lpp15,
+           lpp14,
+           ]);
+  intersection(){
+    circleat(lpp12, r=lp_r12);
+    rectfromto( lpp12 + [-10,   0],
+               lpp12 + [+10, +10] );
+  }
+}
+
+module LidEdgeFoldClearanceProfile(){
+  translate([-lid_fold_clearance_antislop, 0])
+    polygon([ lpp10,
+             lpp11,
+             lpp11 + [-20,  0],
+             lpp11 + [-20, 20],
+             lpp11 + [+20, 20],
+             lpp10 + [+20,  0] ]);
+}
+
+module ButtonCoverProfile(){
+  intersection(){
+    polygon(concat([ bppM, bppP, bppO, bppJ ],
+                  (enable_support && !$button_suppress_over_keeper
+                   ? [ bppU, bppV, bppW ] : []),
+                  [ bppL, bppK ]));
+    hull(){
+      EdgeProfile();
+      LidEdgeProfile();
+    }
+  }
+}
+
+module ButtonPlan(l, deep, cut){
+  epsilon =
+    (cut  ? 0 : lid_buttoncover_gap);
+
+  delta =
+    (deep ? lid_buttoncover_overlap : 0);
+
+  C = [0,0]; // by definition
+  T = [ 0, epp4[1] ];
+  G = T + [0,10];
+
+  B0 = C + [0,-1] * button_cutout_depth;
+  B1 = B0 + [0,1] * epsilon;
+
+  r0 = 0.5 * (T[1] - B0[1]);
+  A = [  -(l + button_l_fudge)/2 + r0, 0.5 * (T[1] + B0[1]) ];
+  H = A + [0,-1] * delta;
+
+  D = A + [-2,0] * r0;
+  F = D + [0,10];
+
+  E0 = 0.5 * (D + A);
+  E1 = E0 + [1,0] * epsilon;
+
+  I0 = [ E0[0], H[1] ];
+  I1 = [ E1[0], H[1] ];
+
+  hull(){
+    for (m=[0,1]) mirror([m,0])
+      circleat(H, r0 - epsilon);
+  }
+  for (m=[0,1]) mirror([m,0]) {
+    difference(){
+      polygon([ E1,
+               I1,
+               H,
+               B1,
+               G,
+               F,
+               D
+               ]);
+      circleat(D, r0 + epsilon);
+    }
+  }
+}
+
+module ButtonCoverReinf(){ ////toplevel
+  minkowski(){
+    rotate([90,0,0])
+      linear_extrude(height=0.01)
+      intersection(){
+        ButtonCoverProfile();
+       translate([bppJ[0] + 0.1, -50]) mirror([1,0])
+         square([100,100]);
+    }
+    mirror([0,0,1]) linear_extrude(height=0.01) intersection(){
+      circle(r= lid_buttoncover_reinf);
+      translate([-20,0]) square(40, center=true);
+    }
+  }
+}
+
+module ThumbRecessCutProfile(){
+  difference(){
+    polygon([ cppA + [-10,0],
+             cppB + [-10,0],
+             cppB,
+             cppA ]);
+    circleat(epp1, r=case_th_side);
+  }
+}
+
+module Flip_rhs(yn=[0,1]) {
+  for ($rhsflip=yn) {
+    translate([phone_width/2, 0, 0])
+      mirror([$rhsflip,0,0])
+      translate([-phone_width/2, 0, 0])
+      children();
+  }
+}
+
+module Flip_bot(yn=[0,1]) {
+  for ($botflip=yn) {
+    translate([0, -phone_height/2, 0])
+      mirror([0, $botflip, 0])
+      translate([0, phone_height/2, 0])
+      children();
+  }
+}  
+
+module AroundEdges(fill_zstart, fill_th, fill_downwards=0){
+  // sides
+  Flip_rhs(){
+    translate([0, -phone_cnr_rad, 0])
+      rotate([90,0,0])
+      linear_extrude(height = phone_height - phone_cnr_rad*2)
+      children(0);
+  }
+  // corners
+  Flip_rhs() Flip_bot() {
+    translate([+1,-1] * phone_cnr_rad)
+      intersection(){
+       rotate_extrude()
+         intersection(){
+           mirror([1,0,0])
+             translate([-1,0] * phone_cnr_rad)
+             children(0);
+           rectfromto([0,-20],[10,20]);
+         }
+       translate([-10, 0, -20] + 0.01 * [+1,-1, 0] )
+         cube([10,10,40]);
+      }
+  }
+  // top and bottom
+  Flip_bot(){
+    translate([ phone_width - phone_cnr_rad, 0,0 ])
+      rotate([90,0,-90])
+      linear_extrude(height = phone_width - phone_cnr_rad*2)
+      children(0);
+  }
+  // fill
+  translate([0,0, fill_zstart])
+    mirror([0,0, fill_downwards])
+    linear_extrude(height = fill_th)
+    rectfromto([+1,-1] * phone_cnr_rad,
+              [phone_width, -phone_height] + [-1,+1] * phone_cnr_rad);
+}
+
+module CaseAperture(pos, dia, $fn, topbottom=0) {
+  theta = 180/$fn;
+  translate([ bumper[0],
+             -epp2i[0],
+              0 ])
+    rotate([0,0, 90*topbottom])
+    translate([ pos[0] * (topbottom>0 ? -1 : +1), 0, -pos[1] ])
+    rotate([-90, theta, 0])
+    cylinder(r = dia/2 / cos(theta),
+            h = 60);
+}
+
+module SideButton(y, y_ref_sign, l, suppress_over_keeper=0){
+  // y_ref_sign:
+  //   +1  measured from top    of actual phone to top    of button
+  //   -1  measured from bottom of actual phone to bottom of button
+  //    0  y is centre of button in coordinate system
+  $button_l= l;
+  $button_suppress_over_keeper= suppress_over_keeper;
+  eff_y = y_ref_sign > 0 ?         -bumper [1] -y -l/2 :
+         y_ref_sign < 0 ? (-phone -bumper)[1] +y +l/2 :
+         y;
+  //echo(eff_y);
+  translate([0, eff_y, 0])
+    children();
+}
+
+module LidButtonishLeg(y, y_ref_sign, l=buttonishleg_default_l_is_fudge) {
+  $button_leg_only = true;
+  SideButton(y, y_ref_sign, l) children();
+}
+
+module Buttons(){
+  Flip_rhs(1) SideButton(30.320, +1, 22.960  ) children(); // volume
+  Flip_rhs(1) SideButton(64.220, +1, 14.500  ) children(); // power
+  Flip_rhs(1) LidButtonishLeg(14, -1) children();
+  Flip_rhs(0) LidButtonishLeg(21, -1) children();
+  Flip_rhs(0) LidButtonishLeg(38, +1) children();
+  Flip_rhs(0) LidButtonishLeg(14, +1) children();
+}
+
+module Struts(x_start, z_min, th){
+  // if th is negative, starts at z_min and works towards -ve z
+  // and object should then be printed other way up
+  for (i= [1 : 1 : case_struts_count]) {
+    translate([0,
+              0,
+              z_min])
+      mirror([0,0, th<0 ? 1 : 0])
+      translate([0,
+                -phone_height * i / (case_struts_count+1),
+                case_struts_solid_below])
+      linear_extrude(height= abs(th)
+                    -(case_struts_solid_below+case_struts_solid_above))
+      rectfromto([               x_start, -0.5 * case_struts_width ],
+                [ phone_width - x_start, +0.5 * case_struts_width ]);
+  }
+}
+
+module OrdinaryRearAperture(rhs,bot, pos){
+  Flip_rhs(rhs) Flip_bot(bot)
+    linextr(-20, 20)
+    mirror([0,1])
+    translate(pos + bumper)
+    children();
+}
+
+module MicroUSBEtc(){
+  Flip_bot(1){
+    rotate([90,0,0])
+      mirror([0,0,1])
+      linextr(-epp2i[0], 60)
+      translate([0.5 * phone_width, 0, 0])
+      rectfromto([-microusb_width/2, epp2i[1] + microusb_below],
+                [+microusb_width/2, epp0[1] + -microusb_above]);
+  }
+}
+
+module OrdinaryBottomEdgeApertures(){
+  Flip_bot(1)
+    CaseAperture(mainmic_pos, mainmic_dia, 8);
+
+  Flip_bot(1) Flip_rhs(1) {
+    linextr_y_xz(-epp2i[0], 60)
+      hull()
+      for (x= [-1,+1]) {
+       translate([ -bottomspeaker_pos[0], -bottomspeaker_pos[1] ] +
+                 [ 0.5 * x * bottomspeaker_size[0] - bottomspeaker_size[1],
+                   0 ])
+         rotate(360/16)
+         circle(r = bottomspeaker_size[1], $fn = 8);
+      }
+  }
+}
+
+module OrdinaryRearApertures(){
+  // rear speaker
+  //  OrdinaryRearAperture(1,1, rearspeaker_pos_bl)
+  //    rectfromto(-rearspeaker_gap,
+  //          rearspeaker_size + rearspeaker_gap);
+}
+
+module NotInTestFrameRearApertures(){
+  // finger hole to remove phone
+  if (len(fingerpushhole_dias))
+    OrdinaryRearAperture(0,0, [ fingerpushhole_dias[0] + epp2i[0],
+                               phone[1]/2 ])
+    scale(fingerpushhole_dias)
+    circle(r= 0.5 );
+}
+
+module RearCameraAperture(){
+  Flip_rhs(1)
+    mirror([0, 0, 1])
+    translate([0,0,0])
+    hull() // there is some kind of bug if hull() is done in 2D here!
+    linear_extrude(height = 20)
+    mirror([0, 1, 0])
+    translate(bumper)
+    translate(camera_pos_tl)
+    for (xy = [ [0,0], [0,1], [1,0] ]) {
+      translate(
+         camera_edge_rad * [1,1] +
+         xy * (camera_sz - camera_edge_rad * 2)
+               )
+       circle(r = camera_edge_rad);
+    }
+}
+
+module HingeLidProfile(){
+  hull(){
+    circleat(hppT, hp_r1);
+    circleat(lpp12, lp_r12);
+    polygon([lpp10,
+            lpp13 + [2,0],
+            lpp12,
+            hppT]);
+  }
+}
+
+module HingeBaseProfile(){
+  difference(){
+    hull(){
+      circleat(hppB, hp_r1);
+      circleat(hppE, hp_r1);
+      circleat(epp2o, case_th_bottom);
+      circleat(hppB + [10,0], hp_r1);
+    }
+    polygon([epp5, epp1, epp3, bppL]);
+  }
+}
+
+module HingeLeverOuterProfile(){
+  hull(){
+    circleat(hppT, hp_r2);
+    circleat(hppB, hp_r2);
+  }
+}
+
+module HingeLeverInnerProfile(){
+  for (s = [-1,+1]) {
+    c = s > 0 ? hppT : hppB;
+    translate(c)
+      mirror([0,0, s>0 ? 1 : 0])
+      rotate(s<0 ? -40 : 0)
+      hull()
+      for (x=[-20,20])
+       for (y=[0, s * 10])
+         translate([x,y])
+           circle(hp_rn);
+  }
+}
+
+module HingeLeverNutProfile(){
+  for (c= [hppB, hppT]) {
+    translate(c)
+      circle($fn=6, r= 0.5 * hingescrew_nut_across / cos(30));
+  }
+}
+
+module Flip_hinge(doflip=1){
+  hinge_origin = [0, -(phone_height - hppB[0]), hppB[1]];
+  translate(hinge_origin)
+    rotate([doflip*180,0,0])
+    translate(-hinge_origin)
+    children();
+}
+
+module HingePortion(x0,x1){
+  Flip_rhs() Flip_bot(1)
+    translate([x0,0,0])
+    mirror([1,0,0])
+    rotate([90,0,-90])
+    linear_extrude(height=x1-x0)
+    children();
+}
+
+module ThumbRecessApply(ztop){
+  width = thumbrecess_width;
+  w = width + thumbrecess_topcurve_r*2 + 1;
+  translate([phone_width/2, 0,0]){
+    difference(){
+      rotate([90,0,-90])
+       linextr(-w/2, w/2)
+       children(0);
+      translate([0, 50, 0])
+       rotate([90,0,0])
+       linear_extrude(height=100){
+       for (m=[0,1]) mirror([m,0,0]) {
+         hull(){
+           translate([w/2, ztop - thumbrecess_topcurve_r])
+             circle(thumbrecess_topcurve_r);
+           translate([w/2, -50])
+             square(thumbrecess_topcurve_r*2, center=true);
+         }
+       }
+      }
+    }
+  }
+}
+
+module CaseBase(){
+  AroundEdges(epp3[1], case_th_bottom, 1)
+    EdgeProfile();
+}
+
+function prop_x(gamma) = hp_k / (2 * sin(gamma/2)) - hppT[0];
+
+module PropProfileAssignments(gamma){
+  // https://en.wikipedia.org/wiki/Solution_of_triangles#Two_sides_and_the_included_angle_given_(SAS)
+  x = prop_x(gamma);
+  p = phone_height + prlp10[0] - hppB[0];
+  b = p + x;
+
+  q = phone_height - hppT[0] - prcp1[0]; // $prpp7[0] is 0 by definition
+  a = q + x;
+  c = sqrt(a*a + b*b - 2*a*b*cos(gamma));
+  $prp_alpha = acos( (b*b + c*c - a*a) / (2*b*c) );
+
+  $prp_theta = 90 - $prp_alpha;
+  beta = 180 - $prp_alpha - gamma;
+  psi = 90 - beta;
+
+  //echo("abc", a,b,c);
+
+  v1 = [ [ cos(psi), -sin(psi) ],    // x
+        [ sin(psi),  cos(psi) ] ];  // y
+
+  $prpp7 = [0, c + (lpp13[1] - $prpp10[1] - hp_k) ];
+
+  $prp_r1 = prc_r1;
+  $prp_r11 = prop_main_th/2;
+
+  $prpp1 = $prpp7 + [1,0] *
+    // this is approximate, but will do
+    (prop_main_th/2 + prop_prop_gap + prcp1[0] - cppA[0]);
+  $prpp3 = $prpp1 +
+    v1[0] * -$prp_r1 +
+    v1[1] * ((prcp2[1] - prcp1[1]) - prop_prop_gap);
+  $prpp12 = $prpp3 + v1[0] *
+    (prop_end_dia + prop_caserecess_taper * ($prpp1[1] - $prpp3[1]));
+  $prp_r8 = prop_main_th;
+  $prpp4 = [ prop_main_th/2, $prpp3[1] ];
+  $prp_r5 = $prp_r8;
+  $prpp5 = [ $prpp12[0] - $prp_r5,
+           $prpp3[1] - prop_prong_h + $prp_r5 ];
+  $prpp6 = $prpp4 + [0,-1] * (prop_prong_h +
+         prop_prong_heel_slope * ($prpp5[0] - $prpp4[0]));
+  $prpp8 = $prpp4 + [0,-1] * $prp_r8;
+  $prpp9 = $prpp8 + [-1,0] * $prp_r8;
+
+  children();
+}
+
+module PropProfile(gamma, cut=0, rot=0){
+  PropProfileAssignments(gamma){
+
+    //#circleat($prpp3,1);
+    //#circleat($prpp12,1);
+
+    if (!cut) {
+      hull(){
+       translate($prpp8)
+         intersection(){
+           circle($prp_r8);
+           polygon([[-20,-0], [20,20], [0,0]]);
+         }
+       rectfromto($prpp6, $prpp9);
+       translate($prpp5) intersection(){
+         circle($prp_r5);
+         polygon([[-10,-10], [0,0], [10,0]]);
+       }
+       rectfromto($prpp12 + [0,-0.1], $prpp3);
+      }
+      hull(){
+       circleat($prpp1, $prp_r1);
+       rectfromto($prpp12 + [0,-0.1], $prpp3);
+      }
+    }
+    // main shaft
+    rotate([0,0, rot*-$prp_theta]){
+      hull(){
+       extra = cut ? prop_recess_slop : 0;
+       rectfromto($prpp6, $prpp9);
+       circleat($prpp11, $prp_r11 + extra);
+       circleat($prpp10, $prp_r10 + extra);
+      }
+    }
+  }
+}
+
+module PropAggregateProfile(){
+  for (angle = prop_angles)
+    PropProfile(angle, 0,0);
+}
+
+module Prop(){ ////toplevel
+  hw = prop_main_width/2;
+  linextr(-hw, +hw)
+    PropAggregateProfile();
+}
+
+module Case(){ ////toplevel
+  difference(){
+    union(){
+      CaseBase();
+
+      // ledge (fixed keeper)
+      Flip_rhs(1-keeper_side) intersection(){
+       rotate([90, 0, 0])
+         linear_extrude(height = phone_height + phone_cnr_rad * 2)
+         KeeperProfile(fatter=0, slant=1);
+
+       // outline of the whole case, to stop it protruding
+       translate([0,0, -25])
+         linear_extrude(height = 50)
+         hull()
+         Flip_bot()
+         circleat([+1,-1] * phone_cnr_rad, phone_cnr_rad + case_th_side/2);
+      }
+
+      // hinge
+      if (!$suppress_hinge)
+       HingePortion(hex20, hex21) HingeBaseProfile();
+
+      // buildout for prop recess
+      if (prop_caserecess_buildout_r > 0) Flip_rhs(1)
+       linextr(case_bottom_z, epp2i[1])
+       hull() {
+         for (dxs = [-1,+1])
+           circleat([ prop_x_pos + dxs * prop_caserecess_buildout_r,
+                      -epp2o[0] ],
+                    r = epp2o[0] - prcp2[0]);
+        }
+    }
+
+    // slot for keeper
+    Flip_rhs(keeper_side)
+      translate([0, -phone_cnr_rad, 0])
+      rotate([90, 0, 0])
+      linear_extrude(height = phone_height + phone_cnr_rad * 2)
+      minkowski(){
+        KeeperProfile(fatter=keeper_fatter_hole);
+       rectfromto([ -keeper_gap_x,    -keeper_gap_z_bot ],
+                  [ keeper_gap_x_holes,    +keeper_gap_z_top ]);
+      }
+
+    // front camera
+    RearCameraAperture();
+
+    // struts (invisible, because they're buried in the case)
+    Struts(epp2i[0], epp2i[1] - case_th_bottom, case_th_bottom);
+
+    Buttons(){
+      mirror([1,0,0])
+       rotate([90,0,90]) {
+         if (!($button_leg_only && enable_support))
+         intersection(){
+           translate([0,0,-10])
+             linear_extrude(height= 20)
+             ButtonPlan($button_l, 0,1);
+           if ($button_leg_only)
+             rotate([-90,90,0])
+               translate([phone_width/2, -400, kppe[1]])
+               mirror([1-abs($rhsflip - keeper_side),0,0])
+               cube([400, 800, 50]);
+           if (enable_support && !$button_suppress_over_keeper)
+             rotate([-90,90,0])
+             translate([-400, -400, kppd[1]])
+               mirror([0,0,1])
+               cube([800,800,100]);
+         }
+         translate([0,0, -bppR[0]])
+           linear_extrude(height= 20)
+           ButtonPlan($button_l, 1,1);
+        }
+      
+    }
+
+    // apertures along top edge
+    if (!$suppress_forward_holes) {
+      // CaseAperture(jack_pos, jack_dia, 8);
+      Flip_rhs(1)
+       CaseAperture(noisecancelmic_pos, noisecancelmic_dia, 8);
+    }
+    CaseAperture(lhshole_pos, noisecancelmic_dia, 8, 1);
+
+    OrdinaryBottomEdgeApertures();
+
+    OrdinaryRearApertures();
+    NotInTestFrameRearApertures();
+
+    MicroUSBEtc();
+
+    // gaps for the lid's hinge arms
+    if (!$suppress_hinge) {
+      HingePortion(hex20 - hinge_x_arms_gap,
+                  hex21 + hinge_x_arms_gap)
+       minkowski(){
+        HingeLidProfile();
+       circle(r= hinge_r_arms_gap, $fn= 8);
+      }
+
+      // screw holes in the hinge arms
+      HingeScrews();
+    }
+
+    // thumb recess
+    ThumbRecessApply(epp4[1])
+      ThumbRecessCutProfile();
+
+    // lanyard
+    Flip_bot(1)
+      translate([ly_o[0], -(phone_cnr_rad + ly_re), ly_o[1]])
+      rotate([0, ly_theta, 0])
+      rotate([0,0,90])
+      LanyardCutout(lanyard_channel_len);
+
+    // prop recess
+    Flip_rhs(1)
+      translate([prop_x_pos,0,0])
+      mirror([0,1,0])
+      rotate([90,0,90])
+      linextr(-prop_recess_hw, +prop_recess_hw)
+      hull(){
+        for (d=[ [0,0], [0,-1], [+1,-1/prop_caserecess_taper] ])
+         circleat(prcp1 + 20*d,
+                  prc_r3);
+      }
+  }
+}
+
+module LidAdhocMultiprintFrame(phase){
+  if (led_window_style >= 3) {
+    AdhocMultiprintFrame(phase, lpp13[1], -1);
+  }
+}
+
+module LidAroundEdges(){
+  AroundEdges(lpp15[1], lpp13[1] - lpp15[1], 0)
+    children();
+}
+
+module Lid(){ ////toplevel
+  skew_centre = [0, lpp11[0], lpp11[1]];
+  difference(){
+    union(){
+      intersection(){
+       LidAroundEdges()
+         LidEdgeProfile();
+
+       translate(skew_centre)
+         multmatrix([[ 1, 0, 0, 0 ],
+                     [ 0, 1, -lid_fold_clearance_skew, 0 ],
+                     [ 0, 0, 1, 0 ],
+                     [ 0, 0, 0, 1 ]])
+         translate(-skew_centre)
+         LidAroundEdges()
+         LidEdgeFoldClearanceProfile();
+      }
+
+      // button covers
+      Buttons(){
+       intersection(){
+         rotate([90,0,90])
+           translate([0,0,-10])
+           linear_extrude(height= 20)
+           ButtonPlan($button_l, 1,0);
+         union(){
+           rotate([90,0,0])
+             translate([0,0,-100])
+             linear_extrude(height= 200)
+             ButtonCoverProfile();
+           hull()
+             for (y= [-1,+1] * (($button_l + button_l_fudge)/2
+                                - lid_buttoncover_reinf))
+               translate([0,y,0])
+                 ButtonCoverReinf();
+         }
+       }
+      }
+
+      // hinge arms
+      HingePortion(hex20, hex21) {
+       LidEdgeProfile();
+       HingeLidProfile();
+      }
+    }
+    Struts(lpp10[0] + strut_min_at_end, lpp13[1], -case_th_lid);
+
+    // screw holes in the hinge arms
+    HingeScrews();
+
+    // prop recess
+    translate([prop_x_pos, -prlp10[0], prlp10[1]])
+      mirror([0,1,0])
+      rotate([90,0,90])
+      linextr(-prop_recess_hw, +prop_recess_hw)
+      hull()
+      for (pa = prop_angles)
+       PropProfile(pa, 1,1);
+
+    // notification led aperture
+    if (led_window_style)
+      translate([led_pos[0], -led_pos[1], lpp13[1]]) {
+       translate([0,0,-10])
+         cylinder(r=nla_r0, h=20);
+       if (led_window_style >= 2)
+         translate([0,0, -nla_t])
+           cylinder(r=nla_r2, height=20);
+      }
+
+    }
+
+  LidAdhocMultiprintFrame(1);
+}
+
+module HingeLever(){ ////toplevel
+  difference() {
+    // outer body, positive
+    HingePortion(hex22, hex22 + phone_width/2)
+      HingeLeverOuterProfile();
+
+    // space for the screws
+//    HingePortion(hex26, hex24)
+//      HingeLeverInnerProfile();
+
+    // recesses for the nuts
+//    HingePortion(hex23, hex26+1)
+//      HingeLeverNutProfile();
+
+    // bores for the screws
+    HingeScrews();
+
+    // space for the charging cable and speaker and micc apertures
+    hull() {
+      for (x = [-1,+1]) {
+       multmatrix([[ 1,0,
+
+                    x
+                    * ( (hex24 + hinge_over_nut_plate) -
+                        (phone_width/2 - microusb_width/2)
+                       )
+                    / ( (epp0[1] - microusb_above)
+                        -
+                        (hppB[1] - hp_r2) ),
+
+                    x * (epp0[1] - microusb_above)
+
+                     ],
+                   [ 0,1,0, 0 ],
+                   [ 0,0,1, 0 ]]) {
+         union(){
+           MicroUSBEtc();
+           Flip_hinge() MicroUSBEtc();
+         }
+       }
+      }
+    }
+  }
+}
+
+module LidWindow(){ ////toplevel
+  translate([led_pos[0], -led_pos[1], lpp13[1]])
+    mirror([0,0,1])
+    cylinder(r= nla_r1, h=nla_t);
+  LidAdhocMultiprintFrame(0);
+}
+
+module LidWindowPrint(){ ////toplevel
+  rotate([0,180,0])
+    LidWindow();
+}
+
+module DemoLidWindowSelect(){
+  translate([led_pos[0], led_pos[1], -100]) {
+    translate([0, -30, 0]) cube([400, 400, 200]);
+  }
+}
+
+module DemoLidWindow(){ ////toplevel
+  %Lid();
+  LidWindow();
+  translate([0,40,0]){
+    color("blue") intersection(){ Lid(); DemoLidWindowSelect(); }
+    color("red") intersection(){ LidWindow(); DemoLidWindowSelect(); }
+  }
+}
+
+module HingeLeverPrint(){ ////toplevel
+  rotate([-90,0,0])
+    translate([-phone_width/2, phone_height, 0])
+    HingeLever();
+}
+
+module TestSelectLength(){
+  translate([-30, -200, -20])
+    cube([30 + 15, 250, 40]);
+}
+
+module TestLength(){ ////toplevel
+  intersection(){
+    Case();
+    TestSelectLength();
+  }
+}
+
+module TestLengthRight(){ ////toplevel
+  intersection(){
+    Case();
+    Flip_rhs(1)
+      TestSelectLength();
+  }
+}
+
+module TestSelectWidth(){
+  translate([-30, -(phone_height - 25), -20])
+    mirror([0, 1, 0])
+    cube([200, 50, 40]);
+}
+
+module TestWidth(){ ////toplevel
+  intersection(){
+    Case();
+    TestSelectWidth();
+  }
+}
+
+module TestLidWidthPrint(){ ////toplevel
+  rotate([0,180.0]) intersection(){
+    Lid();
+    TestSelectWidth();
+  }
+}
+
+module TestSelectRearAperture(){
+  minkowski(){
+    union() children();
+    translate([20, 0,0])
+      cube([42, 2, 1], center=true);
+  }
+}
+
+module TestSelectCamera(){
+  minkowski(){
+    TestSelectRearAperture()
+      RearCameraAperture();
+    cube([0.1, 50, 0.1]);
+  }
+}
+
+module TestSelectOrdinaryRearApertures(){
+  TestSelectRearAperture()
+    OrdinaryRearApertures();
+}
+
+module TestCamera(){ ////toplevel
+  intersection(){
+    Case();
+    TestSelectCamera();
+  }
+}
+
+module TestLidByCamera(){ ////toplevel
+  intersection(){
+    Lid();
+    TestSelectCamera();
+  }
+}
+
+module TestLidByCameraPrint(){ ////toplevel
+  rotate([180,0,0]) TestLidByCamera();
+}
+
+module DemoByCamera(){ ////toplevel
+  color("blue") TestLidByCamera();
+  color("red")  TestCamera();
+}
+
+module OneKeeper(){ ////toplevel
+  translate([0, -phone_cnr_rad, 0])
+    rotate([90, 0, 0])
+    linear_extrude(height = phone_height - phone_cnr_rad * 2)
+    KeeperProfile(fatter=keeper_fatter, stubbier=keeper_stubbier);
+}
+
+module OneKeeperPrint(){ ////toplevel
+  rotate([0,180,0])
+    OneKeeper();
+}
+
+module LidPrint(){ ////toplevel
+  rotate([0,180,0])
+    Lid();
+}
+
+module TestSelectFrame(){
+  include = [1,-1] * (epp2i[0] + 4);
+
+  difference(){
+    cube(1000, center=true);
+    translate([0,0, -100])
+      linear_extrude(height=200)
+      rectfromto(include,  inside_br - include);
+  }
+
+  for (i= [1,2]) {
+    translate([ 0, -phone[1] * i/3, 0 ])
+      cube(center=true, [1000, 4, 100]);
+  }
+}
+
+module TestSelectLidFrame(){
+  TestSelectFrame();
+  if (len(led_pos))
+    translate([led_pos[0], -led_pos[1], -50])
+    cylinder(r= nla_r2+3, h=100);
+}
+
+module TestFrameCase(){ ////toplevel
+  intersection(){
+    Case();
+    union(){
+      TestSelectFrame();
+      TestSelectCamera();
+      TestSelectOrdinaryRearApertures();
+    }
+  }
+}
+
+module TestSelectTopApertures(){
+  translate([-100, -35, -100])
+    cube([400, 100, 200]);
+  LidAdhocMultiprintFrame(0);
+  LidAdhocMultiprintFrame(1);
+}
+
+module TestTopApertures(){ ////toplevel
+  intersection(){
+    Case();
+    TestSelectFrame();
+    TestSelectTopApertures();
+  }
+}
+
+module TestLidTopAperturesPrint(){ ////toplevel
+  rotate([0,180,0]) intersection(){
+    Lid();
+    TestSelectLidFrame();
+    TestSelectTopApertures();
+  }
+}
+
+module TestLidWindowTopAperturesPrint(){ ////toplevel
+  rotate([0,180,0]) intersection(){
+    LidWindow();
+    TestSelectTopApertures();
+  }
+}
+
+module TestFrameLidPrint(){ ////toplevel
+  rotate([0,180,0]) intersection(){
+    Lid();
+    TestSelectLidFrame();
+  }
+}
+
+module ButtonPlanForDemo(z, deep, cut){
+  translate([0,0,z])
+    ButtonPlan(8, deep, cut);
+}
+
+module HingeScrews(){
+  Flip_rhs() Flip_bot(1){
+    for (c= [ hppT, hppB ])
+      translate([ hex20,
+                 -c[0],
+                 c[1] ]){
+       rotate([0,90,0])
+         translate([0,0,-.2])
+         cylinder( r= hingescrew_shaft_dia/2,
+                   h = hingescrew_shaft_len+0.2 );
+       rotate([0,-90,0])
+         translate([0,0,+.1])
+         cylinder( r= hingescrew_head_dia/2, h = hingescrew_head_th );
+      }
+  }
+}
+
+module DemoPropAngleSelect(c){
+  color(c) difference(){
+    union(){ children(); }
+    translate([ prop_x_pos, -400, -200 ])
+      cube([ 400,800,400 ]);
+  }
+}
+
+module DemoPropAngle(ang){
+  hL = [0, -(phone_height - hppT[0]), hppT[1] - hp_k*2];
+  hC = [0, -(phone_height - hppB[0]), hppB[1]];
+
+  translate(hL)
+    rotate([ang/2,0,0])
+    translate(-hL)
+    translate(hC)
+    rotate([ang/2,0,0])
+    translate(-hC) {
+      DemoPropAngleSelect("red") Case();
+
+      color("orange")
+       translate([prop_x_pos, -prcp1[0], prcp1[1]])
+       PropProfileAssignments(ang) {
+          echo($prpp1);
+         rotate([-$prp_theta, 0, 0])
+         translate([0, $prpp1[0], -$prpp1[1]])
+         rotate([90,0,-90])
+         Prop();
+        }
+    }
+
+  translate([0,0, -hp_k*2])
+    DemoPropAngleSelect("blue")
+    Lid();
+}
+
+module DemoPropAngles(){ ////toplevel
+  for (i=[0 : len(prop_angles)-1])
+    translate(i * [0, -100, 100])
+    DemoPropAngle(prop_angles[i]);
+}
+
+module DemoHingeAngle(ang1,ang2){
+  hL = [0, -(phone_height - hppT[0]), hppT[1]];
+  hC = [0, -(phone_height - hppB[0]), hppB[1]];
+
+  translate(hL)
+    rotate([ang2,0,0])
+    translate(-hL)
+    translate(hC)
+    rotate([ang1,0,0])
+    translate(-hC) {
+      color("red") Lid();
+    }
+
+  color("blue") intersection(){
+    Case();
+    union(){
+      translate([bppJ[0], -400, -200])
+       mirror([1,0,0])
+       cube([400, 800, 400]);
+      translate([10, -400, -200])
+       cube([10, 800, 400]);
+    }
+  }
+}
+
+module DemoHingeAngles(){ ////toplevel
+  angles = [ 0, 4, 8, 12 ];
+  echo("angles",angles);
+  for (i=[0 : len(angles)-1]) {
+    translate(i * [0, 0, 30]) {
+      DemoHingeAngle(0,angles[i]);
+      translate([0, 200, 0])
+       DemoHingeAngle(angles[i],0);
+    }
+  }
+}
+
+module DemoSelectAdhocLeftRight(right=0) {
+  translate([phone_width/2, -400, -100]) // , -15, -100  to cross-section
+    mirror([1-right, 0,0])
+    cube([400, 800, 200]);
+}
+
+module DemoLeft(){ ////toplevel
+  color("red")  intersection(){ Case(); DemoSelectAdhocLeftRight(); }
+  color("blue") intersection(){ Lid();  DemoSelectAdhocLeftRight(); }
+}
+
+module DemoFrame(){ ////toplevel
+  color("red") render() TestFrameCase();
+  color("blue") render() intersection(){ Lid(); TestSelectLidFrame(); }
+  color("black") render() HingeScrews();
+  %render() HingeLever();
+}
+
+module DemoLanyardCutout(){ ////toplevel
+  LanyardCutout(25);
+}
+
+module DemoHingedFrame(){ ///toplevel
+  color("red") render() TestFrameCase();
+  translate([0,0, -2*hp_k])
+  color("blue") render() intersection(){ Lid(); TestSelectLidFrame(); }
+
+  Flip_hinge(){
+    color("orange") render() HingeLever();
+    color("black") render() HingeScrews();
+  }
+}
+
+module DemoHinge(){ ////toplevel
+  translate([ -0.5*phone_width, phone_height, hp_k*3 ]) {
+    DemoFrame();
+    translate([0,0, -hp_k*3])
+      DemoHingedFrame();
+  }
+}
+
+module DemoProfiles(){ ////toplevel
+  LidEdgeProfile();
+  %EdgeProfile();
+  KeeperProfile();
+  translate([0,0,-1]) color("black") KeeperProfile(1);
+  translate(ly_o){
+    rotate(-ly_theta){
+      translate([0,0,+1]) color("purple") LanyardMainChannelProfile();
+      translate([0,0,+2]) color("red") LanyardCurveChannelProfile();
+      translate([0, ly_q_z]){
+       translate([0,0,-1]) color("blue") LanyardEntryChannelProfile();
+       translate([ly_oec_y,0,-2]) color("black") LanyardEntryOuterProfile();
+      }
+    }
+  }
+  translate([0,0,-5]) color("white") translate(epp2i)
+    rotate(-ly_theta)
+    rectfromto([-15, 0],
+              [+15, -max_case_bottom_edge_thickness]);
+
+  translate([0,20]) {
+    LanyardMainChannelProfile();
+    translate([0,0,1]) color("purple") LanyardCurveChannelProfile();
+    translate([0,0,-1]) color("red") LanyardEntryChannelProfile();
+  }
+
+  translate([20,0]) {
+    LidEdgeProfile();
+    %EdgeProfile();
+
+    demopoint_QR = [ bppS[0], bppQ[1] - 0.1];
+  
+    color("blue") ButtonCoverProfile();
+    color("red") {
+      rectfromto(bppQ, demopoint_QR);
+      rectfromto(bppR, demopoint_QR);
+    }
+  }
+
+  translate([-20,0]) {
+    color("black") ButtonPlanForDemo(-2, 0,1);
+    color("red" )  ButtonPlanForDemo(-4, 1,1);
+    color("blue")  ButtonPlanForDemo(-6, 1,0);
+  }
+
+  translate([0, -30]) {
+    %LidEdgeProfile();
+    %EdgeProfile();
+    color("blue") HingeLidProfile();
+    color("red")  HingeBaseProfile();
+    color("black") translate([0,0,-2]) HingeLeverOuterProfile();
+  }
+
+  for (f=[0,1]) {
+    translate([-30, -60 + 30*f]) {
+      translate([0,0,-4]) EdgeProfile();
+      %translate([0,0,-10]) HingeBaseProfile();
+      translate([0,-2] * f * hp_k) {
+       translate([0,0,-4]) LidEdgeProfile();
+       %translate([0,0,-10]) %HingeLidProfile();
+      }
+      translate(+hppB) rotate([0,0,180*f]) translate(-hppB) {
+       translate([0,0,-2]) color("black") HingeLeverOuterProfile(); 
+       translate([0,0,0]) color("red") difference(){
+         HingeLeverOuterProfile();
+         HingeLeverInnerProfile();
+       }
+       translate([0,0,3]) color("yellow") HingeLeverNutProfile();
+      }
+    }
+  }
+
+  translate([20,-30]) {
+    %EdgeProfile();
+    %LidEdgeProfile();
+    //translate([0,0,1]) ThumbRecessCutProfile();
+    translate([0,0,+1]) color("red")
+      difference(){ EdgeProfile(); ThumbRecessCutProfile(); }
+  }
+
+  translate([40,-30]) {
+    difference(){
+      LidEdgeProfile();
+      translate(prlp10)
+       PropProfile(10, 1, 0);
+    }
+    translate(prlp10)
+      PropProfile(15, 0);
+  }
+  translate([60,-30]) {
+    PropAggregateProfile();
+  }
+}
+
+//EdgeProfile();
+//KeeperProfile();
+//CaseBase();
+//%Case();
+//Keeper();
+//LidEdgeProfile();
+//KeeperProfile();
+//DemoProfiles();
+//PropRecess();
diff --git a/filament-test.scad b/filament-test.scad
new file mode 100644 (file)
index 0000000..443aab5
--- /dev/null
@@ -0,0 +1,16 @@
+// -*- C -*-
+translate([3,3,0]) mirror([1,1,0]) cube([15,15,1]);
+
+multmatrix([[  1,      0,      0,      0       ],
+       [       0,      1,      1.0,    0       ],
+       [       0,      0,      1,      0       ],
+       [       0,      0,      0,      1       ]])
+ cylinder(r=6.1/2, h=8);
+
+w=0.5;
+
+translate([15,0])
+difference(){
+  cube([8,8,8]);
+  translate([w,w,-1]) cube([8-w*2, 8-w*2, 8+2]);
+}
diff --git a/filamentclip.scad b/filamentclip.scad
new file mode 100644 (file)
index 0000000..e6d7afb
--- /dev/null
@@ -0,0 +1,53 @@
+include <cliphook.scad>
+include <filamentteeth.scad>
+
+rad=19;
+h=3.5;
+w=2.5;
+
+looprad=2.5;
+loopw=w;
+
+fdia=1.77;
+//fdia=3;
+
+d=0.01;
+
+module our_ClipHook(ye){
+  ClipHook(h=h, w=w, g=0.6, k=1.5, g=0.6, ye=ye, cupcaph=0.5, cupcapg=0.8);
+}
+
+module FilamentClip() {
+  rotate([0,0,-70]) {
+    translate([0,rad-1.5,0]) {
+      rotate([0,0,8])
+       our_ClipHook(ye=-1.3);
+    }
+  }
+
+  rotate([0,0,-35]) {
+    translate([0,rad,0]) {
+      rotate([0,0,180])
+       our_ClipHook(ye=0.8);
+    }
+  }
+
+  linear_extrude(height=h) {
+    assign($fn=80) {
+      FlatArc(0,0, rad-w/2,rad+w/2, 80,350);
+    }
+    assign($fn=30) {
+      FlatArc(0,rad+looprad+w, looprad,looprad+loopw);
+    }
+  }
+
+  for (mir=[0,1]) {
+    mirror([mir,0,0])
+      rotate([0,0,-40])
+      translate([rad+w*0.3+teethw*0.3+fdia/2, 0, 0])
+      rotate([0,0,95])
+      FilamentTeeth(fdia=fdia, h=h);
+  }
+}
+
+FilamentClip();
diff --git a/filamentspool-lt.scad b/filamentspool-lt.scad
new file mode 100644 (file)
index 0000000..3605f00
--- /dev/null
@@ -0,0 +1,4 @@
+//// toplevels-from:
+include <filamentspool.scad>
+lightduty = true;
+fdia = 2.85;
diff --git a/filamentspool-number.eps.pl b/filamentspool-number.eps.pl
new file mode 100755 (executable)
index 0000000..c05903a
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/perl -w
+use strict;
+die unless @ARGV==1 && $ARGV[0] =~ m/^\d+/;
+my $num = $ARGV[0];
+$num /= 1000;
+printf <<END, $num or die $!;
+%%!
+/Helvetica-Bold findfont
+15 scalefont
+setfont
+0 0 moveto
+(%.1f) show
+showpage
+END
diff --git a/filamentspool-sm.scad b/filamentspool-sm.scad
new file mode 100644 (file)
index 0000000..caaf0e4
--- /dev/null
@@ -0,0 +1,4 @@
+//// toplevels-from:
+include <filamentspool.scad>
+lightduty = true;
+fdia = 1.75;
diff --git a/filamentspool-storarm3.scad b/filamentspool-storarm3.scad
new file mode 100644 (file)
index 0000000..31dbaa9
--- /dev/null
@@ -0,0 +1,7 @@
+// -*- C -*-
+include <filamentspool.scad>
+lightduty = false;
+fdia = 2.85;
+storarm_spools=3;
+//// module StorageArmLeft ////toplevel
+//// module StorageArmRight ////toplevel
diff --git a/filamentspool.scad b/filamentspool.scad
new file mode 100644 (file)
index 0000000..b2991ea
--- /dev/null
@@ -0,0 +1,1025 @@
+// -*- C -*-
+
+// filamentspool.scad
+// 3D design for filament spools to hold coils as supplied by Faberdashery
+//
+
+//
+// Copyright 2012,2013,2016 Ian Jackson
+//
+// This work is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This work 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 work.  If not, see <http://www.gnu.org/licenses/>
+//
+
+//
+// Each spool is a hub with 3 or 4 arms.  Each arm has a cup for
+// holding the filament.  The effective diameter can be adjusted by
+// setting the cup into a different seat in the arm.  The cups are
+// held on with simple clips, so the filement coil can easily be
+// removed and replaced.
+//
+// This file (and its includes) can generate:
+//
+// ===== Heavy duty 4-armed spool for 3mm x 100m coil =====
+//
+// A heavy duty 4-armed spool suitable for holding a 100m
+// Faberdashery coil on the spool arm of a Lulzbot TAZ-5.
+//
+//     Set
+//           fdia=2.85
+//           lightduty=false
+//     And print following parts
+//            Hub
+//            ArmEnd x 4
+//            FilamentCup x 4  (or FilamentCupPair x 2)
+//            CupSecuringClip x 4
+//
+//     You will also need  4 x M4 machine screws and nuts.
+//
+//     This is the default.
+//
+// ===== Light duty 3-armed spool for 3mm x <=30m coil =====
+//
+// A light duty 3-armed spool suitable for up to around 30m
+// of Faberdashery 2.85mm PLA.
+//
+//     Set
+//           fdia=2.85
+//           lightduty=true
+//     (or look in filamentspool-lt.scad).
+//
+//     And print following parts
+//           Hub
+//           ArmEnd x 3
+//           FilamentCup x 3  (or FilamentCup + FilamentCupPair)
+//           CupSecuringClip x 3
+//           TowerDoveClipPin x 6
+//
+//     When assembling, insert one TowerDoveClipPin from each side,
+//     joining each ArmEnd to the Hub with two TowerDoveClipPins.
+//     Modest force with pliers is good to seat them properly.
+//
+//     (note that the light duty and heavy duty CupSecuringClips
+//      are slightly different)
+//
+// ===== Notes regarding both the above spools =====
+//
+// When mounting either spool on the TAZ-5 spool arm, put the `pointy'
+// end of the hub towards the printer - ie, put put the spool on
+// `backwards'.  This ensures that the spool's arms will clear the
+// printer framework.
+//
+// For the above, I generally used the Cura `Standard' PLA profile.
+//
+// ===== TAZ-5 feed tube adjustment kit =====
+//
+// With a TAZ-5 I recommend using this kit to improve the feed
+// reliability:
+//
+//       Set
+//           fdia=2.85
+//     And print following parts
+//           FilamentGuideSpacer (ideally, at `high detail')
+//           FilamentGuideArmPrint (optional; `high detail' or `standard')
+//
+//     And possibly also
+//           t-nut_jig_0.2.stl
+//     from Aleph Objects - look here:
+//           http://download.lulzbot.com/TAZ/accessories/tool_heads/version_2/Dual_Extruder_v2/production_parts/stl/
+//
+// The spacer clips onto the filament guide tube holder arm, on the
+// inside, with the pointy flanged end towards the filament guide
+// tube.  It stops the filament guide tube angle (and so the
+// filament's natural pickup location) changing as the print head moves.
+//
+// The FilamentGuideArm[Print] is a replacement for the arm supplied
+// with your TAZ-5.  It's longer, so that the filament pickup point is
+// closer to the middle of the coil.  Use the t-nut_jig to stop the
+// T-nuts in the aluminium channel from annoyingly sliding down to the
+// bottom while you swap out the arm.
+//
+// (Faberdashery coils, and therefore both the above spools, have a
+// larger diameter than the flat-walled spools often supplied by other
+// vendors.  And the spools above have individual arms rather than a
+// continuous disc.  If the filament `unhooks' from the arm, it can
+// pull taught around the hub and stop feeding properly.)
+//
+// ===== Spool storage arm, for mounting on walls =====
+//
+// A storage arm suitable for screwing to walls, bookshelves,
+// etc. (requires non-countersunk M4 screws); will hold two heavy duty
+// spools each with a 100m coil.
+//
+//     Set
+//           fdia=2.85
+//           lightduty=false
+//     And print one of these, according to taste
+//            StorageArmLeft
+//            StorageArmRight
+//
+//     NB that the `light duty' version of this is shorter and
+//     will only take two `light duty' spools.
+//
+// A longer arm for three spools is also available:
+//     Set
+//           fdia=2.85
+//           lightduty=false
+//           storarm_spools=3
+//     (or look in filamentspool-storarm3.scad).
+//
+//     And print one of these, according to taste
+//            StorageArmLeft
+//            StorageArmRight
+//
+// For all of these, I used the Cura `High detail' PLA profile because
+// I wanted it pretty, but the `Standard' profile should do fine.
+//
+// ===== Spools for 1.75mm filament =====
+//
+// Spool (in many parts) for handing 1.75mm filament, printable
+// on, and with parts for mounting on, a Reprappro Huxley.
+
+
+fdia=2.85; // or 1.75
+lightduty=false; // or true
+
+
+slop=0.5;
+bigslop=slop*2;
+
+function selsz(sm,lt,lg) = fdia < 2 ? sm : lightduty ? lt : lg;
+function usedove() = selsz(true,true,false);
+
+num_arms = selsz(3,3,4);
+
+channelslop=selsz(slop,0.75,slop);
+
+exteffrad = 70;
+hubeffrad = selsz(30, 52, 40);
+hubbigrad = selsz(20, 38, 38);
+hublwidth = selsz(3, 2.5, 3.0);
+hubstemwidth = 2;
+hublthick = 10;
+hubaxlerad = selsz(5, 28/2, 28/2);
+totalheightfromtower = 240;
+axletowerfudgebend = 0;
+axleaxlefudgebend = 3;
+axlepadlen = 1.0;
+
+armend_length = selsz(120, 150, 120);
+
+prongthick=selsz(5,4,5);
+prongwidth=selsz(5,4,5);
+prongribwidth=3;
+prongribheight=selsz(0,0,4);
+ratchetstep=10;
+ratchettooth=3;
+ratchettoothheight=5;
+ratchettoothsmoothr=1;
+ratchettoothslope=0.75;
+overlap=0.5;
+cupwidth=selsz(40,25,50);
+cupheight=selsz(75,35,75);
+
+cupstrong_dx=selsz(0,0,-10);
+
+propxshift = -6;
+
+doveclipheight = 10;
+
+teethh=3;
+teethgapx=4+fdia;
+
+prongstalkxwidth=3;
+
+stalklength=selsz(35,25,55);
+overclipcupgap=5;
+overclipdepth=15;
+overcliproundr=2.0;
+overclipthick=1.0;
+overclipcupnextgap=selsz(20,15,20);
+
+hubaxlelen = selsz(25, 62.5, 77.5);
+echo(hubaxlelen);
+
+overclipsmaller=0.5;
+overclipbigger=2.5;
+
+wingspoke=2.5;
+wingsize=6;
+wingthick=3;
+
+armendwallthick=selsz(2.5, 1.8, 2.5);
+armendbasethick=selsz(1.2, 1.2, 1.2);
+
+numbers_relief = 0.7;
+numbers_tick_len = 8;
+numbers_tick_width = 0.75;
+numbers_tick_linespc = 1.0;
+numbers_height_allow = 8;
+
+axlehorizoffset = 12.5;
+axlevertheight = 100;
+towercliph = 16;
+towerclipcount = 3;
+towerpillarw = 5;
+
+axlepinrad = 2;
+axlepintabrad = 5;
+
+washerthick = 1.2;
+washerthinthick = 0.8;
+washerverythinthick = 0.4;
+washerrad = hubaxlerad + 7.5;
+frictionwasherarmwidth = 3;
+frictionwasherextrapush = 1.0;
+
+ratchetpawl=ratchetstep-ratchettooth-bigslop*2;
+
+nondove_armhole_x = 32;
+nondove_armhole_hole = 4 + 0.8;
+nondove_armhole_support = 7;
+nondove_armhole_wall = 3.2;
+nondove_armhole_slop = 0.5;
+nondove_armhole_slop_x = 0.5;
+
+nondove_armbase = nondove_armhole_x + nondove_armhole_hole/2 +
+  nondove_armhole_support;
+echo(nondove_armbase);
+
+include <doveclip.scad>
+include <cliphook.scad>
+include <filamentteeth.scad>
+include <axlepin.scad>
+include <commitid.scad>
+
+hub_clip_baseextend = (hubeffrad - DoveClip_depth()
+                      - hubbigrad + hublwidth);
+
+real_exteffrad = selsz(exteffrad + hub_clip_baseextend,
+                      hubeffrad + DoveClip_depth(),
+                      hubeffrad + nondove_armbase);
+
+channelwidth = prongthick + channelslop;
+channeldepth = prongwidth + ratchettoothheight;
+totalwidth = armendwallthick*2 + channelwidth;
+totalheight = channeldepth + armendbasethick;
+stalkwidth = prongwidth + prongstalkxwidth;
+
+tau = PI*2;
+
+module ArmEnd(length=armend_length){ ////toplevel
+  if (usedove()) {
+    translate([ratchettoothsmoothr, channelwidth/2, -armendbasethick]) {
+      rotate([0,0,-90])
+       DoveClipPairBase(h=doveclipheight);
+    }
+  } else {
+    difference(){
+      translate([1, -armendwallthick, -armendbasethick])
+       mirror([1,0,0])
+       cube([nondove_armbase+1, totalwidth, totalheight]);
+      translate([-nondove_armbase + nondove_armhole_x,
+                -armendwallthick + totalwidth/2,
+                -armendbasethick -1])
+       cylinder(r= nondove_armhole_hole/2, h=totalheight+2, $fn=10);
+      translate([-nondove_armbase, -armendwallthick, -armendbasethick])
+        rotate([90,0,0])
+       Commitid_BestCount([nondove_armbase, totalwidth]);
+    }
+  }
+
+  difference(){
+    union(){
+      difference(){
+       translate([0, -armendwallthick, -armendbasethick])
+         cube([length, totalwidth, totalheight]);
+       translate([-1, 0, 0])
+         cube([length+1 - ratchettooth, channelwidth, channeldepth+1]);
+       translate([-1, 0, ratchettoothheight])
+         cube([length+2, channelwidth, channeldepth+1]);
+      }
+      for (dx = [0 : ratchetstep : length - ratchetstep]) translate([dx,0,0]) {
+       translate([ratchettoothsmoothr+0.5, armendwallthick/2, 0]) minkowski(){
+         rotate([90,0,0])
+           cylinder($fn=20, r=ratchettoothsmoothr, h=armendwallthick);
+         multmatrix([  [       1, 0, ratchettoothslope, 0      ],
+                           [   0,      1,      0,      0       ],
+                           [   0,      0,      1,      0       ],
+                           [   0,      0,      0,      1       ]])
+           cube([ratchettooth - ratchettoothsmoothr*2,
+                 channelwidth, ratchettoothheight - ratchettoothsmoothr]);
+       }
+      }
+    }
+
+    for (otherside=[0,1]) {
+      for (circum = [300:100:1500]) {
+       assign(rad = circum / tau)
+         assign(fn = str("filamentspool-number-n",circum,".dxf"))
+         assign(rotateoffset = [0, totalwidth/2, 0])
+         assign(xlen = rad - real_exteffrad) {
+         if (xlen >= numbers_tick_width/2
+             + (otherside ? numbers_height_allow : 0) &&
+             xlen <= length - (otherside ? 0 : numbers_height_allow))
+           translate([xlen, -armendwallthick,
+                      -armendbasethick + (totalheight - numbers_tick_len)/2])
+           translate(rotateoffset)
+           rotate([0,0, otherside*180])
+           translate(-rotateoffset){
+             translate([-numbers_tick_width/2, -1, 0])
+               cube([numbers_tick_width, numbers_relief+1, numbers_tick_len]);
+             translate([numbers_tick_width/2 + numbers_tick_linespc,
+                        1,
+                        numbers_tick_len])
+               rotate([90,0,0])
+               rotate([0,0,-90])
+               linear_extrude(height= numbers_relief+1)
+               //    scale(templatescale)
+               import(file=fn, convexity=100);
+           }
+       }
+      }
+    }
+
+    if (usedove()){
+      translate([0, -armendwallthick, -armendbasethick])
+       Commitid_BestCount_M([length/3, totalwidth]);
+    }
+  }
+}
+
+module FilamentCupHandle(){
+  pawlusewidth = ratchetpawl-ratchettoothsmoothr*2;
+  mirror([0,1,0]) {
+    cube([stalklength, stalkwidth, prongthick]);
+    translate([stalklength, stalkwidth/2, 0])
+      cylinder(r=stalkwidth/2, h=prongthick, $fn=20);
+    translate([ratchettoothsmoothr, stalkwidth, 0]) {
+      minkowski(){
+       cylinder($fn=20,r=ratchettoothsmoothr, h=1);
+       multmatrix([    [       1, -ratchettoothslope, 0, 0     ],
+                       [       0,      1,      0,      0       ],
+                       [       0,      0,      1,      0       ],
+                       [       0,      0,      0,      1       ]])
+         cube([pawlusewidth,
+               ratchettoothheight - ratchettoothsmoothr,
+               prongthick - 1]);
+      }
+    }
+  }
+}
+
+module FilamentCupCup(){
+  for (my=[0,1]) mirror([0,my,0]) {
+    translate([0, cupwidth/2, 0])
+      cube([cupheight + prongwidth, prongwidth, prongthick]);
+  }
+}
+
+module FilamentCupPositive() {
+  FilamentCupHandle();
+
+  gapy = prongwidth;
+  dy = cupwidth/2 + gapy + overclipcupgap;
+  baselen = dy+cupwidth/2;
+
+  translate([0, dy, 0])
+    FilamentCupCup();
+  cube([prongwidth, baselen+1, prongthick]);
+
+  translate([cupstrong_dx, prongwidth, 0]) {
+    cube([prongwidth, baselen-prongwidth, prongthick]);
+    for (y = [0, .33, .67, 1])
+      translate([0, (baselen - prongwidth) * y, 0])
+       cube([-cupstrong_dx + 1, prongwidth, prongthick]);
+  }
+  if (cupstrong_dx != 0) {
+    rotate([0,0,45])
+      translate([-prongwidth*.55, -prongwidth*2.1, 0])
+      cube([prongwidth*(2.65), prongwidth*4.2, prongthick]);
+  }
+
+  translate([0, -0.2, 0])
+    cube([prongribwidth, baselen, prongthick + prongribheight]);
+
+  if (prongribheight > 0) {
+    translate([-prongwidth, baselen, 0])
+      cube([cupheight/2, prongwidth + prongribheight, prongribwidth]);
+  }
+
+  midrad = cupwidth/2 + prongwidth/2;
+
+  propshift = stalklength - overclipdepth - prongthick + propxshift;
+  proptaken = propshift;
+  echo(midrad, propshift, proptaken);
+
+  translate([propshift, -1, 0]) {
+    // something is wrong with the y calculation
+    cube([prongwidth,
+         gapy+2,
+         prongthick]);
+  }
+  for (y = [overclipcupgap, overclipcupgap+overclipcupnextgap]) {
+    translate([cupstrong_dx, y + prongwidth, 0])
+      rotate([0,0, 102 + fdia])
+      FilamentTeeth(fdia=fdia, h=teethh);
+  }
+  for (x = [-0.3, -1.3]) {
+    translate([cupheight + overclipcupnextgap*x, baselen + prongwidth, 0])
+      rotate([0,0, 12 + fdia])
+      FilamentTeeth(fdia=fdia, h=teethh);
+  }      
+}
+
+module FilamentCup() { ////toplevel
+  difference(){
+    FilamentCupPositive();
+    translate([0, -stalkwidth, 0])
+      Commitid_BestCount_M([stalklength - stalkwidth, stalkwidth]);
+  }
+}
+
+module CupSecuringClipSolid(w,d,h1,h2){
+  rotate([0,-90,0]) translate([0,-h1/2,-w/2]) linear_extrude(height=w) {
+    polygon(points=[[0,0], [d,0], [d,h2], [0,h1]]);
+  }
+}
+
+module CupSecuringClipSolidSmooth(xrad=0, xdepth=0){
+  hbase = totalheight + prongstalkxwidth - overcliproundr*2;
+  minkowski(){
+    CupSecuringClipSolid(w=totalwidth,
+                        d=overclipdepth + xdepth,
+                        h1=hbase + overclipbigger,
+                        h2=hbase - overclipsmaller);
+    cylinder($fn=20, h=0.01, r=overcliproundr+xrad);
+  }
+}
+
+module CupSecuringClip(){ ////toplevel
+  wingswidth = wingspoke*2 + overclipthick*2 + overcliproundr*2 + totalwidth;
+  difference(){
+    union(){
+      CupSecuringClipSolidSmooth(xrad=overclipthick, xdepth=0);
+      translate([-wingswidth/2, -wingsize/2, 0])
+       cube([wingswidth, wingsize, wingthick]);
+      translate([-wingsize/2, -wingswidth/2, 0])
+       cube([wingsize, wingswidth, wingthick]);
+    }
+    translate([0,0,-0.1])
+      CupSecuringClipSolidSmooth(xrad=0, xdepth=0.2);
+  }
+}
+
+module ArmDoveClipPin(){ ////toplevel
+  DoveClipPin(h=doveclipheight);
+}
+
+module TowerDoveClipPin(){ ////toplevel
+  DoveClipPin(h=towercliph/2);
+}
+
+module Hub(){ ////toplevel
+  axlerad = hubaxlerad + slop;
+  xmin = axlerad+hublwidth/2;
+  xmax = hubbigrad-hublwidth/2;
+  hole = hubeffrad - hubbigrad - DoveClip_depth() - hublwidth*2;
+  holewidth = DoveClipPairSane_width() - hubstemwidth*2;
+  nondove_allwidth = nondove_armhole_wall*2 + totalwidth;
+  difference(){
+    union(){
+      difference(){
+       cylinder($fn=60, h=hublthick, r=hubbigrad);
+       translate([0,0,-1])
+         cylinder($fn=30, h=hublthick+2, r=(hubbigrad-hublwidth));
+      }
+      cylinder(h=hubaxlelen, r=axlerad+hublwidth);
+      for (ang=[0 : 360/num_arms : 359])
+       rotate([0,0,ang]) {
+         if (usedove()){
+           difference() {
+             translate([hubeffrad,0,0])
+               DoveClipPairSane(h=doveclipheight,
+                                baseextend = hub_clip_baseextend);
+             if (hole>hublwidth && holewidth > 2) {
+               translate([hubbigrad + hublwidth, -holewidth/2, -1])
+                 cube([hole, holewidth, hublthick+2]);
+             }
+           }
+         } else {
+           difference(){
+             translate([0,
+                        -nondove_allwidth/2,
+                        0])
+               cube([hubeffrad + nondove_armhole_x
+                     + nondove_armhole_hole/2 + nondove_armhole_support,
+                     nondove_allwidth,
+                     nondove_armhole_wall + totalheight]);
+             translate([hubeffrad - nondove_armhole_slop_x,
+                        -nondove_allwidth/2
+                        + nondove_armhole_wall - nondove_armhole_slop,
+                        nondove_armhole_wall])
+               cube([nondove_armhole_x + 50,
+                     totalwidth + nondove_armhole_slop*2,
+                     totalheight + 1]);
+             translate([hubeffrad + nondove_armhole_x, 0, -20])
+               cylinder(r= nondove_armhole_hole/2, h=50, $fn=10);
+           }
+         }
+       }
+      for (ang = [0 : 180/num_arms : 359])
+       rotate([0,0,ang]) rotate([90,0,0]) {
+         translate([0,0,-hublwidth/2])
+           linear_extrude(height=hublwidth)
+           polygon([[xmin,0.05], [xmax,0.05],
+                    [xmax,hublthick-0.2], [xmin, hubaxlelen-0.2]]);
+       }
+    }
+    translate([0,0,-1]) cylinder($fn=60, h=hubaxlelen+2, r=axlerad);
+
+    rotate([0,0, selsz(0,0,45)])
+      translate([axlerad+hublwidth,
+                -hublwidth/2,
+                0])
+      rotate([90,0,0])
+      Commitid_BestCount([(hubbigrad-hublwidth) - (axlerad+hublwidth),
+                         hublthick +
+                         hublwidth/2 * hubaxlelen/(hubbigrad-axlerad),
+                         ]);
+  }
+}
+
+module ArmExtender(){ ////toplevel
+  DoveClipExtender(length=exteffrad-hubeffrad,
+                  ha=doveclipheight,
+                  hb=doveclipheight);
+}
+
+module FsAxlePin(){ ////toplevel
+  AxlePin(hubaxlerad, washerrad*2, axlepinrad, axlepintabrad, slop);
+}
+
+module Axle(){ ////toplevel
+  pillarswidth = DoveClipPairSane_width(towerclipcount);
+
+  rotate([0,0, -( axleaxlefudgebend + atan(slop/hubaxlelen) ) ])
+  translate([-axlehorizoffset, -axlevertheight, 0]) {
+    rotate([0,0,-axletowerfudgebend])
+    rotate([0,0,-90])
+      DoveClipPairSane(h=towercliph, count=towerclipcount, baseextend=3);
+    translate([0, DoveClip_depth(), 0])
+    rotate([0,0,90])
+      ExtenderPillars(axlevertheight - DoveClip_depth(),
+                     pillarswidth, towercliph,
+                     pillarw=towerpillarw);
+  }
+
+  axleclearlen = hubaxlelen + slop*4 + washerthick*2 + axlepadlen;
+  axlerad = hubaxlerad-slop;
+  bump = axlerad * 0.2;
+  shift = axlerad-bump;
+  joinbelowallow = 3;
+
+  intersection(){
+    translate([0, 0, shift]) {
+      difference() {
+       union(){
+         translate([-1, 0, 0])
+           rotate([0,90,0])
+           cylinder($fn=60,
+                    r = axlerad,
+                    h = 1 + axleclearlen + axlepinrad*2 + 2);
+         mirror([1,0,0]) rotate([0,90,0])
+           cylinder(r = axlerad*1.75, h = 3);
+         intersection(){
+           mirror([1,0,0])
+             translate([axlehorizoffset - pillarswidth/2, 0, 0])
+             rotate([0,90,0])
+             cylinder($fn=60,
+                      r = towercliph - shift,
+                      h = pillarswidth);
+           translate([-50, -joinbelowallow, -50])
+             cube([100, joinbelowallow+50, 100]);
+         }
+       }
+       rotate([90,0,0])
+       translate([axleclearlen + axlepinrad/2, 0, -25])
+         cylinder(r = axlepinrad + slop, h=50);
+      }
+    }
+    translate([-50,-50,0]) cube([100,100,100]);
+  }
+}
+
+module washer(thick){
+  Washer(hubaxlerad, washerrad, thick, slop);
+}
+
+module AxleWasher(){ ////toplevel
+  washer(thick=washerthick);
+}
+
+module AxleThinWasher(){ ////toplevel
+  washer(thick=washerthinthick);
+}
+
+module AxleVeryThinWasher(){ ////toplevel
+  washer(thick=washerverythinthick);
+}
+
+module AxleFrictionWasher(){ ////toplevel
+  difference(){
+    cylinder(h=washerthick, r=washerrad);
+    translate([0,0,-1]) cylinder(h=washerthick+2, r=hubaxlerad+slop);
+  }
+  frarmr = hubbigrad;
+  frarmw = frictionwasherarmwidth;
+  frarmpawlr = hublwidth;
+  frarmpawlpush = slop*4 + frictionwasherextrapush;
+  for (ang=[0,180]) rotate([0,0,ang]) {
+    translate([washerrad-1, -frarmw/2, 0])
+      cube([frarmr - washerrad + 1, frarmw, washerthick]);
+    intersection(){
+      translate([frarmr - frarmpawlr, -50, 0])
+       cube([frarmpawlr, 100, 50]);
+      rotate([0,90,0])
+       cylinder(h = 50, r = frarmpawlpush, $fn=36);
+    }
+  }
+}
+
+module TowerExtender(){ ////toplevel
+  l = totalheightfromtower - axlevertheight;
+  echo("TowerExtender",l);
+  DoveClipExtender(length = l,
+                  ha = towercliph, hb = towercliph,
+                  counta = towerclipcount, countb = towerclipcount,
+                  pillarw = towerpillarw);
+}
+
+module FilamentCupPair(){ ////toplevel
+  FilamentCup();
+  translate([cupheight + prongthick*3,
+            cupwidth/2*1.7,
+            0])
+    rotate([0,0,180]) FilamentCup();
+}
+
+//----- storarm -----
+
+storarm_hooklen = 8;
+storarm_hookheight = 5;
+storarm_thick = 10;
+storarm_axleslop = 4;
+
+storarm_base_w = 30;
+storarm_base_h = 100;
+storarm_base_d = 15;
+storarm_base_mind = 2;
+
+storarm_cope_hubaxle_mk1 = true;
+
+storarm_screw_hole = 4;
+storarm_screw_hole_slop = 0.5;
+storarm_besides_hole = 4;
+
+storarm_under_hole = 5;
+storarm_screw_hole_head = 8.8;
+storarm_screw_hole_head_slop = 1.5;
+
+// calculated
+
+storarm_spools = 2;
+
+storarm_axlerad = hubaxlerad - storarm_axleslop;
+storarm_mainlen = hubaxlelen*storarm_spools
+  + storarm_axleslop*(storarm_spools-1)
+  + (storarm_cope_hubaxle_mk1 ? 10 : 0);
+storarm_totlen = storarm_mainlen + storarm_hooklen;
+
+storarm_taller = storarm_axleslop * (storarm_spools-2);
+
+storarm_mid_off_y = storarm_axlerad;
+
+storarm_base_off_y = storarm_mid_off_y + storarm_base_h/2;
+
+module StorageArmDiagPartSide(xmin, xmax){
+  xsz = xmax-xmin;
+  yuse = storarm_thick/2;
+
+  intersection(){
+    translate([xmin-1, -storarm_axlerad, storarm_thick/2])
+      rotate([0,90,0])
+      cylinder(r=storarm_axlerad, h=xsz+2, $fn=60);
+    translate([xmin, -yuse, 0])
+      cube([xsz, yuse, storarm_thick]);
+  }
+}
+
+module StorageArmDiagPart(xmin, xmax, adjbot, shear){
+  hull(){
+    StorageArmDiagPartSide(xmin,xmax);
+
+    multmatrix([[1,0,0,0],
+               [shear,1,0,0],
+               [0,0,1,0],
+               [0,0,0,1]])
+      translate([0, -storarm_axlerad*2 + adjbot, 0])
+      mirror([0,1,0])
+      StorageArmDiagPartSide(xmin,xmax);
+  }
+}
+
+module StorageArmBaseTemplate(){
+  square([storarm_base_w, storarm_base_h]);
+}
+
+module StorageArmAtMountingHoles(){
+  bes = storarm_besides_hole + storarm_screw_hole;
+
+  x0 = bes;
+  x1 = storarm_base_w-bes;
+  y1 = storarm_base_h - bes;
+  y0 = bes;
+
+  for (pos=[ [x0, y1],
+            [x1, y1],
+            [x1, y0] ]) {
+    rotate([0,90,0])
+      translate([pos[0] - storarm_base_w,
+                pos[1] - storarm_base_off_y, -storarm_base_d])
+      children();
+  }
+}
+
+module StorageArmRight(){ ////toplevel
+  shear = storarm_hookheight / (storarm_mainlen/2);
+  shear2 = shear + storarm_taller / (storarm_mainlen/2);
+  base_xyz = [-storarm_base_d, -storarm_base_off_y, storarm_base_w];
+
+  StorageArmDiagPart(-1, storarm_mainlen/2+1,
+                    -storarm_taller, shear2);
+  StorageArmDiagPart(storarm_mainlen/2-1, storarm_mainlen+1,
+                    storarm_hookheight/2, shear/2);
+
+  translate([0, storarm_hookheight, 0])
+    StorageArmDiagPart(storarm_mainlen, storarm_totlen,
+                      -storarm_hookheight/2, shear/2);
+
+  difference(){
+    union(){
+      hull(){
+       translate(base_xyz)
+         rotate([0,90,0])
+         linear_extrude(height=storarm_base_mind)
+         StorageArmBaseTemplate();
+       StorageArmDiagPart(-1, 0, -storarm_taller, shear);
+      }
+      StorageArmAtMountingHoles(){
+       cylinder(r= storarm_screw_hole_head/2,
+                h=10);
+      }
+    }
+    StorageArmAtMountingHoles(){
+      translate([0,0,-1])
+       cylinder(r= (storarm_screw_hole + storarm_screw_hole_slop)/2 ,
+                h=20);
+      translate([0,0,storarm_under_hole])
+       cylinder(r= (storarm_screw_hole_head + storarm_screw_hole_head_slop)/2,
+                h=20);
+    }
+    translate(base_xyz + [0, storarm_base_h/4, -storarm_base_w/4])
+      rotate([0,90,0])
+      Commitid_BestCount([storarm_base_w/2, storarm_base_h/2]);
+  }
+}
+
+module StorageArmLeft(){ ////toplevel
+  mirror([1,0,0]) StorageArmRight();
+}
+
+module StorArmHoleTest(){ ////toplevel
+  sz = storarm_screw_hole_head + storarm_besides_hole*2;
+  intersection(){
+    StorageArmRight();
+    translate([-50, -storarm_base_off_y, -1])
+      cube([100, sz, sz+1]);
+  }
+}
+
+
+//----- filament guide spacer -----
+
+guide_armdia = 15.0;
+guide_armwidth = 10.2;
+guide_armcorelen = 25.0;
+guide_clipcirclethick = 10.0;
+
+guidefilclip_outerdia = 22.8;
+
+guidespacer_armslop = 0.75;
+guidespacer_armlenslop = 1.05;
+
+guidespacer_prongprotrude = 4;
+guidespacer_thick = 1.6;
+
+// calculated
+
+guidespacer_armdia = guide_armdia + guidespacer_armslop;
+guidespacer_armwidth = guide_armwidth + guidespacer_armslop;
+guidespacer_len = guide_armcorelen - guide_clipcirclethick
+  + guidespacer_armlenslop;
+
+guidespacer_wingheight = (guidefilclip_outerdia - guidespacer_armdia)/2;
+
+module FilamentGuideArmTemplate(extra=0){
+  intersection(){
+    circle(r= (guidespacer_armdia/2) + extra);
+    square(center=true, [guidespacer_armwidth+extra*2,
+                        guidespacer_armdia + extra*2 + 10]);
+  }
+}
+
+module FilamentGuideSpacerInnerTemplate(){
+  FilamentGuideArmTemplate();
+  translate([0, -guidespacer_armdia/2])
+    square(center=true, [guidespacer_armwidth - guidespacer_prongprotrude,
+                        guidespacer_armdia]);
+}
+
+module FilamentGuideSpacer(){ ////toplevel
+  difference(){
+    union(){
+      linear_extrude(height= guidespacer_len)
+       FilamentGuideArmTemplate(extra= guidespacer_thick);
+      for (angle=[26, 60]) {
+       for (m=[0,1]) {
+         mirror([m,0,0]) {
+           rotate([0,0,angle]) {
+             hull(){
+               for (t=[[0, guidespacer_wingheight],
+                       [guidespacer_len-1, -guidespacer_wingheight]])
+                 translate([0,0, t[0] + 0.5])
+                   cube([guidespacer_thick, guidespacer_armdia + t[1]*2,
+                     1],
+                        center=true);
+             }
+           }
+         }
+       }
+      }
+    }
+    translate([0,0,-1])
+      linear_extrude(height= guidespacer_len+5)
+      FilamentGuideSpacerInnerTemplate();
+  }
+}
+
+
+//----- replacement filament guide arm for TAZ-5 -----
+
+guidearm_armslop = 0.25;
+guidearm_armlenslop = 0.25;
+
+guidearm_hookprotr = 3;
+guidearm_hookprotrflat = 1;
+guidearm_hookslope = 0.3;
+
+guidearm_totallen = 60;
+
+guidearm_screwplatesz = 12;
+guidearm_screwplateth = 4;
+guidearm_screwplatewd = 15;
+guidearm_screwhole = 5 + 0.5;
+
+guidearm_bendlen = 40;
+guidearm_bendslot = 4.5;
+
+guidearm_stopthick = 4;
+guidearm_protrslop = 1.0;
+
+// calculated
+
+guidearm_armdia = guide_armdia - guidearm_armslop;
+guidearm_armwidth = guide_armwidth - guidearm_armslop;
+guidearm_armcorelen = guide_armcorelen + guidearm_armlenslop;
+
+guidearm_base_z0 = -(guidearm_totallen - guidearm_armcorelen);
+
+guidearm_realbendlen = min(guidearm_bendlen,
+                          guidearm_totallen - guidearm_screwplateth - 0.1);
+guidearm_slopelen = guidearm_hookprotr/guidearm_hookslope;
+
+module FilamentGuideArmStop(h){
+  for (ts=[-1,+1]) {
+    translate([ts * guidearm_hookprotr, 0,0])
+      cylinder(r=guidearm_armdia/2, h, $fn=80);
+  }
+}
+
+module FilamentGuideArmShaftPositive(){
+  r = guidearm_armdia/2;
+
+  translate([0,0, guidearm_base_z0+1])
+    cylinder(r=r, h= guidearm_totallen, $fn=80);
+  translate([0,0, guidearm_armcorelen]){
+    hull(){
+      FilamentGuideArmStop(guidearm_hookprotrflat);
+      translate([0,0, guidearm_slopelen])
+       cylinder(r=r, h=guidearm_hookprotrflat, $fn=80);
+    }
+  }
+  mirror([0,0,1])
+    FilamentGuideArmStop(guidearm_stopthick);
+}
+
+module FilamentGuideArmBase(){
+  translate([0,
+            (guidearm_screwplatewd - guidearm_armwidth)/2,
+            guidearm_base_z0]){
+    difference(){
+      translate([0,0, guidearm_screwplateth/2])
+       cube(center=true,
+            [guidearm_armdia + guidearm_screwplatesz*2,
+             guidearm_screwplatewd,
+             guidearm_screwplateth]);
+      for (ts=[-1,+1]) {
+       translate([ts * (guidearm_armdia/2 + guidearm_screwplatesz/2),
+                  0,
+                  -20])
+         cylinder(r= guidearm_screwhole/2, h=40, $fn=20);
+      }
+    }
+  }
+}
+
+module FilamentGuideArm(){ ///toplevel
+  intersection(){
+    difference(){
+      FilamentGuideArmShaftPositive();
+      translate([-guidearm_bendslot/2,
+                -50,
+                -guidearm_realbendlen + guidearm_armcorelen])
+       cube([guidearm_bendslot,
+             100,
+             guidearm_realbendlen + 100]);
+      hull(){
+       for (zx=[ [ 0, guidearm_bendslot ],
+                 [ guidearm_armcorelen + guidearm_slopelen,
+                   guidearm_hookprotr*2 + guidearm_protrslop ]
+                 ]) {
+         translate([-zx[1]/2, -50, zx[0]])
+         cube([zx[1], 100, 1]);
+       }
+      }
+    }
+    cube(center=true,
+        [guidearm_armdia*2,
+         guidearm_armwidth,
+         guidearm_totallen*3]);
+  }
+  FilamentGuideArmBase();
+}
+
+module FilamentGuideArmPrint(){ ////toplevel
+  rotate([90,0,0])
+    FilamentGuideArm();
+}
+
+module Demo(){ ////toplevel
+  translate([-real_exteffrad,-20,0]) Hub();
+  ArmEnd();
+  translate([ratchettooth*2, 30, 0]) FilamentCup();
+  if (selsz(true,false,false)) {
+    translate([-exteffrad + hubeffrad - hub_clip_baseextend, -10, 0])
+      ArmExtender();
+  }
+}
+
+//ArmEnd();
+//FilamentCup();
+//FilamentCupPair();
+//CupSecuringClip();
+//Hub();
+//ArmExtender();
+//Axle();
+//AxleWasher();
+//AxlePin();
+//AxleFrictionWasher();
+//StorageArmLeft();
+//StorArmHoleTest();
+//FilamentGuideSpacer();
+//FilamentGuideArm();
+//FilamentGuideArmPrint();
+//Demo();
diff --git a/filamentteeth.scad b/filamentteeth.scad
new file mode 100644 (file)
index 0000000..f948c6e
--- /dev/null
@@ -0,0 +1,61 @@
+// -*- C -*-
+//
+// filamentteeth.scad
+//
+// 3D design for clips to hold FFF filament
+// Copyright 2012,2016 Ian Jackson
+//
+// This work is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This work 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 work.  If not, see <http://www.gnu.org/licenses/>.
+
+
+d=0.01;
+teethw=1.5;
+
+module FilamentTeeth(fdia, h, teethw=teethw,
+                        stembendd=0.5, stembendl=7, teethxl=1.5) {
+  gapw = fdia-stembendd*2;
+  teethbigw = gapw + teethw*2;
+  basew = fdia+teethw*2-stembendd*2;
+  based = basew/3;
+
+  translate([-based, -basew/2, 0]) cube([based, basew, h]);
+  difference() {
+    union() {
+      translate([-d, -teethbigw/2, 0])
+       cube([d+stembendl + teethw, teethbigw, h]);
+//  translate([
+//  stembigw = fdia + stembend
+//  translate([-d, -stemw, 
+      linear_extrude(height=h) {
+       translate([stembendl-fdia/2, 0]) circle(fdia/2+teethw, $fn=30);
+      }
+    }
+    translate([0,0,-1]) {
+      translate([0,-gapw/2])
+       cube([stembendl+teethxl+1, gapw, h+2]);
+      linear_extrude(height=h+2) {
+       translate([stembendl-fdia/2, 0]) circle(fdia/2, $fn=30);
+      }
+    }
+  }
+
+  for (mirr=[0:1]) {
+    mirror([0,mirr,0]) {
+      translate([stembendl + teethw, gapw/2, 0])
+       rotate([0,0,30])
+       cube([teethxl, teethw, h]);
+    }
+  }
+}
+
diff --git a/filamenttrestle.scad b/filamenttrestle.scad
new file mode 100644 (file)
index 0000000..376a1ae
--- /dev/null
@@ -0,0 +1,284 @@
+// -*- C -*-
+
+spoolinnerdia = 32;
+spoolwidth = 88.0;
+spoolinnerrad = (spoolinnerdia - 0.2) / 2;
+spoolouterrad = spoolinnerrad + 61.5;
+
+include <doveclip.scad>
+include <axlepin.scad>
+
+spoolradclear = 10;
+spoolradslop = 2;
+
+spoolinnerslop = 3;
+axleslop = 0.5;
+
+axlerad = 7;
+barwasherrad = 17;
+
+hubbasethick = 4;
+hubmainthick = 15;
+hubbaseweb = 1.2;
+hubbasestalkwidth = 4;
+hubwalls = 2.5;
+hubpillarw = 4;
+hubbaserad = spoolinnerrad + 10;
+hubmainrad = spoolinnerrad - spoolradslop;
+
+legw = 12;
+plugl = 20;
+plugwmin = 3;
+plugh = 10;
+plugslope = 0.5;
+plugwmax = plugwmin + plugh * plugslope * 2;
+
+trestlefoot = 15;
+
+trestlelegw = 10;
+trestlebaseh = 10;
+trestleplugd = 1;
+
+topblockthick = 3;
+topblockbasedepth = 5;
+
+pinbasew = 4.0;
+pinminh = 1.0;
+pinmaxh = 3.5;
+pindh = 1.75;
+pindwidth = 1.75;
+
+pintaperlen = 20;
+pinstraightlen = 30-pintaperlen;
+
+spoolouterpad = AxlePin_holerad()*2 * 1.5;
+spoolbarlen = spoolwidth +
+  2*(Washer_thick() + hubbasethick + AxlePin_holerad()
+     + spoolinnerslop + spoolouterpad);
+  barz = axlerad * 0.5;
+axlepin_x = spoolwidth/2 + hubbasethick +
+  Washer_thick() + spoolinnerslop + AxlePin_holerad()*0.5;
+
+trestleheight = spoolouterrad + spoolradclear - barz;
+trestlebase = trestleheight * 1.2;
+
+module Plug(d=0){
+  dw = d;
+  dh = d;
+  dhb = d*2;
+  a = atan(plugslope);
+  bdy = -dhb;
+  bdx = dw / cos(a) + bdy * plugslope;
+  tdy = dh;
+  tdx = bdx + tdy * plugslope;
+  translate([-d,0,0]) rotate([90,0,90]) linear_extrude(height=plugl+0.1+d*2){
+    polygon([[-(plugwmin/2 + bdx),  bdy],
+            [-(plugwmax/2 + tdx),  plugh + tdy],
+            [+(plugwmax/2 + tdx),  plugh + tdy],
+            [+(plugwmin/2 + bdx),  bdy]]);
+  }
+}
+
+module Bar(){ ////toplevel
+  spoolw = spoolbarlen;
+  biggestw = spoolw + 50;
+
+  intersection(){
+    for (mir=[0,1]) {
+      mirror([mir,0,0]) {
+       translate([spoolw/2, 0, 0])
+         Plug();
+       translate([-1, -50, -50])
+         cube([spoolw/2+1.1, 100, 100]);
+      }
+    }
+    difference(){
+      translate([-biggestw/2, -50, 0])
+       cube([biggestw, 100, 100]);
+      for (mir=[0,1]) {
+       mirror([mir,0,0])
+         translate([axlepin_x, 0, -50])
+         cylinder(r=AxlePin_holerad(), 100, $fn=15);
+      }
+    }
+    translate([0,0,barz]) {
+      translate([-100,0,0])
+       rotate([0,90,0]) cylinder(r=axlerad, h=200, $fn=60);
+    }
+  }
+}
+
+module FtAxlePin(){ ////toplevel
+  AxlePin(axlerad, (axlerad + barwasherrad*2)/3 * 2);
+}
+
+module AxleWasher(){ ////toplevel
+  Washer(axlerad, barwasherrad);
+}
+
+module Trestle(){ ////toplevel
+  legang = atan2(trestlebase/2, trestleheight);
+  eplen = sqrt(trestleheight*trestleheight + trestlebase*trestlebase*0.25);
+  topblockw = plugwmax + trestleplugd*2 + topblockthick*2;
+
+  pinholebasew = pinbasew + pindwidth*2;
+  pinholeh =     pinmaxh +  pindh;
+
+  difference(){
+    union(){
+      for (mir=[0,1]) {
+       mirror([mir,0,0]) {
+         rotate([0,0, -90-legang])
+           ExtenderPillars(length=eplen+trestlelegw,
+                           width=trestlelegw,
+                           height=legw,
+                           baseweb=true);
+
+         translate([-trestlebase/2, -trestleheight, 0])
+           cylinder(r=trestlelegw/2*1.2, h=trestlefoot);
+       }
+      }
+      translate([-topblockw/2, -topblockbasedepth, 0])
+       cube([topblockw,
+             topblockbasedepth + plugh + topblockthick
+             + (pinmaxh - pinminh)*0.5,
+             plugl]);
+
+      translate([-trestlebase/2, -trestleheight, 0])
+       ExtenderPillars(length=trestlebase, width=trestlebaseh*2, height=legw);
+    }
+    translate([-300, -trestleheight-50, -1])
+      cube([600, 50, 52]);
+
+    rotate([-90,-90,0])
+      Plug(d=trestleplugd);
+
+    for (rot=[0,180]) {
+      translate([0,0,plugl/2]) rotate([0,rot,0]) translate([0,0,-plugl/2]) {
+       translate([0,
+                  plugh - (pinmaxh - pinminh)*1.00,
+                  (plugl - pinholebasew*2)/3]) {
+         translate([-(topblockw*0.25+1), 0, pinholebasew/2])
+           rotate([-90,0,0]) %Pin();
+         translate([-(topblockw+1), 0, 0]) {
+           rotate([0,90,0]) {
+             linear_extrude(height = topblockw*2.0+2) {
+               polygon([[-1.0 * pinholebasew, -0.01],
+                        [-0.5 * pinholebasew, pinholeh],
+                        [ 0                 , -0.01]]);
+             }
+           }
+         }
+       }
+      }
+    }
+  }
+}
+
+module Pin(){ ////toplevel
+  rotate([90,0,90]) {
+    hull(){
+      for (mir=[0,1]) {
+       mirror([mir,0,0]) {
+         linear_extrude(height=0.1) {
+           polygon([[-0.01, 0],
+                    [-0.01, pinminh],
+                    [pinbasew*0.5*(pinminh/pinmaxh), 0]]);
+         }
+         translate([0,0,pintaperlen])
+           linear_extrude(height=pinstraightlen) {
+           polygon([[-0.01, 0],
+                    [-0.01, pinmaxh],
+                    [pinbasew*0.5, 0]]);
+         }
+       }
+      }
+    }
+  }
+}
+
+module HubEnd(){ ////toplevel
+  thick = hubmainthick+hubbasethick;
+  difference(){
+    union(){
+      for (ang=[0 : 60 : 359]) {
+       rotate([0,0,ang]) {
+         translate([hubmainrad - hubwalls/2, -hubbasestalkwidth/2, 0])
+           cube([hubbaserad - (hubmainrad - hubwalls/2),
+                 hubbasestalkwidth, hubbasethick]);
+         ExtenderPillar(length = hubmainrad-hubwalls/2,
+                        height = hubbasethick + hubmainthick,
+                        pillarw = hubpillarw);
+       }
+      }
+      cylinder(r=axlerad+hubwalls, h=thick);
+      cylinder(r=hubmainrad-0.1, h=hubbaseweb);
+      difference(){
+       cylinder(r=hubmainrad, h=thick, $fn=100);
+       translate([0,0,-1])
+         cylinder(r=hubmainrad-hubwalls, h=thick+2);
+      }
+      difference(){
+       cylinder(r=hubbaserad, h=hubbasethick, $fn=50);
+       translate([0,0,-1])
+         cylinder(r=hubbaserad-hubwalls, h=hubbasethick+2);
+      }
+    }
+    translate([0,0,-1])
+      cylinder(r=axlerad+axleslop, h=thick+2, $fn=50);
+  }
+}
+
+
+module TestKit(){ ////toplevel
+  translate([60,0,0]) mirror([1,0,0]) Pin();
+  translate([60,15,0]) mirror([1,0,0]) Pin();
+  translate([0,40,0]) intersection(){
+    Trestle();
+    translate([-50,-10,-1]) cube([100,100,100]);
+  }
+  intersection(){
+    translate([-60,10,0]) Bar();
+    cube(50,center=true);
+  }
+  %translate([50,40, AxlePin_zoffset()]) FtAxlePin();
+  %translate([0,-20,0]) AxleWasher();
+}
+
+module DemoSpool(){
+  rotate([0,90,0]) translate([0,0,-spoolwidth/2])
+    difference(){
+      cylinder(r=spoolouterrad, h=spoolwidth);
+      translate([0,0,-1]) cylinder(r=spoolinnerrad, h=spoolwidth+2);
+    }
+}
+
+module Demo(){ ////toplevel
+  color("blue") Bar();
+  for (mir=[0,1]) {
+    mirror([mir,0,0]) {
+      color("red") translate([spoolbarlen/2,0,0])
+       rotate([90,0,90]) Trestle();
+      color("orange")
+       translate([spoolwidth/2 + hubbasethick + spoolinnerslop*2/3, 0, barz])
+       rotate([0,90,0]) AxleWasher();
+      color("orange") translate([axlepin_x, 0, barz])
+       rotate([90,0,90]) FtAxlePin();
+      color("cyan")
+       translate([spoolwidth/2 + hubbasethick + spoolinnerslop*1/3, 0, barz])
+       rotate([0,-90,0]) HubEnd();
+    }
+  }
+  %translate([0,0,barz]) DemoSpool();
+}
+
+//Bar();
+//FtAxlePin();
+//AxleWasher();
+//Trestle();
+//Pin();
+//TestKit();
+//Plug(d=1);
+//ExtenderPillars(80,12,8, baseweb=true);
+//HubEnd();
+//Demo();
diff --git a/fire-blanket-wall-mushroom.scad b/fire-blanket-wall-mushroom.scad
new file mode 100644 (file)
index 0000000..dae4769
--- /dev/null
@@ -0,0 +1,40 @@
+// -*- C -*-
+
+fudge=0.15;
+
+screwrad = 4.5 / 2 + fudge; // xxx check
+shaftrad = 7.5 / 2 - fudge;
+
+diskrad = 12.0 / 2 - fudge;
+
+csinkpart = 0.5;
+
+shaftlen = 8; // xxx check
+
+diskthick = 1.5;
+
+disktaperratio = 2;
+
+// computed
+
+disktaperrad = diskrad - diskthick / disktaperratio;
+totallen = shaftlen + diskthick;
+
+module SidePlan(){
+  polygon([[-screwrad, 0],
+          [-disktaperrad, 0],
+          [-diskrad, -diskthick],
+          [-shaftrad, -diskthick],
+          [-shaftrad, -totallen],
+          [-screwrad, -totallen]]);
+}
+
+module Bush(){
+  rotate_extrude($fn=25, convexity=3){
+    SidePlan();
+  }
+}
+
+//SidePlan();
+rotate([0,180,0])
+  Bush();
diff --git a/fire-blanket-wall-mushroom.slic3r b/fire-blanket-wall-mushroom.slic3r
new file mode 100644 (file)
index 0000000..2aa4f4a
--- /dev/null
@@ -0,0 +1 @@
+first_layer_extrusion_width = 100%
diff --git a/firmware-eeprom.hex b/firmware-eeprom.hex
new file mode 100644 (file)
index 0000000..5064bfa
--- /dev/null
@@ -0,0 +1,65 @@
+:20000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:20040000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC
+:20042000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC
+:20044000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC
+:20046000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C
+:20048000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C
+:2004A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C
+:2004C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C
+:2004E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C
+:20050000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB
+:20052000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB
+:20054000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB
+:20056000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B
+:20058000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B
+:2005A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B
+:2005C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B
+:2005E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B
+:20060000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA
+:20062000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA
+:20064000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA
+:20066000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A
+:20068000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A
+:2006A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A
+:2006C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A
+:2006E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A
+:20070000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9
+:20072000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9
+:20074000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9
+:20076000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99
+:20078000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79
+:2007A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59
+:2007C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39
+:2007E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19
+:00000001FF
diff --git a/firmware-efuse.hex b/firmware-efuse.hex
new file mode 100644 (file)
index 0000000..1a73311
--- /dev/null
@@ -0,0 +1,2 @@
+:0100000000FF
+:00000001FF
diff --git a/firmware-flash.hex b/firmware-flash.hex
new file mode 100644 (file)
index 0000000..78762bd
--- /dev/null
@@ -0,0 +1,2045 @@
+:200000000C949E000C94C6000C94C6000C94C6000C94C6000C94C6000C94C6000C94C600D8
+:200020000C94C6000C94C6000C94C6000C94C6000C94C6000C94C6000C94C6000C94C60090
+:200040000C94C6000C94C6000C941B300C94C6000C9425310C94C6000C94C6000C94C6005B
+:200060000C94C6000C94C6000C94C6000C94C6000C944D310C94C6000C94C6007C3C3E5EAA
+:200080002B3D3F2F5B5D3B2C2A225C00002124272A002225282B00202326290202020202F7
+:2000A0000202020404040404040404030303030303030301010101010101010102040810DB
+:2000C000204080010204081020408001020408102040808040201008040201000000010240
+:2000E000000000000000000403070600000000000000000000000000000000494E46494E78
+:200100004954594E414ECDCCCC3D0AD7233C17B7D13877CC2B329595E6241FB14F0A000056
+:2001200020410000C84200401C4620BCBE4CCA1B0E5AAEC59D7471062F2FC93211241FBE19
+:20014000CFEFD0E1DEBFCDBF15E0A0E0B1E0EAE9F6E702C005900D92AC32B107D9F71DE0F8
+:20016000ACE2B5E001C01D92A630B107E1F711E0CCE3D1E004C02297FE010E941638C633D0
+:20018000D107C9F70E940C350C944B3B0C94000080912E07882399F14091AD035091AE0390
+:2001A0006091AF037091B00380912C0790912D072CE235E00E94B623882319F140912F07F5
+:2001C000509130076091310770913207411551056105710591F080912C0790912D072CE2F4
+:2001E00035E00E94B623882361F010922F0710923007109231071092320710922E0781E0D5
+:20020000089580E00895EF92FF920F931F937B018C010E94C8008823A9F0ECE2F5E01192E1
+:2002200087E0EC32F807D9F7E092AD03F092AE030093AF031093B00380912E078160809340
+:200240002E0781E01F910F91FF90EF900895DF92EF92FF920F931F937B018C01D42E80911A
+:20026000AD039091AE03A091AF03B091B0038E159F05A007B107C1F00E94C8008823D9F0F0
+:2002800080912C0790912D07B801A7012CE235E00E94B024882379F0E092AD03F092AE0362
+:2002A0000093AF031093B00380912E078D2980932E0781E001C080E01F910F91FF90EF907F
+:2002C000DF9008958F929F92AF92BF92CF92DF92EF92FF920F931F93CF93DF936C01142F72
+:2002E00070932D0760932C07442321F488249924540140C0453008F01CC160E070E080E02D
+:2003000090E040E00E942701882309F412C1C12FD0E02197FE0174E0EE0FFF1F7A95E1F75B
+:20032000E45DFA4FE254FE4F8081EE5BF14090E08F779070892B09F0FCC0E653FE4F8081CF
+:200340009181A281B38184369105A105B10508F4F0C064E0CC0FDD1F6A95E1F7CE50D94FA4
+:2003600088809980AA80BB8081149104A104B10409F4DFC0C501B40140E00E9427018823C7
+:2003800009F4D7C080913705909138058050924009F0CFC020913C05222309F4CAC0809125
+:2003A0003A0590913B05892B09F4C3C080913905882309F4BEC0F601228B809139058483FA
+:2003C000158661E070E007C04F5FF60145874150483008F0AEC0F6014585848190E09B0178
+:2003E000042E02C0220F331F0A94E2F78217930759F720914205309143052115310519F016
+:2004000040E050E008C020915005309151054091520550915305F60125833683478350874D
+:20042000E0903A05F0903B0500E010E0E80CF91C0A1D1B1DE38AF48A058B168B80913D05A6
+:2004400090913E05918F808F60913C0570E080E090E00E9485376E0D7F1D801F911FF601FC
+:20046000628F738F848F958F20913D0530913E0555E0220F331F5A95E1F721503E4F232F8C
+:200480003327269540E050E0260F371F481F591F26873787408B518B80913F0590914005C0
+:2004A000009719F0A0E0B0E008C080914C0590914D05A0914E05B0914F0585017401E21ADF
+:2004C000F30A040B150BA8019701280F391F4A1F5B1FF601058404C05695479537952795AA
+:2004E0000A94D2F72187328743875487253FFFE03F07F0E04F07F0E05F0710F48CE006C074
+:20050000255F3F4F4040504028F480E1F601878B81E010C08091580590915905A0915A0585
+:20052000B0915B05F601828F938FA48FB58F80E2EECF80E0DF91CF911F910F91FF90EF90CC
+:20054000DF90CF90BF90AF909F908F9008954F925F926F927F928F929F92AF92BF92CF922B
+:20056000DF92EF92FF920F931F93CF93DF93EC016A017B01280139014230510561057105F5
+:2005800008F46FC089859A85AB85BC850196A11DB11D84179507A607B70708F462C02B89FB
+:2005A0003C894D895E898F89803129F499278F2D7E2D6D2D09C0C701B60117E0969587958C
+:2005C000779567951A95D1F74B015C01820E931EA41EB51E8091AD039091AE03A091AF03A7
+:2005E000B091B00388169906AA06BB0639F0C501B40140E00E942701882399F18F898031CD
+:2006000049F4F601F070EE0FFF1FE45DFA4F518240820DC0F601EF77F070EE0FFF1FEE0F6A
+:20062000FF1FE45DFA4F408251826282738280912E07816080932E078A89823080F08D81F2
+:200640009E81AF81B885880E991EAA1EBB1E80922F0790923007A0923107B092320781E039
+:2006600001C080E0DF91CF911F910F91FF90EF90DF90CF90BF90AF909F908F907F906F9073
+:200680005F904F900895CF92DF92EF92FF920F931F93CF93DF93EC017A018B016901898577
+:2006A0009A85AB85BC850196A11DB11D84179507A607B70708F450C02B893C894D895E89A3
+:2006C0008F89803129F49927812F702F6F2D09C0C801B701E7E09695879577956795EA95AA
+:2006E000D1F7260F371F481F591F8091AD039091AE03A091AF03B091B003281739074A078E
+:200700005B0739F0CA01B90140E00E942701882319F18F89803159F4F701F070EE0FFF1FA1
+:20072000E45DFA4F80819181A0E0B0E00EC0F701EF77F070EE0FFF1FEE0FFF1FE45DFA4FC0
+:2007400080819181A281B381BF70F60180839183A283B38381E001C080E0DF91CF911F9195
+:200760000F91FF90EF90DF90CF900895AF92BF92CF92DF92EF92FF920F931F93DF93CF93C2
+:2007800000D000D0CDB7DEB76C017A018B0182E090E0A0E0B0E0F60180839183A283B383E1
+:2007A0005E010894A11CB11CC601B801A70195010E944303882319F1C601B801A70100E051
+:2007C00010E020E030E00E94A7028823C1F0E980FA800B811C81F6018789803129F088EF1E
+:2007E0009FEFAFEFBFE004C088EF9FEFA0E0B0E0E816F9060A071B07B8F281E001C080E0F9
+:200800000F900F900F900F90CF91DF911F910F91FF90EF90DF90CF90BF90AF9008954F9254
+:200820005F926F927F928F929F92AF92BF92CF92DF92EF92FF920F931F93DF93CF9300D0F5
+:2008400000D0CDB7DEB76C0149835A836B837C833901EE24FF2487012E010894411C511C20
+:20086000812CA2E09A2EA12CB12C49815A816B817C81C60192010E944303882351F1D50144
+:20088000C401F601058404C0880F991FAA1FBB1F0A94D2F7E80EF91E0A1F1B1F29813A8121
+:2008A0004B815C818789803129F088EF9FEFAFEFBFE004C088EF9FEFA0E0B0E0281739071B
+:2008C0004A075B0790F2F301E082F1820283138381E00F900F900F900F90CF91DF911F91A2
+:2008E0000F91FF90EF90DF90CF90BF90AF909F908F907F906F905F904F9008952F923F9295
+:200900004F925F926F927F928F929F92AF92BF92CF92DF92EF92FF920F931F93DF93CF9303
+:20092000CDB7DEB72F970FB6F894DEBF0FBECDBF1C014A875B876C877D873F872E87D90175
+:200940000D911D912D913C91011511052105310549F0480159010894811C911CA11CB11CEC
+:20096000198610C0FC0180809180A280B38019868A859B85AC85BD850197A105B10511F46B
+:2009800091E09987D10119968D919D910D90BC91A02D0196A11DB11D8D839E83AF83B8877D
+:2009A000750164014424552432018E010F5F1F4FF10181859285A385B485481659066A0640
+:2009C0007B0608F090C08D819E81AF81B8858C159D05AE05BF0550F482E0882E912CA12C14
+:2009E000B12CB2E0CB2ED12CE12CF12CC101B701A60198010E944303882309F474C08981E0
+:200A00009A81AB81BC81A70196012F5F3F4F4F4F5F4F0097A105B10519F049015A010DC03D
+:200A2000281939094A095B098A859B85AC85BD85281739074A075B0759F00894411C511CF9
+:200A4000611C711C0894C11CD11CE11CF11CB0CFC101A601B7010FEF1FEF2FEF3FE00E9491
+:200A6000A702882399F43FC02601370108944108510861087108C101B301A20197018601DF
+:200A80000E94A702882379F1730162018C149D04AE04BF0448F3AE85BF854D915D916D91ED
+:200AA0007C91411551056105710539F0C101950184010E94A7028823B1F0EE85FF8580820B
+:200AC0009182A282B382F985FF2359F00894811C911CA11CB11CD1018D929D92AD92BC92A4
+:200AE000139781E001C080E02F960FB6F894DEBF0FBECDBFCF91DF911F910F91FF90EF9090
+:200B0000DF90CF90BF90AF909F908F907F906F905F904F903F902F9008958130910579F074
+:200B2000823091051CF4009741F00EC08230910541F08330910541F406C05F9A05C0469A6C
+:200B400003C02B9A01C0299AFC01EE0FFF1FEE0FFF1FE553F84F80819181A281B3810196D6
+:200B6000A11DB11D80839183A283B3835F9846982B98299808950F931F93CF93DF938C01CC
+:200B8000EB012130310519F02FEF3FEF4BC081E030C0DF01AA0FBB1FAA0FBB1FAC0FBD1F94
+:200BA00012966D917C911397601771070CF53197EE0FFF1FEE0FFF1FEC0FFD1F228133817C
+:200BC0000190F081E02D021B130B8D919C918E1B9F0B621B730B9C01029FC001039F900DF4
+:200BE000129F900D11240E94B0376E0F7F1F16C08F5FE82FF0E0E417F5075CF2E417F507E7
+:200C000019F060E070E00AC041505040440F551F440F551FC40FD51F688179812FEF33E0E7
+:200C2000261B370BC901DF91CF911F910F910895CF93DF93EB012130310519F06FEF7FEF8E
+:200C40004CC02FEF33E0281B390B81E031C0DF01AA0FBB1FAA0FBB1FAC0FBD1F6D917C91D6
+:200C60001197261737071CF53197EE0FFF1FEE0FFF1FEC0FFD1F408151810280F381E02D95
+:200C8000241B350B12968D919C9113978E1B9F0B641B750BAC01249FC001259F900D349F81
+:200CA000900D11240E94B0376E0F7F1F16C08F5FE82FF0E0E417F50754F2E417F50719F0D6
+:200CC00060E070E00AC041505040440F551F440F551FC40FD51F6A817B81CB01DF91CF91C1
+:200CE0000895AF92BF92CF92DF92EF92FF920F931F93E0903404F0903504A0903A04B0907E
+:200D00003B04C0903C04D0903D04662777276E197F09882777FD8095982F0E941736A60194
+:200D200095010E947C350E94E4357093430B6093420B0027F7FC0095102FC801B7010E946D
+:200D40001736A60195010E947C350E94E4357093450B6093440B04E414E085E090E0B801FC
+:200D60004EE150E021E030E00E94BB059093470B8093460B8CE291E0B8014EE150E021E0D0
+:200D800030E00E94BB059093490B8093480B10925B0B10925C0B10925F0B1092610B82E077
+:200DA00090E0A0E0B0E08093620B9093630BA093640BB093650B1092790B84E295E0909329
+:200DC000810B8093800B1092840B90939D0B80939C0B1092A00B1F910F91FF90EF90DF9019
+:200DE000CF90BF90AF900895EF92FF920F931F93CF93DF930E948B302091510730915207AF
+:200E00004091530750915407621B730B840B950B603771058105910508F41DC10E948B30E1
+:200E20006093510770935207809353079093540787E00E943F2FCFEFD3E0C81BD90BD0930E
+:200E40002B0BC0932A0B8091460B9091470B8C179D0784F082E090E09093380780933707BF
+:200E600081E090E090933A07809339071092290B1092280B8091480B9091490BC817D907A7
+:200E800024F01092290B1092280B0091280B1091290B0C1B1D0B1093410B0093400BB80125
+:200EA000882777FD8095982F0E941736209136043091370440913804509139040E94CB368F
+:200EC0000E94E4357B017093390B6093380B8091340B9091350B800F911F2091420B3091AF
+:200EE000430B821793074CF04091440B5091450B9C01481759070CF49A013093350B209337
+:200F0000340BB901882777FD8095982F0E94173620913A0430913B0440913C0450913D04C8
+:200F20000E94CB360E94E4358B0170933B0B60933A0B8091360B9091370BBE01681B790BCB
+:200F4000882777FD8095982F0E94173620913E0430913F0440914004509141040E94CB36CE
+:200F60000E94E4359B0170933D0B60933C0BD093370BC093360BB701621B730B600F711FAA
+:200F800077FF03C060E070E008C08091320490913304861797070CF4BC0170933F0B6093E9
+:200FA0003E0B8DE00E945D2F80913707909138070297D9F02091280B3091290B8091460B01
+:200FC0009091470B821793079CF480913B0790913C07019690933C0780933B07019705979E
+:200FE00044F010923C0710923B0780E180B901C0149A0E948B302091550730915607409192
+:20100000570750915807621B730B840B950B6858734180409040F8F00E948B306093550770
+:2010200070935607809357079093580786E00E943F2F2FEF33E0281B390B30932F0B20937F
+:201040002E0B80912C0B90912D0B2817390714F05A9801C05A9ADF91CF911F910F91FF90DD
+:20106000EF9008958091390790913A07019741F4109238071092370710923A07109239074E
+:201080000E948B3020912A0B30912B0B8091280B9091290B2817390724F410923807109263
+:2010A000370708952F923F924F925F926F927F928F929F92AF92BF92CF92DF92EF92FF9217
+:2010C0000F931F93DF93CF93CDB7DEB7C856D0400FB6F894DEBF0FBECDBFA3969FAF8EAF96
+:2010E000A3976091AB077091AC078091AD079091AE072091BB073091BC074091BD07509157
+:20110000BE070E94C736181614F4459801C0459A6091AF077091B0078091B1079091B207B6
+:201120002091BF073091C0074091C1075091C2070E94C736181614F4479A01C04798609126
+:20114000B3077091B4078091B5079091B6072091C3073091C4074091C5075091C6070E947A
+:20116000C736181614F42A9A01C02A986091B7077091B8078091B9079091BA072091C70754
+:201180003091C8074091C9075091CA070E94C736181614F4289801C0289A80913D07882354
+:2011A00059F4329B09C0A396AEADBFADA3971D921D921D921C92139780913E07882351F467
+:2011C000339B08C0A396EEADFFADA397148215821682178280913F07882361F4349B0AC071
+:2011E000A396AEADBFADA39718961D921D921D921C921B97A396EEADFFADA397A080B1808F
+:20120000C280D380A114B104C104D10409F05E98A3962EAD3FADA3972C5F3F4F3AAF29AF32
+:20122000A396AEADBFADA3971496ED90FD900D911C911797E114F1040105110509F05E98D2
+:20124000A396EEADFFADA3973896F8AFEFABA396AEADBFADA39718962D913D914D915C9186
+:201260001B97211531054105510509F01598A396EEADFFADA3973C96A196FFAFEEAFA197C8
+:20128000A396AEADBFADA3971C966D917D918D919C911F976F966CAF7DAF8EAF9FAF6F9778
+:2012A000611571058105910509F05E98FE01319680E1DF011D928A95E9F7A982BA82CB82CE
+:2012C000DC82ED82FE820F83188729873A874B875C876F966CAD7DAD8EAD9FAD6F976D87A1
+:2012E0007E878F87988BAE14BF04C006D106B0F42E153F054007510760F46E157F058007E2
+:20130000910738F481E090E061969FAF8EAF61973CC0AE14BF04C006D106A8F02A153B0589
+:201320004C055D0580F46F966CAD7DAD8EAD9FAD6F976A157B058C059D0528F461961FAE9F
+:201340001EAE619722C02A153B054C055D05B8F02E153F054007510790F06F968CAD9DADDF
+:20136000AEADBFAD6F9782179307A407B50738F4A2E0B0E06196BFAFAEAF619706C0E3E085
+:20138000F0E06196FFAFEEAF61976196EEADFFAD6197EE0FFF1FEE0FFF1FEC0FFD1F21811E
+:2013A0003281438154812BAB3CAB4DAB5EAB56954795379527956B962CAF3DAF4EAF5FAF01
+:2013C0006B976896BFAD68976996AFAD69976A967FAD6A976B966FAD6B9720E030E080E08B
+:2013E00090E0AE014F5E5F4F6196EEADFFAD61972E173F0739F0FA01E80FF91FB083A18328
+:2014000072836383FC01E553F84F10821182128213822F5F3F4F04962430310529F76196D0
+:20142000EEADFFAD6197EE0FFF1FEE0FFF1FDF01A552B84F2D913D914D915C9167962CAF2A
+:201440003DAF4EAF5FAF67972093A7073093A8074093A9075093AA07E759F84F7080618055
+:201460003281A4963FAFA4974381A5964FAFA5977092990760929A0730939B0740939C070D
+:2014800061968EAC9FAC6197272D362DA4964FADA497A5965FADA597C901DA017C018D017D
+:2014A000CC24DD24AA24BB24A396EEADFFADA397EA0DFB1D2081318142815381211531056F
+:2014C0004105510551F161966EAD7FAD6197C616D70619F1F501E759F84F208031804280A5
+:2014E0005380C201B1010E948537F401EE0FFF1FEE0FFF1FA396AEADBFADA397EA0FFB1FCE
+:2015000020813181428153810E94C337E216F3060407150710F4460102C017012801089443
+:20152000C11CD11CE4E0F0E0AE0EBF1EF4E0CF16D10419F082017101B7CF60E071EE85EF8F
+:2015400095E0272D362DA4964FADA497A5965FADA5970E94C337C901DA0127968CAF9DAFE6
+:20156000AEAFBFAF27979401220F331F220F331FA3964EAD5FADA397420F531F5AAB49AB11
+:201580001EAE1DAE1CAE1BAEA396EEADFFADA3976BAD7CADE60FF71F608071808280938035
+:2015A000611471048104910409F44FC0C201B1010E9415365B016C01A9A9BAA96D917D912F
+:2015C0008D919C910E9415367B018C01C401B3010E9415369B01AC01C801B7010E947C3547
+:2015E0009B01AC01C601B5010E94CB360E94E9359B01AC01E1E2F0E0EC0FFD1F6BAD7CAD8E
+:20160000E60FF71F20833183428353838DAD9EAD6196AEADBFAD61978A179B07B1F42093F7
+:20162000990730939A0740939B0750939C0760E071EE85EF95E00E94C337C901DA01279625
+:201640008CAF9DAFAEAFBFAF2797EDADFEAD3196FEAFEDAF2BAD3CAD2C5F3F4F3CAF2BAF86
+:20166000349709F091CF2BED422E27E0522E91E2692E712C6C0E7D1EA8961FAEA897A996F2
+:201680001FAEA99722243324CC24DD24A396EEADFFADA397EC0DFD1D80819181A281B38178
+:2016A0000097A105B10509F47DC06F964CAD5DAD6EAD7FAD6F97411551056105710561F1CE
+:2016C000D2016D917D918D919C910E9417369B01AC0160E27CEB8EEB9CE40E947C357B0137
+:2016E0008C01F30160817181828193810E9415369B01AC0160E27CEB8EEB9CEC0E947C354C
+:201700009B01AC01C801B7010E9414357B018C01F601E758F84F2BC0D2016D917D918D91A6
+:201720009C910E9417369B01AC0160E27CEB8EEB9CE40E947C357B018C01F301608171817F
+:20174000828193810E9415369B01AC0160E27CEB8EEB9CEC0E947C359B01AC01C801B70175
+:201760000E9414357B018C01F601E757F84F60817181828193810E9415369B01AC01C80110
+:20178000B7010E947C354B015C01A8966FADA897A9967FADA997C101A50194010E94C736B5
+:2017A00018164CF4A8968FADA897A9969FADA997D1014C015D0124E030E0C20ED31E420E95
+:2017C000531E620E731E30E1C316D10451F0D501C401A8968FAEA897A9969FAFA9971D0157
+:2017E00055CF61960EAD1FAD6197000F111F000F111FF801E552F84F608171818281938170
+:201800000E9417369B01AC0160E27CEB8EEB9CE40E947C356B017C010C0F1D1FD80191965B
+:201820006D917D918D919C9194970E9415369B01AC0160E27CEB8EEB9CEC0E947C359B0157
+:20184000AC01C701B6010E941435A50194010E947C350E94E93560939D0770939E078093D1
+:201860009F079093A0070E94153620E030E040E05FE30E94CB36A50194010E94CB367B019C
+:201880008C0127966CAD7DAD8EAD9FAD27970E9417369B01AC01C801B7010E941435A5012C
+:2018A00094010E94CB360E94E4350E9415362EEA37E441E85FE30E94CB360E94E935609354
+:2018C000A1077093A2078093A3079093A40781E08093A5078091990790919A07A0919B07C3
+:2018E000B0919C0767962CAD3DAD4EAD5FAD679782179307A407B50710F41092A5070E94B1
+:20190000993023966CAF7DAF8EAF9FAF239724E630E040E050E00E948537609341077093B3
+:201920004207809343079093440760934507709346078093470790934807609349077093EB
+:201940004A0780934B0790934C0760934D0770934E0780934F079093500760E071EE85EFC6
+:2019600095E067962CAD3DAD4EAD5FAD67970E94C337C901DA012B968CAF9DAFAEAFBFAFD9
+:201980002B9761968EAD9FAD6197880F991F880F991FA3964EAD5FADA397480F591F63962F
+:2019A0005FAF4EAF63972BA93CA94DA95EA92F962CAF3DAF4EAF5FAF2F9722243324210150
+:2019C000AC014F5B584FA7965FAF4EAFA797BCC20E94F4068091A507882309F423C12114EB
+:2019E00031044104510489F48091990790919A07A0919B07B0919C078093A7079093A807DE
+:201A0000A093A907B093AA071DC18091A1079091A207A091A307B091A40782159305A405EF
+:201A2000B50508F488C060919D0770919E0780919F079091A00720E137E240E050E00E94E2
+:201A4000E537122F032FF42EE52E0E94993023962CAD3DAD4EAD5FAD2397621B730B840B90
+:201A6000950B24E630E040E050E00E94C337CA01B901212F302F4F2D5E2D0E9485379B018B
+:201A8000AC0127966CAD7DAD8EAD9FAD2797260F371F481F591F60E071EE85EF95E00E94C0
+:201AA000E537C901DA018093A7079093A807A093A907B093AA0767962CAD3DAD4EAD5FAD34
+:201AC000679782179307A407B50750F41092A6072093A7073093A8074093A9075093AA07F1
+:201AE0006B964CAD5DAD6EAD7FAD6B97241635064606570608F4A6C02092A1073092A20754
+:201B00004092A3075092A4072091A7073091A8074091A9075091AA0760E071EE85EF95E052
+:201B20000E94C337C901DA012B968CAF9DAFAEAFBFAF2B9785C02F962CAD3DAD4EAD5FADBB
+:201B40002F9782179307A407B50708F46BC08091A607882359F40E94993023966CAF7DAFDD
+:201B60008EAF9FAF239731E03093A60760919D0770919E0780919F079091A00720E137E2CB
+:201B800040E050E00E94E537122F032FF42EE52E0E94993023962CAD3DAD4EAD5FAD2397E7
+:201BA000621B730B840B950B24E630E040E050E00E94C337CA01B901212F302F4F2D5E2DBA
+:201BC0000E9485372B962CAD3DAD4EAD5FAD2B97261B370B480B590B60E071EE85EF95E098
+:201BE0000E94E537C901DA018093A7079093A807A093A907B093AA072091990730919A0765
+:201C000040919B0750919C07281739074A075B07C8F42093A7073093A8074093A9075093A6
+:201C2000AA0710C067964CAD5DAD6EAD7FAD67974093A7075093A8076093A9077093AA0774
+:201C40001092A6076396EEADFFAD639780819181A281B3810097A105B10509F475C180915A
+:201C60003D078823C9F4329B17C061962EAD3FAD6197232B09F495C1A396EEADFFADA39703
+:201C800080819181A281B3810097A105B10521F0108211821282138280913E078823B9F4DA
+:201CA000339B15C06196EEADFFAD6197319709F478C1E9ADFAAD80819181A281B38100970F
+:201CC000A105B10521F0108211821282138280913F078823B9F4349B15C06196EEADFFADB8
+:201CE0006197329709F45DC1EFA9F8AD80819181A281B3810097A105B10521F01082118238
+:201D0000128213820E94993024E630E040E050E00E948537A796EEADFFADA7972081318152
+:201D200042815381621B730B840B950B6093FC077093FD078093FE079093FF0797FFE2C06C
+:201D40000E94993024E630E040E050E00E948537A796AEADBFADA7976D937D938D939C93AF
+:201D600013978091A7079091A807A091A907B091AA07B695A795979587958093FC0790934F
+:201D8000FD07A093FE07B093FF07BCC00197A109B1096396EEADFFAD639780839183A283D0
+:201DA000B3838091FC079091FD07A091FE07B091FF072091A7073091A8074091A907509106
+:201DC000AA07821B930BA40BB50B8093FC079093FD07A093FE07B093FF0761968EAD9FAD6C
+:201DE00061970E948D05A796EEADFFADA79780819181A281B3812091A7073091A8074091EB
+:201E0000A9075091AA07820F931FA41FB51F80839183A283B383A3968EAC9FACA397B1E1AA
+:201E2000AB2EB12CAC0EBD1E3E010894611C711CCC24DD2461962EAD3FAD6197C216D3061A
+:201E4000E1F1F40180819181A281B3810097A105B10599F1D501ED90FD900D911C91F301B5
+:201E600080819181A281B381E81AF90A0A0B1B0BD501ED92FD920D931C93139717FF1DC0E8
+:201E8000C6010E948D05F40180819181A281B3810197A109B10980839183A283B3832BA9A6
+:201EA0003CA94DA95EA9E20EF31E041F151FD501ED92FD920D931C9313970894C11CD11CA5
+:201EC000E4E0F0E08E0E9F1EAE0EBF1E6E0E7F1EF4E0CF16D10409F0ADCF0894211C311C3A
+:201EE000411C511C2F962CAD3DAD4EAD5FAD2F9721503040404050402F962CAF3DAF4EAF49
+:201F00005FAF2F972091FC073091FD074091FE075091FF078091A7079091A807A091A90747
+:201F2000B091AA07281739074A075B0768F06396EEADFFAD639780819181A281B3810097EA
+:201F4000A105B10509F022CFE9ADFAAD80819181A281B381EFA9F8AD20813181428153816D
+:201F6000820F931FA41FB51FA396EEADFFADA3972081318142815381820F931FA41FB51F09
+:201F8000A196EEADFFADA1972081318142815381820F931FA41FB51F0097A105B10509F0DB
+:201FA00017CD159AABEA6A2EA7E07A2EFBEBCF2EF7E0DF2EEBEC8E2EE7E09E2E71EBA72E14
+:201FC00073E0B72ED601ED90FD900D911C91F3016081718182819381A80197010E94C736DF
+:201FE0001816DCF4D4016D917D918D919C910E941536F50120813181428153810E947C3597
+:202000009B01AC01C801B7010E941435D6016D937D938D939C93139719C0F401608171818A
+:20202000828193810E941536D5012D913D914D915C910E947C359B01AC01C801B7010E94B0
+:202040001335F601608371838283938324E030E0620E731EC20ED31E820E931EA20EB31EB7
+:202060003BEB631637E0730609F0ACCFC859DF4F0FB6F894DEBF0FBECDBFCF91DF911F91A7
+:202080000F91FF90EF90DF90CF90BF90AF909F908F907F906F905F904F903F902F900895E1
+:2020A0002F923F924F925F926F927F928F929F92AF92BF92CF92DF92EF92FF920F931F938E
+:2020C000DF93CF93CDB7DEB766970FB6F894DEBF0FBECDBFBDE3EB2EB7E0FB2E00E010E086
+:2020E000DD24D394F801E555F84FD801A554B84F60817181828193812D913D914D915C91E4
+:202100000E94C73688231CF0F701D08202C0F70110820C5F1F4F0894E11CF11C003111050D
+:2021200009F76091AB077091AC078091AD079091AE0720E030E046E153E40E94C736181672
+:2021400064F480E090E0A6E1B3E48093AB079093AC07A093AD07B093AE076091AF07709117
+:20216000B0078091B1079091B20720E030E044E153E40E94C736181664F480E090E0A4E11F
+:20218000B3E48093AF079093B007A093B107B093B2076091B3077091B4078091B5079091C9
+:2021A000B60720E030E048EC52E40E94C736181664F480E090E0A8ECB2E48093B3079093D9
+:2021C000B407A093B507B093B607AA24BB24F501E555F84FD501A554B84F60817181828185
+:2021E00093812D913D914D915C910E9413356B017C01F501EC5FF74F608371838283938328
+:2022000020E030E040E050E00E94C736181624F0F7FAF094F7F8F0948501075A184FF50151
+:20222000EF54FC4FC701B60120813181428153810E94CB360E94E935F8016083718382836F
+:20224000938384E090E0A80EB91E90E1A916B10409F0BDCF8091CD049091CE040A9734F4FF
+:202260008AE090E09093CE048093CD04A0900408B0900508C0900608D0900708C601B501D8
+:2022800020E030E040E050E00E94C736181654F0C601B50120E030E040E050E00E947835A1
+:2022A0008823BCF5609108087091090880910A0890910B0820E030E040E050E00E947835A9
+:2022C000882339F560910C0870910D0880910E0890910F0820E030E040E050E00E947835FC
+:2022E0008823B9F4C601B50120E030E040E050E00E94C736181624F0D7FAD094D7F8D09460
+:20230000A0920008B0920108C0920208D092030810C1C601B50120E030E040E050E00E941F
+:202320007835882309F060C0E0900808F090090800910A0810910B08C801B70120E030E033
+:2023400040E050E00E94C736181654F0C801B70120E030E040E050E00E9478358823E4F469
+:2023600060910C0870910D0880910E0890910F0820E030E040E050E00E947835882361F434
+:20238000C801B70120E030E040E050E00E94C73618160CF4C6C0C1C0C801B70120E030E0F7
+:2023A00040E050E00E9478358823F1F4E0900C08F0900D0800910E0810910F08C801B701F5
+:2023C00020E030E040E050E00E94C73618160CF4A8C0C801B70120E030E040E050E00E94E5
+:2023E000783588230CF499C0C601B50120E030E040E050E00E94C73618163CF1C601B501D8
+:2024000020E030E040E050E00E94783587FD1DC0E0900808F090090800910A0810910B0844
+:20242000C801B70120E030E040E050E00E94C73618165CF0C801B70120E030E040E050E0C1
+:202440000E94783588230CF04BC0E0900808F090090800910A0810910B08C601B501A601EF
+:2024600095010E94CB365B016C01C801B701A80197010E94CB369B01AC01C601B5010E948D
+:2024800014350E9432377B018C01A0900C08B0900D08C0900E08D0900F08C601B50120E0EC
+:2024A00030E040E050E00E94C7361816D4F5C801B701A80197010E94CB367B018C01C601F1
+:2024C000B501A60195010E94CB369B01AC01C801B7010E9414350E9432377B018C0121C0BC
+:2024E000E0901008F09011080091120810911308C801B70120E030E040E050E00E94C736D4
+:2025000018167CF0C801B70120E030E040E050E00E94783588230CF010C117FB109517F9B2
+:202520001095E0920008F092010800930208109303086091CD047091CE04882777FD8095D9
+:20254000982F0E94173620EC31EE44E65CE40E947C359B01AC016091000870910108809180
+:202560000208909103080E947C353B014C011A8A198AE989FA89E75AF84F80819181A2814F
+:20258000B3810097A105B10509F463C0E989FA89EC5FF74FA080B180C280D380C601B5010B
+:2025A00020E030E040E050E00E94C73618161CF48601750106C08601750117FB109517F957
+:2025C0001095C401B30120EC31EE44E65CE40E947C356B8B7C8B8D8B9E8BE989FA89EF5380
+:2025E000FC4F2080318042805380C801B7012B893C894D895E890E947C35A20191010E94C9
+:20260000C736181634F5C601B50120E030E040E050E00E94C736181624F0D7FAD094D7F8A4
+:20262000D094C401B301A20191010E947C357B018C01C601B5012B893C894D895E890E94D7
+:202640007C359B01AC01C801B7010E94CB363B014C01E989FA893496FA8BE98B709709F01B
+:2026600088CFC301D4018093EF079093F007A093F107B093F207EE24FF24F701E75AF84F2B
+:2026800060817181828193816115710581059105C1F087010552184F0E9415369B01AC011B
+:2026A000C401B3010E947C3520E030E048EC52E40E94CB360E94E435F801608371838283A1
+:2026C000938384E090E0E80EF91E90E1E916F104A1F68091590790915A07A0915B07B09140
+:2026E0005C0789839A83AB83BC8380915D0790915E07A0915F07B09160078D839E83AF8344
+:20270000B8878091610790916207A0916307B091640789879A87AB87BC87809165079091EC
+:202720006607A0916707B09168078D879E87AF87B88BCE0101960E94520866960FB6F8947C
+:20274000DEBF0FBECDBFCF91DF911F910F91FF90EF90DF90CF90BF90AF909F908F907F909C
+:202760006F905F904F903F902F9008950E948B3060934A0B70934B0B80934C0B90934D0B7E
+:202780008091370790913807029759F587EC9BE00E942B2F60E071E00E94453387EC9BE020
+:2027A0000E942B2F6091390770913A070E94663487EC9BE00E942B2F64E071E00E944533D5
+:2027C00087EC9BE00E942B2FE0913907F0913A07EE0FFF1FEE53FB4F608171810E94453309
+:2027E00009C0E0911C0BF0911D0BEC5EF44F8081882341F487EC9BE00E942B2F67E071E0DF
+:202800000E94743308950F931F9307EC1BE0C8010E942B2F0E94AA32C8010E942B2F6AE03E
+:2028200071E00E944533C8010E942B2F4091F7075091F8076091F9077091FA074F5F5F4F6A
+:202840006F4F7F4F0E9453340E94B6131F910F91089540911C0B50911D0B20E630E0B9019A
+:20286000469F9001479F300D569F300D1124682F772767FD7095C9018C5E974F0E94D63A6E
+:202880009C019093270B8093260B80E0232B09F081E008958091260B9091270B019660E0F1
+:2028A00070E00E941A3808951092C10B8091840B882321F080E89BE00E94DE2686E59BE0FE
+:2028C00060E04FE10E94C024882339F487EC9BE00E942B2F62E171E027C082E69BE066E597
+:2028E0007BE041E00E946201882381F482E69BE066E57BE040E00E946201882339F487EC3E
+:202900009BE00E942B2F6FE171E00EC080E89BE062E67BE00E94D629882349F487EC9BE0D4
+:202920000E942B2F62E371E00E947433089581E08093C10B0895AF92BF92CF92DF92EF925D
+:20294000FF920F931F93CF93DF9387E40E942914882309F4CFC20E944A140E94E4356C31E3
+:20296000710509F4CFC06D3171056CF477FF02C00C94531E62307105A4F06430710511F0F1
+:202980000C94531E8BC06B35710509F497C26C35710509F497C26A35710511F00C94531EDB
+:2029A0008AC2EBEBEE2EE7E0FE2E0BEA17E0C0E0D0E0FE01E853FB4F80810E94291488239B
+:2029C00041F10E944A145B016C01FE01ED5CF84F8081882311F061E002C06091FB0770E07A
+:2029E000882777FD8095982F0E941736F70120813181428153810E94CB369B01AC01C60154
+:202A0000B5010E941435F80160837183828393830AC0F70180819181A281B381F801808302
+:202A20009183A283B383219644E050E0E40EF51E0C5F1F4FC430D10509F0BBCF86E40E94E5
+:202A400029148823E9F00E944A140E94E4357B018C017093EC076093EB07B701882777FD3B
+:202A60008095982F0E94173620E030E040E050E00E94C736181624F4F092CE04E092CD04AF
+:202A80000E9450100E948B3060934A0B70934B0B80934C0B90934D0B0C94551E80E50E9437
+:202AA0002914882321F4AA24BB24650106C00E944A140E94E9355B016C0183E50E94291470
+:202AC000882361F00E944A1420E030E04AE754E40E94CB360E94E9355B016C010E948B30F8
+:202AE0007B018C01EA0CFB1C0C1D1D1D02C00E94F4060E948B306E157F0580079107B8F3D1
+:202B00000C94531E8091CD049091CE049093EE078093ED078091BB079091BC07A091BD0704
+:202B2000B091BE078093AB079093AC07A093AD07B093AE078091BF079091C007A091C1075D
+:202B4000B091C2078093AF079093B007A093B107B093B2078091C3079091C407A091C5071D
+:202B6000B091C6078093B3079093B407A093B507B093B6078091C7079091C807A091C907DD
+:202B8000B091CA078093B7079093B807A093B907B093BA071092CE041092CD048091C804B5
+:202BA0000E942914882371F48091C9040E942914882341F48091CA040E942914882311F41B
+:202BC00081E001C080E08093CC04882339F48091C8040E942914882309F466C00F2EF0E021
+:202BE000EF2EF0E0FF2EF0E00F2FF0E01F2FF02DE092BB07F092BC070093BD071093BE073A
+:202C000080E090E0A1E6B3EC8093AB079093AC07A093AD07B093AE076091D1037091D203A9
+:202C20008091D3039091D4030E94E4357093CE046093CD040E945010E092BB07F092BC07E6
+:202C40000093BD071093BE0780E090E0A0EAB0E48093AB079093AC07A093AD07B093AE074D
+:202C60000E94501080E090E0A0E2B1EC8093AB079093AC07A093AD07B093AE070E945010E7
+:202C8000E092BB07F092BC070093BD071093BE07E092AB07F092AC070093AD071093AE0704
+:202CA0001092CE041092CD048091CC04882339F48091C9040E942914882309F466C00F2EAC
+:202CC000F0E0EF2EF0E0FF2EF0E00F2FF0E01F2FF02DE092BF07F092C0070093C107109342
+:202CE000C20780E090E0AEE5B3EC8093AF079093B007A093B107B093B2076091D5037091B5
+:202D0000D6038091D7039091D8030E94E4357093CE046093CD040E945010E092BF07F092E3
+:202D2000C0070093C1071093C20780E090E0A0EAB0E48093AF079093B007A093B107B09346
+:202D4000B2070E94501080E090E0A0E2B1EC8093AF079093B007A093B107B093B2070E949D
+:202D60005010E092BF07F092C0070093C1071093C207E092AF07F092B0070093B10710935C
+:202D8000B2071092CE041092CD048091CC04882339F48091CA040E942914882309F478C03C
+:202DA0000F2EF0E0EF2EF0E0FF2EF0E00F2FF0E01F2FF02DE092C307F092C4070093C507BB
+:202DC0001093C60780E090E0A6E1B3EC8093B3079093B407A093B507B093B6076091D90326
+:202DE0007091DA038091DB039091DC030E94E4357093CE046093CD040E945010E092C30774
+:202E0000F092C4070093C5071093C60780E090E0A0E0B0E48093B3079093B407A093B50718
+:202E2000B093B6070E94501080E090E0A0E2B1EC8093B3079093B407A093B507B093B60707
+:202E40000E94501089EC9BE060E070E00E943B2F282F30E0C901880F892F881F990B892B65
+:202E600011F020503140B901882777FD8095982F0E94173620E030E048EC52E40E947C35FB
+:202E80006093C3077093C4078093C5079093C6078091ED079091EE079093CE048093CD04E4
+:202EA0000E948B3060934A0B70934B0B80934C0B90934D0BF8C61092FB07F5C681E080939E
+:202EC000FB07F1C6C8ECD4E00BEB17E088810E942914882339F00E944A14F8016083718358
+:202EE0008283938321960C5F1F4FF4E0CC3CDF0769F7D9C68DE40E942914882309F4B6C6F2
+:202F00000E944A140E94E4356C35710509F44DC46D3571050CF045C06931710509F48FC15B
+:202F20006A317105ECF46531710509F4FCC0663171056CF46530710509F4D7C064317105C4
+:202F400009F4DCC06430710509F0ADC674C06731710509F4F2C0683171050CF067C1E8C096
+:202F60006235710509F4E7C3633571056CF46B31710509F491C16B3171050CF468C16C31F6
+:202F8000710509F090C6C0C16435710509F4DAC3643571050CF4D2C36535710509F083C64C
+:202FA000EEC36737710509F4A8C4683771050CF56B36710509F4B8C36C3671056CF4693627
+:202FC000710509F453C26A3671050CF083C36836710509F068C612C26237710509F434C4FE
+:202FE0006337710509F421C46D36710509F05BC6CBC26A3C710509F4F1C46B3C71056CF4D4
+:203000006E3B710509F4ECC2693C710509F4BBC46C38710509F047C610C26D3C710509F441
+:203020006AC221E06D32720709F41DC56B3C710509F039C6FEC487EC9BE00E942B2F62E465
+:2030400071E00E94453387EC9BE00E942B2F60913707709138070E945E3487EC9BE00E94E8
+:203060002B2F64E071E00E94453387EC9BE00E942B2FE0913707F0913807EE0FFF1FE4549B
+:20308000FB4F608171810E9474338091370790913807029709F007C687EC9BE00E942B2FD2
+:2030A00060E071E00E94453387EC9BE00E942B2F6091390770913A070E945E3487EC9BE0E6
+:2030C0000E942B2F64E071E00E94453387EC9BE00E942B2FE0913907F0913A07EE0FFF1FCD
+:2030E000EE53FB4F60817181DCC5109238071092370710923A0710923907D5C587EC9BE0C3
+:203100000E942B2F65E471E00E94743380E89BE060E040E00E94D52A87EC9BE00E942B2F02
+:2031200065E571E0BEC51092C00B0E945414BBC51092C00B1092C10BB6C58091C10B88239C
+:2031400009F4B1C51092C00B8CE99BE00E94DE268091260B9091270B04966AE270E00E948C
+:20316000D63AFC01009711F0319710824091260B5091270B4C5F5F4F8CE99BE060E87BE04F
+:2031800021E00E94242E882309F449C087EC9BE00E942B2F63E671E00E94453387EC9BE0FD
+:2031A0000E942B2F6091260B7091270B6C5F7F4F0E94453387EC9BE00E942B2F60E771E089
+:2031C0000E94453387EC9BE00E942B2F4091AE0B5091AF0B6091B00B7091B10B0E941B346C
+:2031E0001092BC0B1092BD0B1092BE0B1092BF0B8091AE0B9091AF0BA091B00BB091B10B97
+:203200008093B80B9093B90BA093BA0BB093BB0B87EC9BE00E942B2F67E771E042C587ECED
+:203220009BE00E942B2F65E871E03BC58091C10B882309F438C581E08093C00B34C58091AE
+:20324000C00B882309F42FC51092C00B2CC58091C10B882309F427C583E50E942914882346
+:2032600009F421C58091260B9091270B019660E070E04AE050E00E947A39AB01BC014093C4
+:20328000BC0B5093BD0B8093BE0B7093BF0B8CE99BE00E94D02507C58091C10B882369F1DE
+:2032A00087EC9BE00E942B2F66E971E00E94453387EC9BE00E942B2F4091BC0B5091BD0B3F
+:2032C0006091BE0B7091BF0B0E94173487EC9BE00E942B2F68EA71E00E94453387EC9BE0E7
+:2032E0000E942B2F4091B80B5091B90B6091BA0B7091BB0B0E941B34D6C487EC9BE00E9401
+:203300002B2F6AEA71E0CDC48091C10B882309F4CAC48CE99BE00E94DE261092C00B8091F6
+:20332000260B9091270B04966AE270E00E94D63AEC01009709F120911C0B30911D0B80E67C
+:2033400090E0AC01249FC001259F900D349F900D11248C5E974F6EE470E00E94D63A60E260
+:2033600070E00E94D63A9C012F5F3F4F3093270B2093260B219718824091260B5091270B52
+:203380004C5F5F4F8CE99BE060E87BE026E50E94242E8823E9F487EC9BE00E942B2F6AEB7B
+:2033A00071E00E94453387EC9BE00E942B2F6091260B7091270B6C5F7F4F0E94453387EC3D
+:2033C0009BE00E942B2F6EEC71E00E9445336BC481E08093C20B87EC9BE00E942B2F60ED0A
+:2033E00071E00E94453387EC9BE00E942B2F6091260B7091270B6C5F7F4F53C483E50E9469
+:203400002914882391F00E944A140E94E435DC01CB0164E474E04EE150E021E030E00E9431
+:20342000BB059093290B8093280B8091390790913A07019709F037C4D0C083E50E94291419
+:20344000882309F430C40E944A140E94E435DC01CB0160ED74E044E150E021E030E00E94C3
+:20346000BB0590932D0B80932C0B1DC480912A0B90912B0B64E474E04EE150E021E030E05D
+:203480000E9418069093310B8093300B80912E0B90912F0B60ED74E044E150E021E030E013
+:2034A0000E9418069093330B8093320B87EC9BE00E942B2F62EE71E00E94453387EC9BE008
+:2034C0000E942B2F6091300B7091310B0E945E3487EC9BE00E942B2F68EE71E00E944533A8
+:2034E00087EC9BE00E942B2F6091320B7091330B0E946634DAC387EC9BE00E942B2F6CEE58
+:2035000071E00E94453387EC9BE00E942B2F60913E0B70913F0B0E945E3487EC9BE00E940D
+:203520002B2F62EF71E00E94453387EC9BE00E942B2F6091380B7091390B0E945E3487EC6B
+:203540009BE00E942B2F67EF71E00E94453387EC9BE00E942B2F60913A0B70913B0B0E942A
+:203560005E3487EC9BE00E942B2F6CEF71E00E94453387EC9BE00E942B2F60913C0B7091E6
+:203580003D0B0E945E3491C383E50E942914882321F10E944A147B018C0160914204709116
+:2035A0004304882777FD8095982F0E9417369B01AC01C801B7010E9413350E94E435DC018A
+:2035C000CB0164E474E04EE150E021E030E00E94BB059093290B8093280B0E94320863C312
+:2035E00083E50E942914882391F00E944A140E94E435DC01CB0164E474E04EE150E021E0F8
+:2036000030E00E94BB0590932D0B80932C0B0E948B307B018C0151C00E948B306E197F09B0
+:20362000800B910B695E73408040904008F443C080912A0B90912B0B64E474E04EE150E0C2
+:2036400021E030E00E9418069093310B8093300B87EC9BE00E942B2F61E072E00E94453355
+:2036600087EC9BE00E942B2F6091300B7091310B0E945E3487EC9BE00E942B2F68EE71E032
+:203680000E94453387EC9BE00E942B2F8C0180912E0B90912F0B64E474E04EE150E021E0F8
+:2036A00030E00E941806BC01C8010E9466340E948B307B018C010E94F40620912E0B3091CB
+:2036C0002F0B80912C0B90912D0B281739070CF4A3CFE9C283E50E942914882319F15C9A81
+:2036E0000E944A1420E030E040E050E00E94783588231CF460E070E011C00E944A1420E0FF
+:2037000030E04FE753E40E94C73618161CF46FEF70E004C00E944A140E94E4358CE00E9414
+:203720005D2FC1C25C9ABFC28CE060E070E00E945D2F5C98B8C210923607B5C281E08093A1
+:203740003607B1C283E50E942914882399F00E944A1420E030E04AE754E40E94CB360E9480
+:20376000E9356093520B7093530B8093540B9093550B99C25E9A5E9A159A5E9A94C283E5D5
+:203780000E9429140E944A1420E030E04AE754E40E94CB360E94E93560934E0B70934F0BC5
+:2037A0008093500B9093510B7EC2C8ECD4E001EB13E088810E942914882339F00E944A14D9
+:2037C000F801608371838283938321960C5F1F4FF4E0CC3CDF0769F7C0E0D0E08E0107590D
+:2037E000184FFE01E352FC4FDE01AF54BC4F60817181828193812D913D914D915C910E9413
+:20380000CB369B01AC0160E27CEB8EEB9CE40E947C350E94E935F8016083718382839383BE
+:203820002496C031D105D1F63EC287EC9BE00E942B2F64E072E00E94453387EC9BE00E9416
+:203840002B2F6DE074E02DC287EC9BE00E942B2F6CE172E00E94453387EC9BE00E942B2FF1
+:203860004091BB075091BC076091BD077091BE070E94FD3487EC9BE00E942B2F6FE172E037
+:203880000E94453387EC9BE00E942B2F4091BF075091C0076091C1077091C2070E94FD348F
+:2038A00087EC9BE00E942B2F62E272E00E94453387EC9BE00E942B2F4091C3075091C4073D
+:2038C0006091C5077091C6070E94FD3487EC9BE00E942B2F65E272E00E94453387EC9BE0FF
+:2038E0000E942B2F4091C7075091C8076091C9077091CA070E940135D6C187EC9BE00E94EB
+:203900002B2F68E272E00E94453387EC9BE00E942B2F329903C06FE272E002C062E372E023
+:203920000E94453387EC9BE00E942B2F65E372E00E94453387EC9BE00E942B2F339903C056
+:203940006FE272E002C062E372E00E94453387EC9BE00E942B2F6CE372E00E94453387EC39
+:203960009BE00E942B2F349903C06FE272E002C062E372E00E94453387EC9BE00E942B2F40
+:2039800063E472E08EC148ECE42E44E0F42EC0E0D0E0F70180810E9429148823A9F00E94A5
+:2039A0004A148E010758184FFE01EF54FC4F20813181428153810E94CB360E94E935F80181
+:2039C00060837183828393830894E11CF11C2496FCECEF16F4E0FF06E1F665C138ECE32E9D
+:2039E00034E0F32EC0E0D0E0F70180810E9429148823A9F00E944A148E010757184FFE01D3
+:203A0000EF54FC4F20813181428153810E94CB360E94E935F8016083718382839383089444
+:203A2000E11CF11C2496FCECEF16F4E0FF06E1F63AC18AE50E942914882309F434C10E949D
+:203A40004A1420E030E048EC52E40E94CB360E94E9359B01AC0189EC9BE060E070E0422FF1
+:203A60000E94362F20C180E50E942914882351F00E944A1460933604709337048093380474
+:203A80009093390489E40E942914882351F00E944A1460933A0470933B0480933C049093A5
+:203AA0003D0484E40E942914882351F00E944A1460933E0470933F04809340049093410458
+:203AC00086E40E942914882341F00E944A140E94E43570933304609332048AE50E94291455
+:203AE000882341F00E944A140E94E435709343046093420487E50E942914882341F00E94E3
+:203B00004A140E94E435709335046093340487EC9BE00E942B2F64E472E00E94453387EC14
+:203B20009BE00E942B2F409136045091370460913804709139040E94013587EC9BE00E9414
+:203B40002B2F68E472E00E94453387EC9BE00E942B2F40913A0450913B0460913C04709108
+:203B60003D040E94013587EC9BE00E942B2F6CE472E00E94453387EC9BE00E942B2F4091CB
+:203B80003E0450913F0460914004709141040E94013587EC9BE00E942B2F60E572E00E9449
+:203BA000453387EC9BE00E942B2F60913204709133040E94663487EC9BE00E942B2F69E5D0
+:203BC00072E00E94453387EC9BE00E942B2F60913404709135040E94663487EC9BE00E9460
+:203BE0002B2F64E672E00E94453387EC9BE00E942B2F60914204709143040E946634E090A0
+:203C00003404F0903504662777276E197F09882777FD8095982F0E94173620913A043091DB
+:203C20003B0440913C0450913D040E947C350E94E4357093430B6093420B0027F7FC0095C4
+:203C4000102FC801B7010E94173620913A0430913B0440913C0450913D040E947C350E949E
+:203C6000E4357093450B6093440B1DC087EC9BE00E942B2F6BE672E00E94743387EC9BE0F5
+:203C80000E942B2F40911C0B50911D0B20E630E0429FB001439F700D529F700D11246C5EB3
+:203CA000774F0E9474330E94B613DF91CF911F910F91FF90EF90DF90CF90BF90AF90089563
+:203CC000AF92BF92CF92DF92EF92FF920F931F93DF93CF930F92CDB7DEB78091200B90912F
+:203CE000210B03970CF47AC16CC24D3051F04A3341F02091230B3091240B2F3531050CF4C0
+:203D00004EC1E091230BF091240B309709F459C2A0911E0BB0911F0BAC9D7001AD9DF00CA1
+:203D2000BC9DF00C1124EE0DFF1DEC5EF74F10828091250B882309F02BC1AC5EB44F1C9234
+:203D400087010C5E174FC8016EE470E00E94D63A9C01009709F4B0C09093270B8093260BB4
+:203D60002F5F3F4F201B310B2E0D3F1DC9018C5E974F60E070E04AE050E00E947A3960934D
+:203D8000F3077093F4078093F5079093F6072091F7073091F8074091F9075091FA072F5F4C
+:203DA0003F4F4F4F5F4F6217730784079507D9F080911E0B90911F0B9C012C9DC0012D9DD0
+:203DC000900D3C9D900D11248C5E974F6CE772E00E94E13A009739F487EC9BE00E942B2FBB
+:203DE00061E872E03AC080911E0B90911F0B8C9D80018D9D100D9C9D100D1124C8018C5E7A
+:203E0000974F6AE270E00E94D63A9C010097E1F140E0FF24C80102C0FE264F5FFC01E40FD8
+:203E2000F11DEC5EF74FE081EA32B1F73093270B2093260BC901019660E070E00E941A3801
+:203E40000E94E4352F2D30E02617370721F187EC9BE00E942B2F61EC72E00E94453387EC98
+:203E60009BE00E942B2F4091F7075091F8076091F9077091FA070E9453340E9403141092A5
+:203E8000240B1092230B9DC187EC9BE00E942B2F66EE72E0E2CF8091F3079091F407A0912C
+:203EA000F507B091F6078093F7079093F807A093F907B093FA071EC0C8016AE270E00E9439
+:203EC000D63A0097B9F087EC9BE00E942B2F66E173E00E94453387EC9BE00E942B2F409139
+:203EE000F7075091F8076091F9077091FA070E945334C5CF80911E0B90911F0B8C9D700115
+:203F00008D9DF00C9C9DF00C112487010C5E174FC80167E470E00E94D63A9C01009711F16D
+:203F20009093270B8093260B2F5F3F4F201B310B2E0D3F1DC9018C5E974F60E070E00E94F2
+:203F40001A380E94E4356230710560F48091C20B882341F487EC9BE00E942B2F67E071E0B8
+:203F60000E94743380911E0B90911F0B019668E070E00E94B03790931F0B80931E0B809121
+:203F8000200B9091210B01969093210B8093200B1092250B1092240B1092230B24C04B33B0
+:203FA00011F4B092250B8091250B8823E1F480911E0B90911F0B8C9DF0018D9DF00D9C9DCA
+:203FC000F00D1124E20FF31FEC5EF74F40832F5F3F4F3093240B2093230B05C0A0E6CA2E27
+:203FE000D12CBB24B39487EC9BE00E942B2F0E946E32882309F4D2C18091200B9091210BAE
+:2040000008970CF0CBC187EC9BE00E942B2F0E948232482F8093220B8A3009F066CE71CE5C
+:204020008091230B9091240B892B09F0CAC0A5C08CE99BE0B70141E050E00E94B527019746
+:2040400019F08FEF9FEF02C0898190E09093C40B8093C30B482F8093220B8A3079F08D30A5
+:2040600069F08A3359F02091230B3091240B2F35310524F48F5F9F4F09F060C02091A50B0A
+:204080003091A60B4091A70B5091A80B2093BC0B3093BD0B4093BE0B5093BF0B8091B80B75
+:2040A0009091B90BA091BA0BB091BB0B281739074A075B0750F01092C00B87EC9BE00E94AF
+:2040C0002B2F66E473E00E9474338091230B9091240B009709F475C020911E0B30911F0B83
+:2040E000209FF001219FF00D309FF00D1124E80FF91FEC5EF74F10828091250B8823B9F488
+:20410000F901EC5EF44FD0828091200B9091210B01969093210B8093200BC901019668E070
+:2041200070E00E94B03790931F0B80931E0B1092250B1092240B1092230B27C08B3311F400
+:20414000D092250B8091250B8823F9F480911E0B90911F0B809FF001819FF00D909FF00D16
+:204160001124E20FF31FEC5EF74F40832F5F3F4F3093240B2093230B08C07E010894E11CE5
+:20418000F11C00E610E0DD24D3942091B80B3091B90B4091BA0B5091BB0B8091BC0B9091A5
+:2041A000BD0BA091BE0BB091BF0B82179307A407B50738F48091200B9091210B08970CF444
+:2041C00037CF8091200B9091210B892B09F491C08091C20B882309F474C020911C0B3091FB
+:2041E0001D0B80E690E0289F5001299FB00C389FB00C1124F4E1CF2EF8E0DF2ECA0CDB1CD9
+:20420000C60169E573E00E94E13A009709F046C0D6010D900020E9F77D010894E108F10873
+:20422000EC18FD0810929E0BC6016EE470E00E94D63A8C01009731F4F501EE0DFF1DED5E6E
+:20424000F74F0FC060E270E00E94D63A6C010894C11CD11CC8016AE270E00E94D63AFC011E
+:2042600031978DE081838AE0828313828CE99BE0B6010E94A42D80919E0B882341F087ECDE
+:204280009BE00E942B2F6DE573E00E94743387EC9BE00E942B2F67E071E010C08CE99BE077
+:2042A0000E9496268CE99BE00E94DE261092C20B87EC9BE00E942B2F63E773E00E947433CB
+:2042C00002C00E949B148091200B9091210B01979093210B8093200B80911C0B90911D0B9C
+:2042E000019668E070E00E94B03790931D0B80931C0B0E94F4060E948B30E0904E0BF0903F
+:204300004F0B0091500B1091510B20914A0B30914B0B40914C0B50914D0B621B730B840B52
+:20432000950BE616F7060807190798F4E114F1040105110571F01092290B1092280B5D9822
+:2043400010922D0B10922C0B5A985E9A5E9A159A5E9A0E948B30E090520BF090530B009188
+:20436000540B1091550B20914A0B30914B0B40914C0B50914D0B621B730B840B950BE61639
+:20438000F7060807190780F4E114F1040105110559F05E9A5E9A159A5E9A06C08091C00BF5
+:2043A000882309F40ECF3CCE0F90CF91DF911F910F91FF90EF90DF90CF90BF90AF900895A8
+:2043C000CF92DF92EF92FF920F931F93CF93DF938EED9CE040E052EC61E070E00E947531A3
+:2043E0008EED9CE065E873E00E947433E4E1FBE011928BE0EC31F807D9F70C9A149A3D9A18
+:204400003F9A229A209A569A5E9A569A5E9A0D9A159A569A5E9A3A98429A3B98439A3C980D
+:20442000449A549A5C9A559A529A579A3E9A239A219AC0E0D0E0FE01EF54FC4FE080F180F0
+:204440000281138199E6C92E97E0D92ECC0EDD1EFE01E352FC4FC801B70120813181428166
+:2044600053810E94CB369B01AC0160E27CEB8EEB9CE40E947C350E94E935F60160837183F9
+:204480008283938389E7C82E87E0D82ECC0EDD1EFE01E351FC4F60817181828193810E944F
+:2044A0001736A80197010E94CB360E94E935F6016083718382839383B9E8CB2EB7E0DB2EE8
+:2044C000CC0EDD1EFE01E350FC4F60817181828193810E941736A80197010E94CB360E942B
+:2044E000E935F60160837183828393832496C031D10509F0A0CF0E945414DF91CF911F9142
+:204500000F91FF90EF90DF90CF900895FC01673020F086E1858380E00895262F30E060FDB0
+:2045200002C0663019F48DB58E7F02C08DB581608DBD8CB58C7F8CBD4CB522FD02C090E0B1
+:2045400001C092E036952795822F8170842B892B8CBD81E00895FF920F931F93CF93DF939C
+:204560008B010E948B30EB01FF24FA94FEBC0DB407FEFDCF8EB58F3F11F481E008C00E9488
+:204580008B306C1B7D0B6017710780F380E0DF91CF911F910F91FF900895FC01848160E001
+:2045A0000E94D02F0895FC01848161E00E94D02F0895DC016EBDFA0120E030E00DB407FE63
+:2045C000FDCF80818EBD0DB407FEFDCF81818EBD2E5F3F4F329682E02030380779F70DB4DF
+:2045E00007FEFDCF8FEF8EBD0DB407FEFDCF8FEF8EBD0DB407FEFDCF8FEF8EBD0DB407FEA5
+:20460000FDCF8EB51A968C931A978F71853011F481E0089581E115968C931597CD010E940B
+:20462000D32280E00895FF920F931F93CF93DF93EC010E948B308B01FF24FA9409C00E94DD
+:204640008B30601B710B6D52714010F08FE00DC0FEBC0DB407FEFDCF8EB58A878F3F79F322
+:204660008E3F11F481E006C08DE08D83CE010E94D32280E0DF91CF911F910F91FF900895B2
+:20468000CF93DF93EC018E818823D1F08FEF8EBD2F8138854FEF04C00DB407FEFDCF4EBD09
+:2046A0002F5F3F4FC901019781509240A8F338872F830DB407FEFDCFCE010E94D3221E8235
+:2046C000DF91CF910895DF92EF92FF920F931F93CF93DF93EC01D62E79018A010E944023C7
+:2046E000CE010E94CD22CE016CE271E00E94AB228D2D80648EBD0DB407FEFDCF28E130E0E9
+:20470000D801C701022E04C0B695A795979587950A94D2F78EBD0DB407FEFDCF2850304009
+:204720008FEF283F380761F7DD2011F485E906C088E0D81611F487E801C08FEF8EBD0DB4B2
+:2047400007FEFDCF90E02FEF2EBD0DB407FEFDCF8EB587FF02C09150B9F78A87DF91CF9180
+:204760001F910F91FF90EF90DF900895CF92DF92EF92FF920F931F93CF93DF93EC017A015B
+:204780008B016901411551056105710511F482E137C08B85833039F029E0EE0CFF1C001F13
+:2047A000111F2A95D1F7CE0168E1A80197010E946323882311F084E023C0CE016EEFA601FB
+:2047C0000E94D9228823E9F0CE0168E572E00E94AB22882311F485E113C0CE016DE020E0D6
+:2047E00030E040E050E00E946323882341F48FEF8EBD0DB407FEFDCF8EB5882379F084E13A
+:204800008D83CE010E94D32280E0DF91CF911F910F91FF90EF90DF90CF900895CE010E94B8
+:20482000D32281E0F2CF8F929F92AF92BF92CF92DF92EF92FF920F931F93CF93DF93EC0184
+:204840005A016B0149010115110509F47BC0C801820F931F8150924008F06FC08E81882353
+:2048600071F088819981AA81BB81481759076A077B0729F48F8198858816990618F5A882DD
+:20488000B982CA82DB828B85833039F069E0AA0CBB1CCC1CDD1C6A95D1F7CE0161E1A60112
+:2048A00095010E946323882319F083E08D8345C0CE010E941323882309F43FC018861F8281
+:2048C00081E08E838FEF8EBD8F8198852FEF05C00DB407FEFDCF2EBD019688159905C0F38B
+:2048E00098878F83D801119720E030E09FEF0BC00DB407FEFDCF8EB5F701E20FF31F8083CA
+:204900009EBD2F5F3F4F2A173B0790F30DB407FEFDCF8EB5AE0DBF1D8C932F813885200FF3
+:20492000311F38872F838985882319F02050324048F0CE010E94402305C0CE010E94D322DB
+:2049400080E001C081E0DF91CF911F910F91FF90EF90DF90CF90BF90AF909F908F90089560
+:20496000EF92FF920F931F93790120E030E000E012E00E9413241F910F91FF90EF900895A1
+:20498000BF92CF92DF92EF92FF920F931F93CF93DF93EC01B62E1B8619861E821D824C839B
+:2049A0000E948B306B018C8161E00E94B22FCE010E94D32286E060E00E94B22F85E061E028
+:2049C0000E94B22F87E061E00E94B22F84E061E00E94B22F83E58CBD8DB58E7F8DBD80E057
+:2049E0009FEF9EBD0DB407FEFDCF8F5F8A30C9F7CE010E94CD2209C00E948B306C197D0943
+:204A0000615D774010F081E05EC0CE0160E020E030E040E050E00E946323182F8A87813002
+:204A200059F7CE0168E02AEA31E040E050E00E94632382FF02C01B8711C080E02FEF2EBD53
+:204A40000DB407FEFDCF9EB58F5F8430C1F79A879A3A11F082E037C082E08B878B85823092
+:204A600021F0EE24FF2487010FC0E12CF12C012D70E4172F09C00E948B306C197D09615DB8
+:204A8000774010F088E01FC0CE0167E320E030E040E050E00E946323CE0169E2A80197011C
+:204AA0000E9463238A87882331F78B85823031F5CE016AE320E030E040E050E00E9463235E
+:204AC000882339F086E08D83CE010E94D32280E01CC08FEF8EBD0DB407FEFDCF8EB5807C50
+:204AE000803C11F483E08B8780E09FEF9EBD0DB407FEFDCF2EB58F5F8330C1F7CE010E94F8
+:204B0000D322CE016B2D0E948622DF91CF911F910F91FF90EF90DF90CF90BF900895CF93A5
+:204B2000DF93EC01DB0180E090E020E2FD01E80FF91F208301968B309105C1F757E040E0C1
+:204B400021C02E3219F08CE790E007C05A3019F15AE048E017C03217F1F0FC010196349111
+:204B60003323C9F75417B8F02132A8F02F3798F4822F81568A3108F42052FD01E40FF11D7F
+:204B800020834F5F29912223E1F690E08C91803219F091E001C090E0892FDF91CF910895DF
+:204BA0006F927F928F929F92AF92BF92CF92DF92EF92FF920F931F93CF93DF93EC017A018B
+:204BC0008B012C81222309F480C08A899B89AC89BD8984179507A607B70708F476C085C04F
+:204BE000E114F1040105110549F41D821E821F82188619861A861B861C8665C029853A85E0
+:204C00004B855C85EA8DFB8D858590E00996215030404040504039014A01082E04C0969431
+:204C20008794779467940A94D2F72F5F3F4F4F4F5F4F0894E108F108010911095701680121
+:204C400004C0D694C794B794A7948A95D2F70894E11CF11C011D111DA614B704C804D9044D
+:204C600028F0211531054105510549F48E899F89A88DB98D8D839E83AF83B88716C0A618E7
+:204C8000B708C808D90811C04D815E816F8178858A8D9B8D94010E9443030894A108B1087F
+:204CA000C108D108882331F410C085E0882E912C8C0E9D1EA114B104C104D10429F7E986F2
+:204CC000FA860B871C8781E001C080E0DF91CF911F910F91FF90EF90DF90CF90BF90AF9013
+:204CE0009F908F907F906F900895223009F078CFE6CFCF93DF93EC01462F6D857E858F8535
+:204D000098890E942701882319F420E030E00AC08989282F30E095E0220F331F9A95E1F79E
+:204D2000245D3A4FC901DF91CF9108950F931F93CF93DF938C01FC0184818823C9F1838112
+:204D400087FF33C0C80161E00E947926EC01009779F1F8018481823040F482899389A48969
+:204D6000B5898C8F9D8FAE8FBF8FF80186899789A08DB18D9B8F8A8FCD01AA27BB279D8BA4
+:204D80008C8BE091C50BF091C60B309751F0BE016A5E7F4FCE0148960995888D998D9B8B60
+:204DA0008A8BF80183818F7783830E94C80001C080E0DF91CF911F910F910895CF93DF93B9
+:204DC000EC010E949626882311F01C8281E0DF91CF9108956F927F928F929F92AF92BF927A
+:204DE000CF92DF92EF92FF920F931F93DF93CF9300D000D0CDB7DEB77C015A016B01DC01CD
+:204E000014968C911497813009F08FC013968C9181FF8BC09EC00097A105B10511F481E0DF
+:204E200085C0F7016184728483849484C701B601A5010E94D025882309F477C0D7015A96D8
+:204E40008D919C915B97A114B104C104D10489F456964D915D916D917C9159970E94B60386
+:204E6000882309F462C0F701168A178A108E118E3DC0D70115964D915D916D917C911897EC
+:204E80009E012F5F3F4F0E944303882309F44DC0D7015A96ED91FC915B9749815A816B8164
+:204EA0007C818789803129F088EF9FEFAFEFBFE004C088EF9FEFA0E0B0E0481759076A07D0
+:204EC0007B07A0F4CF010E94B603882371F1F7014581568167817085828D938D0FEF1FEFD7
+:204EE0002FEF3FE00E94A7028823F9F0D7015296AD92BD92CD92DC92559713968C9113971F
+:204F0000806813968C93C7010E949626882369F0B601A5016A147B048C049D0410F4B40173
+:204F2000A301C7010E94D02501C080E00F900F900F900F90CF91DF911F910F91FF90EF90A3
+:204F4000DF90CF90BF90AF909F908F907F906F900895F70182899389A489B589841795073B
+:204F6000A607B70708F057CFE0CF2F923F924F925F926F927F928F929F92AF92BF92CF923E
+:204F8000DF92EF92FF920F931F93CF93DF93EC012A018C81882309F4C6C08B8180FFC3C005
+:204FA000D8C025014E185F084B0175E0272E312C2C0E3D1E3201B1C029853A854B855C85BC
+:204FC0006FEFA62E61E0B62EA222B3228C8179018A0199E016950795F794E7949A95D1F712
+:204FE000EA8DFB8D823049F4428D538D648D758D4E0D5F1D601F711F3FC0D480DA94DE207C
+:20500000A114B104D9F4DD20C9F4211531054105510549F48E899F89A88DB98D8D839E836F
+:20502000AF83B8870BC04D815E816F817885CF0191010E944303882309F475C0EA8DFB8D74
+:205040004D815E816F8178854250504060407040058404C0440F551F661F771F0A94D2F7AE
+:2050600086859785A089B189480F591F6A1F7B1F4D0D511D611D711D80E092E08A199B09C7
+:2050800063018615970508F46C018B8186FD06C080E0C81682E0D80609F073C08091AD034C
+:2050A0009091AE03A091AF03B091B003481759076A077B0709F465C080912C0790912D07DA
+:2050C0009501860174010E941324882361F18C0C9D1C12C09501245D3A4FA9014C0D5D1D28
+:2050E000D401F90102C081918D93E417F507D9F7E21BF30B8E0E9F1E960140E050E08985DD
+:205100009A85AB85BC85820F931FA41FB51F89879A87AB87BC876C187D086114710409F093
+:205120004BCFA20102C04FEF5FEFCA01DF91CF911F910F91FF90EF90DF90CF90BF90AF900F
+:205140009F908F907F906F905F904F903F902F900895AA88BB88CC88DD88E984FA840B8551
+:205160001C859A0140E050E0D601C5018E199F09A00BB10B82179307A407B50708F411CFDA
+:2051800013CFCB01BA0140E00E942701882309F0A1CFC9CFCF92DF92EF92FF920F931F93D8
+:2051A000DF93CF930F92CDB7DEB76C01FC018481823070F0E184F28403851485C601BE015E
+:2051C0006F5F7F4F41E050E00E94B527019769F020E030E0C9010F90CF91DF911F910F91DA
+:2051E000FF90EF90DF90CF900895F60181859285A385B4854F96A11DB11D81879287A38705
+:20520000B487F5E016950795F794E794FA95D1F78E2D8F70282F30E0E5E0220F331FEA95F2
+:20522000E1F7245D3A4FD6CFDF93CF9300D00F92CDB7DEB7282F6AE00E94A437805D898382
+:20524000822F0E94A437905D9A831B8287EC9BE00E942B2FBE016F5F7F4F0E9445330F907B
+:205260000F900F90CF91DF910895EF92FF920F931F938C01812F8695869586950E941429B0
+:20528000B7ECEB2EBBE0FB2EC7010E942B2F6AE30E943D33C801A5E096958795AA95E1F7BF
+:2052A0008F730E941429C7010E942B2F6AE30E943D330F711070802F880F0E9414291F9115
+:2052C0000F91FF90EF900895EF92FF920F931F937C0107EC1BE0C8010E942B2FB701672F9F
+:2052E000772766956454784F0E942634C8010E942B2F6DE20E943D33C70125E09695879560
+:205300002A95E1F78F700E941429C8010E942B2F6DE20E943D338E2D8F710E9414291F91A8
+:205320000F91FF90EF900895DF92EF92FF920F931F93CF93DF937C01D62EEC0110E000E039
+:205340008881803291F0083041F487EC9BE00E942B2F6EE20E943D331F5F87EC9BE00E944A
+:205360002B2F68810E9435331F5F0F5F21960B3039F7F70183858871803171F487EC9BE0D5
+:205380000E942B2F6FE205C087EC9BE00E942B2F60E20E943D331F5F1D15B0F3DF91CF919A
+:2053A0001F910F91FF90EF90DF9008950F931F93CF93DF93EC018B018C81882309F041C030
+:2053C000FB0187898031B1F482E08C831E8A1F8A188E198E808D918DA0E0B0E045E0880F65
+:2053E000991FAA1FBB1F4A95D1F78A8B9B8BAC8BBD8B15C0803229F583E08C83FB01428D0F
+:20540000538D648D758D4E8B5F8B688F798F9E012E5E3F4FC8010E940F04882391F01B8FEA
+:205420000A8F81E08B831D821E821F82188619861A861B861C861D861E861F86188A198A33
+:2054400001C080E0DF91CF911F910F9108951F93CF93DF93EC01142FE62FF0E075E0EE0F81
+:20546000FF1F7A95E1F7E45DFA4F8385817121F0842F827409F054C0698B8091AD03909106
+:20548000AE03A091AF03B091B0038D879E87AF87B88B84899589A0E0B0E0BC0155274427F3
+:2054A000828D938DA0E0B0E0482B592B6A2B7B2B4E8B5F8B688F798F838590E08871907048
+:2054C000009751F4848D958DA68DB78D8A8B9B8BAC8BBD8B81E00CC0409711F59E012E5E57
+:2054E0003F4F8A8D9B8D0E940F048823C9F084E08C83812F8F708B831D821E821F82188618
+:2055000019861A861B861C8616FD02C081E009C0CE0140E050E060E070E00E94EA2601C0E8
+:2055200080E0DF91CF911F910895DF92EF92FF920F931F93CF93DF938C01EB017A01D22E4F
+:20554000FC018481882341F5822F8073803321F18A8D9B8D938F828FE5E0440F551FEA9582
+:20556000E1F760E070E0CE010E94D0258823A1F0CE010E94CA28FC01009771F0808188231D
+:2055800059F0853E49F08E3239F06E2D6F70C8014D2D0E94272A01C080E0DF91CF911F918C
+:2055A0000F91FF90EF90DF9008952F923F924F925F926F927F928F929F92AF92BF92CF9276
+:2055C000DF92EF92FF920F931F93DF93CF93CDB7DEB76E970FB6F894DEBF0FBECDBF6C014E
+:2055E0006D8FE42EFC0115821682178210861186128613861486862F90E063E0462E512C87
+:205600004822592251E0652E712C6822792244E0842E912C882299221E010894211C311CE2
+:205620002E2D2E5F2E8F32E0A32EB12CA822B9228BC0F8018081882309F48DC0853E09F466
+:2056400083C08E3209F480C0838583FD7DC0FF2408C087EC9BE00E942B2F60E20E943D331C
+:20566000F394FE14B1F74114510411F460E001C06EE0C8010E9494296114710489F0F80167
+:20568000808D918D0E94642987EC9BE00E942B2F60E20E943D33F801868997890E9435294A
+:2056A000F801838588718031A9F0A114B10491F087EC9BE00E942B2F60E20E943D3387EC0A
+:2056C0009BE00E942B2FF801448D558D668D778D0E94173487EC9BE00E942B2F0E945D33A7
+:2056E0008114910489F1F80183858871803161F5F60181859285A385B48525E0B695A79504
+:20570000979587952A95D1F78C0184E295E09A8389831D82A80141505040C101B60121E046
+:205720000E94952A882329F0C1016D8D4E8D0E94D52A85E0000F111F8A95E1F7A80160E088
+:2057400070E0C6010E94D025C6010E94CA288C01009709F06ECF6E960FB6F894DEBF0FBE27
+:20576000CDBFCF91DF911F910F91FF90EF90DF90CF90BF90AF909F908F907F906F905F90C8
+:205780004F903F902F900895CF93DF93EC019C012B5F3F4F8A8D9B8D41E050E060E070E069
+:2057A0000E947E048823A1F08E899F89A88DB98D0097A105B10559F48D819E81AF81B885F5
+:2057C0008E8B9F8BA88FB98F8B8180688B8381E0DF91CF9108952F923F924F925F927F92C2
+:2057E0008F929F92AF92BF92CF92DF92EF92FF920F931F93DF93CF9300D000D000D0CDB7C5
+:20580000DEB76C01162F072F5E834D83DC0114968C911497813009F075C113968C9181FFE5
+:2058200071C18FC1F601428953896489758981859285A385B48584179507A607B70731F01C
+:20584000C6010E94D025882309F45CC1812F902F9C0129012D803E8019C1D6015A96ED9165
+:20586000FC915B97DA01C90119E0B695A795979587951A95D1F774807A9478227FEF872E06
+:2058800071E0972E82229322772009F050C08114910409F04CC0D60115964D915D916D917E
+:2058A0007C911897411551056105710581F456968D919D910D90BC91A02D0097A105B1054D
+:2058C00059F1F60185839683A783B08730C0CF019E012F5F3F4F0E944303882319F440E0CA
+:2058E00050E016C129813A814B815C81D6015A96ED91FC915B978789803129F088EF9FEFF0
+:20590000AFEFBFE004C088EF9FEFA0E0B0E0281739074A075B0730F0C6010E94C42B88231C
+:2059200031F4F0C0F601258336834783508780E092E08819990951018215930508F45C01AA
+:20594000D6015A96ED91FC915B971596ED90FD900D911C9118978EEF9FEFAFEFBFEFE80E87
+:20596000F91E0A1F1B1F058404C0EE0CFF1C001F111F0A94D2F786859785A089B189E80E15
+:20598000F91E0A1F1B1FE70CF11C011D111D90E0A91692E0B90651F58091AD039091AE0308
+:2059A000A091AF03B091B0038E159F05A007B10761F48FEF9FEFAFEFBFEF8093AD039093D7
+:2059C000AE03A093AF03B093B00380912C0790912D07B801A70192010E94B623882309F48B
+:2059E00091C0A0E0B2E04A0E5B1E3EC08114910409F5F60121853285438554858289938931
+:205A0000A489B589281739074A075B0798F00E94C800882309F476C0E092AD03F092AE03C4
+:205A20000093AF031093B00380912E07816080932E0708C0C801B70141E00E94270188237D
+:205A400009F460C09401245D3A4FA9014A0D5B1DD201F90102C08D918193E417F507D9F789
+:205A6000E21BF30B4E0E5F1E2A183B08950140E050E0F60181859285A385B485820F931F2F
+:205A8000A41FB51F81879287A387B487D60119962D913D914D915C911C972114310409F0F6
+:205AA000DCCE52968D919D910D90BC91A02D82179307A407B50748F4F601228B338B448B4A
+:205AC000558B83818068838311C08091C50B9091C60B892B59F08D819E81892B39F0D60172
+:205AE00013968C911397806813968C93F601838183FF05C0C6010E949626882319F04D819D
+:205B00005E8106C081E0D60112968C934FEF5FEFCA0126960FB6F894DEBF0FBECDBFCF9127
+:205B2000DF911F910F91FF90EF90DF90CF90BF90AF909F908F907F905F904F903F902F9082
+:205B4000089582FD6FCE82CEDB010D900020E9F71197A61BB70BAD010E94EB2B0895DF9383
+:205B6000CF930F92CDB7DEB76983BE016F5F7F4F41E050E00E94EB2B0F90CF91DF910895AD
+:205B8000DF92EF92FF920F931F93CF93DF93EC010E94C42B882309F44EC0EA8DFB8DED80BA
+:205BA000FE800F8118858EEF9FEFAFEFBFEFE80EF91E0A1F1B1F058404C0EE0CFF1C001FF2
+:205BC000111F0A94D2F786859785A089B189E80EF91E0A1F1B1FD4800894E108F108010958
+:205BE00011090BC0C801B7016D0D711D811D911D0E9403018823F9F0DA94DD2099F7EA8D3F
+:205C0000FB8D20E032E040E050E0058404C0220F331F441F551F0A94D2F78A899B89AC8920
+:205C2000BD89820F931FA41FB51F8A8B9B8BAC8BBD8B81E001C080E0DF91CF911F910F91E8
+:205C4000FF90EF90DF9008956F927F928F929F92AF92BF92CF92DF92EF92FF920F931F93FC
+:205C6000DF93CF93CDB7DEB72B970FB6F894DEBF0FBECDBF5C016B01822EDC0114968C9111
+:205C8000882309F0D2C0CA018E010F5F1F4FB8010E948F25882309F4C8C0F601828D938D33
+:205CA000D5015B969C938E935A971582168217821086D60119961D921D921D921C921C978A
+:205CC0009924380141C0C6010E94CA28FC01009709F4ABC085E016950795F794E7948A95A5
+:205CE000D1F71E2D1F708081882311F0853ED1F4992091F4D50151961C9351972091AD03DA
+:205D00003091AE034091AF035091B0031D962D933D934D935C9350978081882399F499240B
+:205D2000939421C0C301BF014BE050E00E94C93A009751F4882D8073803309F476C0C501A7
+:205D4000612F6FC099249394F601E184F2840385148582899389A489B589E816F9060A070D
+:205D60001B0708F4B0CF882D8271823109F05DC0992041F0C50161E00E9479268C010097BF
+:205D800089F453C0D60114968C91823009F44DC0C6010E94C02D882309F447C0F501118A83
+:205DA0000CE215E080E2D8011D928A95E9F7D801FE0131968BE001900D928150E1F7E091C3
+:205DC000C50BF091C60B309739F0B801625F7F4FC8014096099509C081E298E2F801918B71
+:205DE000808B80E098E097878687D80150968D919C91519753969C938E93529759969C9363
+:205E00008E9358971E968D919C911F9757969C938E9356970E94C800882339F0C501F50199
+:205E20006189482D0E94272A01C080E02B960FB6F894DEBF0FBECDBFCF91DF911F910F91C7
+:205E4000FF90EF90DF90CF90BF90AF909F908F907F906F900895FC018081918108958EEDB7
+:205E60009CE09093C80B8093C70B0895CB01642F0E94033B0895CB010E94FB3A08959091F1
+:205E8000CF049295990F990F907C8770892B80937C0080917A00806480937A0080917A00EB
+:205EA00086FDFCCF2091780040917900942F80E030E0282B392BC90108951F93CF93DF934A
+:205EC000182FEB0161E00E94B22FE12FF0E0E552FF4F8491833051F48091800080688093CD
+:205EE0008000D0938900C093880038C0843051F480918000806280938000D0938B00C09323
+:205F00008A002CC0813029F484B5806884BDC7BD25C0823029F484B5806284BDC8BD1EC0E4
+:205F2000863041F48091B00080688093B000C093B30014C0873041F48091B000806280938E
+:205F4000B000C093B4000AC0C038D1051CF4812F60E002C0812F61E00E94D02FDF91CF91CE
+:205F60001F910895282F30E0C90185549F4FFC01949125563F4FF9018491882381F0E82F6F
+:205F8000F0E0E457FF4FE491F0E0662329F4808190958923808308958081892B80830895F6
+:205FA000482F50E0CA0185529F4FFC012491CA0185549F4FFC01949145565F4FFA0134913B
+:205FC0003323C1F1222331F1213019F484B58F7704C0223021F484B58F7D84BD1BC02330D6
+:205FE00021F4809180008F7705C0243031F4809180008F7D809380000DC0263021F480913E
+:20600000B0008F7705C0273029F48091B0008F7D8093B000E32FF0E0EF56FF4FE491F0E047
+:20602000662329F4808190958923808308958081892B808308951F920F920FB60F92112406
+:206040002F938F939F93AF93BF938091CA0B9091CB0BA091CC0BB091CD0B0196A11DB11D75
+:206060008093CA0B9093CB0BA093CC0BB093CD0B8091CE0B9091CF0BA091D00BB091D10B0C
+:206080008050904CAF4FBF4F8093CE0B9093CF0BA093D00BB093D10B27C08091CE0B909140
+:2060A000CF0BA091D00BB091D10B80589E43A040B0408093CE0B9093CF0BA093D00BB0931A
+:2060C000D10B8091D20B9091D30BA091D40BB091D50B0196A11DB11D8093D20B9093D30BB2
+:2060E000A093D40BB093D50B8091CE0B9091CF0BA091D00BB091D10B81589E43A040B040D3
+:2061000060F6BF91AF919F918F912F910F900FBE0F901F9018958FB7F8942091D20B309101
+:20612000D30B4091D40B5091D50B8FBFB901CA0108956FB7F89486B590E0A0E0B0E0A89BF0
+:2061400008C00097A105B10521F480E091E0A0E0B0E02091CA0B3091CB0B4091CC0B5091E8
+:20616000CD0B6FBF542F432F322F2227280F391F4A1F5B1F82E0220F331F441F551F8A952D
+:20618000D1F7B901CA010895789484B5826084BD84B5816084BD85B5826085BD85B58160D9
+:2061A00085BDEEE6F0E0808181608083E1E8F0E0808182608083808181608083E0E8F0E018
+:2061C000808181608083E1EBF0E0808184608083E0EBF0E0808181608083EAE7F0E0808134
+:2061E000846080838081826080838081816080838081806880831092C10008950F931F93AD
+:20620000CF93DF93482FFB018B0100581F4FD801CD91DC91CE01019660E870E00E94B037BA
+:206220009C01EE57FF4F80819181E258F0402817390731F0CE0FDF1F4883F801318320831B
+:20624000DF91CF911F910F9108951F920F920FB60F9211242F933F934F935F936F937F9318
+:206260008F939F93AF93BF93EF93FF938091C60066ED7BE00E94FE30FF91EF91BF91AF912D
+:206280009F918F917F916F915F914F913F912F910F900FBE0F901F9018951F920F920FB6C0
+:2062A0000F9211242F933F934F935F936F937F938F939F93AF93BF93EF93FF938091CE00B1
+:2062C0006AE57CE00E94FE30FF91EF91BF91AF919F918F917F916F915F914F913F912F91E3
+:2062E0000F900FBE0F901F9018955F926F927F928F929F92AF92BF92CF92DF92EF92FF926C
+:206300000F931F93CF93DF93EC013A014B01413482E458078FE0680780E078070CF07FC0AF
+:2063200060E874E88EE190E0A40193010E94E5372150304040405040CA01B90122E030E05B
+:2063400040E050E00E94E53759016A01A6019501209530954095509594E0220F331F441F9F
+:20636000551F9A95D1F760E074E284EF90E00E94E537CA01B9012FEF30E040E050E00E94D6
+:206380008537A40193010E94E537C90181509F4F181619061CF4522E5A9403C055245394D3
+:2063A000521A60E079E08DE390E0A40193010E94E5372150304040405040CA01B90122E089
+:2063C00030E040E050E00E94E537209530954095509583E0220F331F441F551F8A95D1F7C7
+:2063E00060E074E284EF90E00E94E537CA01B9012FEF30E040E050E00E948537A4019301CC
+:206400000E94E537C90181509F4F181619061CF4822F815002C081E0821B851500F5E8859A
+:20642000F98581E090E00A8802C0880F991F0A94E2F7808360E079E08DE390E0A40193013E
+:206440000E94E5372150304040405040CA01B90122E030E040E050E00E94E53704C0E885B7
+:20646000F98510829501EC81FD813083EE81FF812083EA85FB85208141E050E0CA010E8408
+:2064800002C0880F991F0A94E2F7282B2083EA85FB852081CA010F8402C0880F991F0A94E1
+:2064A000E2F7282B2083EA85FB858081088802C0440F551F0A94E2F7842B8083DF91CF910B
+:2064C0001F910F91FF90EF90DF90CF90BF90AF909F908F907F906F905F900895DC01129695
+:2064E000ED91FC911397E058FF4F8191919180589F4F20813181821B930B60E870E00E949F
+:20650000B0370895CF93DF93DC011296ED91FC911397EF01CE57DF4F48815981E058FF4F7D
+:2065200080819181E058F0408417950719F42FEF3FEF0CC0E40FF51F2081CA01019660E832
+:2065400070E00E94B0379983888330E0C901DF91CF910895DC011296ED91FC911397EE5775
+:20656000FF4F80819181929382930895FC01A085B18521898C9190E0022E02C0959587951C
+:206580000A94E2F780FFF6CF0484F585E02D608308952AE235E03093DF0C2093DE0C86EDD2
+:2065A0009BE09093E10C8093E00C85EC90E09093E30C8093E20C84EC90E09093E50C809356
+:2065C000E40C80EC90E09093E70C8093E60C81EC90E09093E90C8093E80C86EC90E09093D3
+:2065E000EB0C8093EA0CE4E0E093EC0C73E07093ED0C67E06093EE0C55E05093EF0C41E0B5
+:206600004093F00C3093F20C2093F10C8AE59CE09093F40C8093F30C8DEC90E09093F60C0C
+:206620008093F50C8CEC90E09093F80C8093F70C88EC90E09093FA0C8093F90C89EC90E017
+:206640009093FC0C8093FB0C8EEC90E09093FE0C8093FD0CE093FF0C7093000D6093010D33
+:206660005093020D4093030D0895DC01ED91FC910190F081E02D09950895DC01ED91FC918E
+:206680000190F081E02D099508950F931F93CF93DF938C01EB0109C02196D801ED91FC91AB
+:2066A0000190F081E02DC801099568816623A1F7DF91CF911F910F9108950F931F938C01BC
+:2066C000DC01ED91FC910190F081E02D6DE00995D801ED91FC910190F081E02DC8016AE0D2
+:2066E00009951F910F9108950F931F938C010E944533C8010E945D331F910F9108952F926B
+:206700003F924F925F926F927F928F929F92AF92BF92CF92DF92EF92FF920F931F93DF9336
+:20672000CF93CDB7DEB7A0970FB6F894DEBF0FBECDBF1C016A017B014115510561057105D4
+:2067400049F4DC01ED91FC910190F081E02D60E3099554C0882499245401422E55246624DF
+:20676000772401E010E00C0F1D1F080D191DC701B601A30192010E94C337F8016083089441
+:20678000811C911CA11CB11CC701B601A30192010E94C337C901DA016C017D01C114D10499
+:2067A000E104F104F1F681E0E82EF12CEC0EFD1EE80CF91C3E010894611C711CD501C401E6
+:2067C0000197A109B1096C01C818D90814C0F601EE0DFF1D60816A3010F4605D01C0695CEB
+:2067E000D101ED91FC910190F081E02DC10109950894E108F1086E147F0449F7A0960FB68F
+:20680000F894DEBF0FBECDBFCF91DF911F910F91FF90EF90DF90CF90BF90AF909F908F901E
+:206820007F906F905F904F903F902F9008952AE00E947F3308950F931F938C010E94173488
+:20684000C8010E945D331F910F910895AB0160E070E00E9417340895EF92FF920F931F9324
+:20686000CF93DF93EC017A018B0177FF0FC0E881F9810190F081E02D6DE2099510950095F2
+:20688000F094E094E11CF11C011D111DCE01B801A7012AE00E947F33DF91CF911F910F91FC
+:2068A000FF90EF9008950F931F938C010E942C34C8010E945D331F910F910895AB01662729
+:2068C00057FD6095762F0E942C3408950F931F938C01AB01662757FD6095762F0E942C3421
+:2068E000C8010E945D331F910F9108959F92AF92BF92CF92DF92EF92FF920F931F93CF9353
+:20690000DF93EC017A018B01922ECB01BA0120E030E040E050E00E947835882364F4E881AF
+:20692000F9810190F081E02DCE016DE2099517FB109517F9109520E030E040E05FE3AA2466
+:206940000BC0CA01B90120E030E040E251E40E947C359B01AC01A394A91498F3C801B701E4
+:206960000E9414355B016C010E94E9357B018C01CE01B801A7010E941734992029F0CE01DC
+:206980006BEA73E00E944533C801B7010E9415369B01AC01C601B5010E94133520C020E037
+:2069A00030E040E251E40E94CB367B018C010E94E4355B016C01CC24B7FCC094DC2CCE0172
+:2069C000B601A5010E942C34C601B5010E9417369B01AC01C801B7010E9413359A94992051
+:2069E000F1F6DF91CF911F910F91FF90EF90DF90CF90BF90AF909F90089522E00E9476340C
+:206A000008950F931F938C010E94FD34C8010E945D331F910F9108950E94C4300E94E02104
+:206A20000E94601EFDCF5058BB27AA270ED075C166D130F06BD120F031F49F3F11F41EF43E
+:206A40005BC10EF4E095E7FB51C1E92F77D180F3BA17620773078407950718F071F49EF501
+:206A60008FC10EF4E0950B2EBA2FA02D0B01B90190010C01CA01A0011124FF27591B99F038
+:206A8000593F50F4503E68F11A16F040A22F232F342F4427585FF3CF469537952795A795CA
+:206AA000F0405395C9F77EF41F16BA0B620B730B840BBAF09150A1F0FF0FBB1F661F771FF9
+:206AC000881FC2F70EC0BA0F621F731F841F48F4879577956795B795F7959E3F08F0B3CF7A
+:206AE0009395880F08F09927EE0F979587950895D9D008F481E008950CD00FC107D140F0E6
+:206B0000FED030F021F45F3F19F0F0C0511139C1F3C014D198F39923C9F35523B1F3951B58
+:206B2000550BBB27AA2762177307840738F09F5F5F4F220F331F441FAA1FA9F333D00E2E66
+:206B40003AF0E0E830D091505040E695001CCAF729D0FE2F27D0660F771F881FBB1F261794
+:206B600037074807AB07B0E809F0BB0B802DBF01FF2793585F4F2AF09E3F510568F0B6C038
+:206B800000C15F3FECF3983EDCF3869577956795B795F7959F5FC9F7880F911D96958795CD
+:206BA00097F90895E1E0660F771F881FBB1F621773078407BA0720F0621B730B840BBA0BBD
+:206BC000EE1F88F7E095089504D06894B111D9C00895BCD088F09F5790F0B92F9927B75125
+:206BE000A0F0D1F0660F771F881F991F1AF0BA95C9F712C0B13081F0C3D0B1E00895C0C05C
+:206C0000672F782F8827B85F39F0B93FCCF3869577956795B395D9F73EF4909580957095E5
+:206C200061957F4F8F4F9F4F0895E89409C097FB3EF490958095709561957F4F8F4F9F4F4F
+:206C40009923A9F0F92F96E9BB279395F695879577956795B795F111F8CFFAF4BB0F11F4AD
+:206C600060FF1BC06F5F7F4F8F4F9F4F16C0882311F096E911C0772321F09EE8872F762F14
+:206C800005C0662371F096E8862F70E060E02AF09A95660F771F881FDAF7880F96958795D8
+:206CA00097F90895990F0008550FAA0BE0E8FEEF16161706E807F907C0F012161306E40715
+:206CC000F50798F0621B730B840B950B39F40A2661F0232B242B252B21F408950A2609F48C
+:206CE000A140A6958FEF811D811D089597F99F6780E870E060E008959FEF80EC089500243B
+:206D00000A941616170618060906089500240A9412161306140605060895092E0394000C23
+:206D200011F4882352F0BB0F40F4BF2B11F460FF04C06F5F7F4F8F4F9F4F089557FD905810
+:206D4000440F551F59F05F3F71F04795880F97FB991F61F09F3F79F08795089512161306D5
+:206D60001406551FF2CF4695F1DF08C0161617061806991FF1CF8695710561050894089542
+:206D8000E894BB2766277727CB0197F908958ADF08F48FEF08950BD0C0CFB1DF28F0B6DF4A
+:206DA00018F0952309F0A2CFA7CF1124EACFC6DFA0F3959FD1F3950F50E0551F629FF001DB
+:206DC000729FBB27F00DB11D639FAA27F00DB11DAA1F649F6627B00DA11D661F829F222794
+:206DE000B00DA11D621F739FB00DA11D621F839FA00D611D221F749F3327A00D611D231F21
+:206E0000849F600D211D822F762F6A2F11249F5750408AF0E1F088234AF0EE0FFF1FBB1FD5
+:206E2000661F771F881F91505040A9F79E3F510570F05CCFA6CF5F3FECF3983EDCF386950F
+:206E400077956795B795F795E7959F5FC1F7FE2B880F911D9695879597F9089511F40EF437
+:206E60004BCF3EC073DFD0F39923D9F3CEF39F57550B87FF43D00024A0E640EA9001805870
+:206E80005695979528F4805C660F771F881F20F026173707480730F4621B730B840B202960
+:206EA00031294A2BA69517940794202531254A2758F7660F771F881F20F02617370748079B
+:206EC00030F4620B730B840B200D311D411DA09581F7B901842F9158880F969587950895BD
+:206EE0009F3F31F0915020F4879577956795B795880F911D9695879597F908959150504004
+:206F0000660F771F881FD2F70895629FD001739FF001829FE00DF11D649FE00DF11D929F39
+:206F2000F00D839FF00D749FF00D659FF00D9927729FB00DE11DF91F639FB00DE11DF91FAB
+:206F4000BD01CF0111240895991B79E004C0991F961708F0961B881F7A95C9F780950895CA
+:206F600097FB092E07260AD077FD04D049D006D000201AF4709561957F4F0895F6F7909564
+:206F800081959F4F0895A1E21A2EAA1BBB1BFD010DC0AA1FBB1FEE1FFF1FA217B307E407F3
+:206FA000F50720F0A21BB30BE40BF50B661F771F881F991F1A9469F760957095809590959F
+:206FC0009B01AC01BD01CF01089597FB092E05260ED057FD04D0D7DF0AD0001C38F4509586
+:206FE0004095309521953F4F4F4F5F4F0895F6F790958095709561957F4F8F4F9F4F089571
+:20700000AA1BBB1B51E107C0AA1FBB1FA617B70710F0A61BB70B881F991F5A95A9F7809538
+:207020009095BC01CD010895EE0FFF1F0590F491E02D0994A0E0B0E0E0E2F8E30C941A3B82
+:20704000EC015B016115710519F0FB0191838083F9908F2D90E00E94A93A892BC9F7FDE252
+:20706000FF1621F4F990EE24E39405C02BE2F21609F4F990EE248E0101501040C8016BEF0F
+:2070800070E043E050E00E94B13A892B01F58E010E5F1F4FC8016EEF70E045E050E00E943F
+:2070A000B13A892B19F48E01095F1F4FA114B10419F0F50111830083E0FC04C070E090E0DF
+:2070C00080E814C070E090E080E86FEF08C1C80163E071E043E050E00E94B13A892B69F4D7
+:2070E000A114B10429F470E090E080EC6FE7F7C02296F501D183C083F6CF8824992440E03D
+:2071000050E060E070E0EF2DE053EA30A0F5F2E0EF2A8E2D90E09C0128703070E2FE06C020
+:20712000232B79F50894811C911C2BC0232B19F0089481089108DB01CA0112E0880F991FC5
+:20714000AA1FBB1F1A95D1F7480F591F6A1F7B1F440F551F661F771F4E0F511D611D711D6A
+:20716000483929E9520729E9620729E1720748F084E0E82A06C0EE3F31F4E3FC39C098E00F
+:20718000E92AF990C0CFE53311F0E53189F529912D3219F4E0E1EE2A05C02B3219F081E08C
+:2071A00090E003C0299182E090E0E22FE053EA3018F0C81BD90B1CC020E030E0FCE02038C3
+:2071C0003F075CF4C901880F991F880F991F280F391F220F331F2E0F311DE991E053EA304D
+:2071E00068F3E4FE03C0309521953F4F820E931ECE2CDD24E1FE07C0A114B10421F0219771
+:20720000F501D183C083CB01BA010E9415367B018C01F3E0CF22DD2423E0C216D10421F4DA
+:2072200017FB109517F9109557016801C801B70120E030E040E050E00E947835882309F449
+:207240004AC097FE0DC02AE1E22E21E0F22E9094819491089394C0E2D0E000E010E012C099
+:2072600092E3E92E91E0F92EF6CFF7012591359145915491C601B5010E94CB365B016C010D
+:207280008C1A9D0A8C169D0684F7D595C7950F5F1F4F0630110529F08CEF9FEFE80EF91EC4
+:2072A000F1CFC501D6017C018D018C2D880F8D2D881F8F3F51F0C601B50120E030E040E0F9
+:2072C00050E00E947835882331F482E290E09093050D8093040D7E2D9F2D802F612F272F26
+:2072E000392F482F562FB901CA01CDB7DEB7ECE00C94363B2F923F925F926F927F928F92F9
+:207300009F92AF92BF92CF92DF92EF92FF920F931F93CF93DF938C011B01EA01611571051E
+:2073200019F0FB0191838083209749F0CE010297839728F020E030E040E050E0F6C0F80198
+:20734000A1908F018A2D90E00E94A93A892BB9F7FDE2AF1631F4F801A1908F015524539479
+:2073600007C0FBE2AF1619F4F801A1908F015524209719F0C031D105C1F4F0E3AF1679F423
+:20738000F8018081883711F0883549F4F801A1800E5F1F4FF2E05F2AC0E1D0E006C020971B
+:2073A00021F480E3A816E9F427C0C830D10531F1C930D10524F4C230D10531F50CC0CA3048
+:2073C000D10589F0C031D105F9F4C12CD12CE12CB8E0FB2E28C0C12CD12CE12CA0E4FA2E67
+:2073E00022C0CAE0D0E0FCECCF2EFCECDF2EFCECEF2EFCE0FF2E17C0C8E0D0E0C12CD12C50
+:20740000E12CE0E1FE2E0FC09E01442737FD4095542F60E070E080E090E80E94C337C9013F
+:20742000DA016C017D0120E030E040E050E060E03E01882477FC8094982C70EDB72EBA0CA8
+:20744000E9E0EB1570F48A2D81548A3118F499ECB92E06C08A2D81568A3150F589EAB82E93
+:20746000BA0C8B2D90E08C179D0714F56F3FE1F0C216D306E406F506B0F0CA01B901A401EF
+:2074800093010E9485379B01AC012B0D311D411D511D2130F0E03F07F0E04F07F0E85F0794
+:2074A00010F461E001C06FEFF801A1908F01C5CF2114310481F0662331F001501040F10102
+:2074C0001183008308C051FE1AC002501040F1011183008314C067FF12C050FC05C02FEFBE
+:2074E0003FEF4FEF5FE704C020E030E040E050E882E290E09093050D8093040D16C050FE5D
+:2075000008C050954095309521953F4F4F4F5F4F0CC057FF0AC082E290E09093050D80938C
+:20752000040D2FEF3FEF4FEF5FE7B901CA01DF91CF911F910F91FF90EF90DF90CF90BF909A
+:20754000AF909F908F907F906F905F903F902F900895911166C0803219F089508550D0F77E
+:207560000895FB01DC014150504088F08D9181341CF08B350CF4805E659161341CF06B3548
+:207580000CF4605E861B611171F3990B0895881BFCCFFB01DC0104C08D910190801921F40D
+:2075A00041505040C8F7881B990B0895FC018191861721F08823D9F7992708953197CF01DA
+:2075C0000895FB0151915523A9F0BF01DC014D9145174111E1F759F4CD010190002049F019
+:2075E0004D9140154111C9F3FB014111EFCF81E090E001970895F999FECF92BD81BDF89ABA
+:20760000992780B50895262FF999FECF1FBA92BD81BD20BD0FB6F894FA9AF99A0FBE019605
+:2076200008959927882708952F923F924F925F926F927F928F929F92AF92BF92CF92DF9275
+:20764000EF92FF920F931F93CF93DF93CDB7DEB7CA1BDB0B0FB6F894DEBF0FBECDBF099428
+:207660002A88398848885F846E847D848C849B84AA84B984C884DF80EE80FD800C811B8198
+:20768000AA81B981CE0FD11D0FB6F894DEBF0FBECDBFED010895F894FFCF45433A002C2080
+:2076A000006F6B00526573656E643A00534420696E6974206661696C00766F6C756D652E68
+:2076C000696E6974206661696C6564006F70656E526F6F74206661696C656400533A0042C7
+:2076E0006567696E2066696C65206C69737400456E642066696C65206C6973740046696C7D
+:2077000065206F70656E65643A002053697A653A0046696C652073656C65637465640066EB
+:20772000696C652E6F70656E206661696C6564005344207072696E74696E672062797465B4
+:2077400020002F004E6F74205344207072696E74696E67006F70656E206661696C65642C04
+:207760002046696C653A20002E0057726974696E6720746F2066696C653A20006F6B205492
+:207780003A0020423A006F6B206F3A002C20703A002C20693A002C20643A00543A00464984
+:2077A000524D574152455F4E414D453A72727020555549443A00583A00593A005A3A00455E
+:2077C0003A00785F6D696E3A004C2000482000795F6D696E3A007A5F6D696E3A00004B70DE
+:2077E00020004B6920004B6420005049445F4D415820005049445F495F4D415820004E5AF2
+:207800004F4E452000556E6B6E6F776E20636F6D6D616E643A004D3131300053657269616A
+:207820006C204572726F723A204C696E65204E756D626572206973206E6F74204C61737426
+:20784000204C696E65204E756D6265722B312C204C617374204C696E653A004572726F72CF
+:207860003A20636865636B73756D206D69736D617463682C204C617374204C696E653A0023
+:207880004572726F723A204E6F20436865636B73756D2077697468206C696E65206E756D60
+:2078A0006265722C204C617374204C696E653A004572726F723A204E6F204C696E65204E96
+:2078C000756D626572207769746820636865636B73756D2C204C617374204C696E653A007C
+:2078E000446F6E65207072696E74696E672066696C65004D3239006572726F722077726963
+:2079000074696E6720746F2066696C6500446F6E6520736176696E672066696C652E007363
+:2079200074617274004F6B005344004572726F72004E6F204572726F7200486F74656E64E8
+:2079400000426564002E00FFFFFFFF71DBB64271DBB64200007A45008063440060EA4600F4
+:2079600060EA460000FA430060EA460080BB440080BB440000F0420000C8410000C841CD9B
+:20798000CC4C3E00002041E8030000E803000032000000FA000000F4010000F40100003212
+:2079A000000000F401000030303030303030302D303030302D303030302D303030302D302E
+:2079C000303030303030303030303000FF005000000000400AD7233C0000A04105000100E1
+:2079E000A10324002B014700F6006A00D9008D00C600B000B800D300AD00F600A300190125
+:207A00009A003C0193005F018C0082018600A5018000C8017A00EB0175000E02700031028A
+:207A20006B0054026600770261009A025B00BD025600E002510003034C00260346004903F9
+:207A40003F006C0338008F033000B2032600D5031700F80300008B038E0391039703A003C9
+:207A6000A70358595A4501DC05010100C0023600D8006B00AF00A0009800D50089000A019D
+:207A80007D003F01730074016A00A9016300DE015B001302550048024E007D024700B20214
+:207AA0004100E7023A001C03320051032A0086031F00BB031100F003000000000000AF2D4D
+:207AC00000000000B632FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8
+:207AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6
+:207B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85
+:207B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65
+:207B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45
+:207B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25
+:207B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05
+:207BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5
+:207BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5
+:207BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5
+:207C0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84
+:207C2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64
+:207C4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44
+:207C6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24
+:207C8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04
+:207CA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4
+:207CC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4
+:207CE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:207D0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83
+:207D2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63
+:207D4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43
+:207D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23
+:207D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03
+:207DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3
+:207DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3
+:207DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3
+:207E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82
+:207E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62
+:207E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42
+:207E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22
+:207E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02
+:207EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2
+:207EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2
+:207EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2
+:207F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81
+:207F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61
+:207F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41
+:207F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21
+:207F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01
+:207FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1
+:207FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1
+:207FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1
+:20800000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:20802000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:20804000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:20806000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20808000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00
+:2080A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:2080C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:2080E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20810000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:20812000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:20814000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:20816000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20818000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:2081A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:2081C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:2081E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20820000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:20822000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:20824000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:20826000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20828000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:2082A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:2082C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:2082E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20830000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:20832000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:20834000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:20836000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:20838000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:2083A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:2083C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:2083E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20840000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C
+:20842000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C
+:20844000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C
+:20846000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C
+:20848000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC
+:2084A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC
+:2084C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC
+:2084E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C
+:20850000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B
+:20852000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B
+:20854000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B
+:20856000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B
+:20858000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB
+:2085A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB
+:2085C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB
+:2085E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B
+:20860000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A
+:20862000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A
+:20864000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A
+:20866000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A
+:20868000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA
+:2086A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA
+:2086C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA
+:2086E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A
+:20870000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79
+:20872000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59
+:20874000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39
+:20876000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19
+:20878000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9
+:2087A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9
+:2087C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9
+:2087E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99
+:20880000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78
+:20882000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58
+:20884000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38
+:20886000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18
+:20888000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8
+:2088A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8
+:2088C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8
+:2088E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98
+:20890000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77
+:20892000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57
+:20894000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37
+:20896000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17
+:20898000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7
+:2089A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7
+:2089C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7
+:2089E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97
+:208A0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76
+:208A2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56
+:208A4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36
+:208A6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16
+:208A8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6
+:208AA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6
+:208AC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6
+:208AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96
+:208B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75
+:208B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55
+:208B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35
+:208B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15
+:208B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5
+:208BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5
+:208BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5
+:208BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95
+:208C0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74
+:208C2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54
+:208C4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34
+:208C6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14
+:208C8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4
+:208CA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4
+:208CC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4
+:208CE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94
+:208D0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73
+:208D2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53
+:208D4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33
+:208D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13
+:208D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3
+:208DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3
+:208DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3
+:208DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93
+:208E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72
+:208E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52
+:208E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32
+:208E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12
+:208E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2
+:208EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2
+:208EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2
+:208EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92
+:208F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71
+:208F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51
+:208F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31
+:208F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11
+:208F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1
+:208FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1
+:208FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1
+:208FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91
+:20900000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70
+:20902000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50
+:20904000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30
+:20906000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10
+:20908000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0
+:2090A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0
+:2090C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0
+:2090E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90
+:20910000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F
+:20912000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F
+:20914000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F
+:20916000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F
+:20918000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF
+:2091A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF
+:2091C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF
+:2091E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F
+:20920000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E
+:20922000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E
+:20924000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E
+:20926000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E
+:20928000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE
+:2092A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE
+:2092C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE
+:2092E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E
+:20930000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D
+:20932000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D
+:20934000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D
+:20936000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D
+:20938000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED
+:2093A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD
+:2093C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD
+:2093E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D
+:20940000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C
+:20942000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C
+:20944000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C
+:20946000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C
+:20948000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC
+:2094A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC
+:2094C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC
+:2094E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C
+:20950000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B
+:20952000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B
+:20954000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B
+:20956000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B
+:20958000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB
+:2095A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB
+:2095C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB
+:2095E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B
+:20960000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A
+:20962000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A
+:20964000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A
+:20966000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A
+:20968000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA
+:2096A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA
+:2096C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA
+:2096E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A
+:20970000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69
+:20972000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49
+:20974000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29
+:20976000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09
+:20978000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9
+:2097A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9
+:2097C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9
+:2097E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89
+:20980000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68
+:20982000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48
+:20984000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28
+:20986000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08
+:20988000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8
+:2098A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8
+:2098C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8
+:2098E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88
+:20990000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67
+:20992000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47
+:20994000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27
+:20996000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07
+:20998000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7
+:2099A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7
+:2099C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7
+:2099E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87
+:209A0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66
+:209A2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46
+:209A4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26
+:209A6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06
+:209A8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6
+:209AA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6
+:209AC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6
+:209AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86
+:209B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65
+:209B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45
+:209B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25
+:209B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05
+:209B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5
+:209BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5
+:209BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5
+:209BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85
+:209C0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64
+:209C2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44
+:209C4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24
+:209C6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04
+:209C8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4
+:209CA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4
+:209CC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:209CE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84
+:209D0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63
+:209D2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43
+:209D4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23
+:209D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03
+:209D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3
+:209DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3
+:209DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3
+:209DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83
+:209E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62
+:209E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42
+:209E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22
+:209E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02
+:209E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2
+:209EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2
+:209EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2
+:209EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82
+:209F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61
+:209F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41
+:209F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21
+:209F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01
+:209F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1
+:209FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1
+:209FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1
+:209FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81
+:20A00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:20A02000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:20A04000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20A06000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00
+:20A08000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20A0A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20A0C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20A0E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:20A10000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:20A12000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:20A14000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20A16000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20A18000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20A1A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20A1C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20A1E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:20A20000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:20A22000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:20A24000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20A26000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20A28000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20A2A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20A2C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20A2E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:20A30000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:20A32000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:20A34000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:20A36000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20A38000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20A3A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20A3C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20A3E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:20A40000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C
+:20A42000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C
+:20A44000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C
+:20A46000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC
+:20A48000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC
+:20A4A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC
+:20A4C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C
+:20A4E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C
+:20A50000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B
+:20A52000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B
+:20A54000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B
+:20A56000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB
+:20A58000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB
+:20A5A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB
+:20A5C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B
+:20A5E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B
+:20A60000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A
+:20A62000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A
+:20A64000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A
+:20A66000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA
+:20A68000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA
+:20A6A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA
+:20A6C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A
+:20A6E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A
+:20A70000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59
+:20A72000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39
+:20A74000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19
+:20A76000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9
+:20A78000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9
+:20A7A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9
+:20A7C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99
+:20A7E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79
+:20A80000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58
+:20A82000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38
+:20A84000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18
+:20A86000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8
+:20A88000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8
+:20A8A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8
+:20A8C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98
+:20A8E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78
+:20A90000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57
+:20A92000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37
+:20A94000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17
+:20A96000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7
+:20A98000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7
+:20A9A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7
+:20A9C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97
+:20A9E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77
+:20AA0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56
+:20AA2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36
+:20AA4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16
+:20AA6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6
+:20AA8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6
+:20AAA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6
+:20AAC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96
+:20AAE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76
+:20AB0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55
+:20AB2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35
+:20AB4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15
+:20AB6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5
+:20AB8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5
+:20ABA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5
+:20ABC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95
+:20ABE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75
+:20AC0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54
+:20AC2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34
+:20AC4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14
+:20AC6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4
+:20AC8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4
+:20ACA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4
+:20ACC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94
+:20ACE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74
+:20AD0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53
+:20AD2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33
+:20AD4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13
+:20AD6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3
+:20AD8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3
+:20ADA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3
+:20ADC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93
+:20ADE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73
+:20AE0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52
+:20AE2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32
+:20AE4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12
+:20AE6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2
+:20AE8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2
+:20AEA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2
+:20AEC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92
+:20AEE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72
+:20AF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51
+:20AF2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31
+:20AF4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11
+:20AF6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1
+:20AF8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1
+:20AFA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1
+:20AFC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91
+:20AFE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71
+:20B00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50
+:20B02000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30
+:20B04000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10
+:20B06000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0
+:20B08000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0
+:20B0A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0
+:20B0C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90
+:20B0E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70
+:20B10000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F
+:20B12000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F
+:20B14000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F
+:20B16000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF
+:20B18000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF
+:20B1A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF
+:20B1C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F
+:20B1E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F
+:20B20000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E
+:20B22000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E
+:20B24000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E
+:20B26000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE
+:20B28000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE
+:20B2A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE
+:20B2C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E
+:20B2E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E
+:20B30000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D
+:20B32000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D
+:20B34000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D
+:20B36000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED
+:20B38000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD
+:20B3A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD
+:20B3C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D
+:20B3E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D
+:20B40000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C
+:20B42000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C
+:20B44000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C
+:20B46000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC
+:20B48000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC
+:20B4A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC
+:20B4C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C
+:20B4E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C
+:20B50000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B
+:20B52000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B
+:20B54000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B
+:20B56000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB
+:20B58000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB
+:20B5A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB
+:20B5C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B
+:20B5E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B
+:20B60000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A
+:20B62000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A
+:20B64000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A
+:20B66000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA
+:20B68000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA
+:20B6A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA
+:20B6C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A
+:20B6E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A
+:20B70000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49
+:20B72000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29
+:20B74000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09
+:20B76000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9
+:20B78000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9
+:20B7A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9
+:20B7C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89
+:20B7E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69
+:20B80000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48
+:20B82000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28
+:20B84000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08
+:20B86000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8
+:20B88000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8
+:20B8A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8
+:20B8C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88
+:20B8E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68
+:20B90000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47
+:20B92000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27
+:20B94000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07
+:20B96000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7
+:20B98000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7
+:20B9A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7
+:20B9C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87
+:20B9E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67
+:20BA0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46
+:20BA2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26
+:20BA4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06
+:20BA6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6
+:20BA8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6
+:20BAA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6
+:20BAC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86
+:20BAE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66
+:20BB0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45
+:20BB2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25
+:20BB4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05
+:20BB6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5
+:20BB8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5
+:20BBA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5
+:20BBC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85
+:20BBE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65
+:20BC0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44
+:20BC2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24
+:20BC4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04
+:20BC6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4
+:20BC8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4
+:20BCA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20BCC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84
+:20BCE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64
+:20BD0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43
+:20BD2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23
+:20BD4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03
+:20BD6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3
+:20BD8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3
+:20BDA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3
+:20BDC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83
+:20BDE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63
+:20BE0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42
+:20BE2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22
+:20BE4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02
+:20BE6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2
+:20BE8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2
+:20BEA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2
+:20BEC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82
+:20BEE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62
+:20BF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41
+:20BF2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21
+:20BF4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01
+:20BF6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1
+:20BF8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1
+:20BFA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1
+:20BFC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81
+:20BFE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61
+:20C00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:20C02000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20C04000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00
+:20C06000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20C08000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20C0A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20C0C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:20C0E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:20C10000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:20C12000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20C14000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20C16000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20C18000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20C1A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20C1C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:20C1E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:20C20000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:20C22000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20C24000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20C26000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20C28000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20C2A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20C2C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:20C2E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:20C30000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:20C32000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:20C34000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20C36000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20C38000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20C3A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20C3C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:20C3E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:20C40000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C
+:20C42000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C
+:20C44000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC
+:20C46000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC
+:20C48000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC
+:20C4A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C
+:20C4C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C
+:20C4E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C
+:20C50000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B
+:20C52000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B
+:20C54000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB
+:20C56000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB
+:20C58000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB
+:20C5A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B
+:20C5C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B
+:20C5E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B
+:20C60000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A
+:20C62000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A
+:20C64000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA
+:20C66000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA
+:20C68000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA
+:20C6A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A
+:20C6C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A
+:20C6E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A
+:20C70000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39
+:20C72000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19
+:20C74000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9
+:20C76000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9
+:20C78000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9
+:20C7A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99
+:20C7C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79
+:20C7E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59
+:20C80000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38
+:20C82000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18
+:20C84000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8
+:20C86000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8
+:20C88000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8
+:20C8A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98
+:20C8C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78
+:20C8E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58
+:20C90000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37
+:20C92000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17
+:20C94000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7
+:20C96000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7
+:20C98000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7
+:20C9A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97
+:20C9C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77
+:20C9E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57
+:20CA0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36
+:20CA2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16
+:20CA4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6
+:20CA6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6
+:20CA8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6
+:20CAA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96
+:20CAC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76
+:20CAE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56
+:20CB0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35
+:20CB2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15
+:20CB4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5
+:20CB6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5
+:20CB8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5
+:20CBA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95
+:20CBC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75
+:20CBE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55
+:20CC0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34
+:20CC2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14
+:20CC4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4
+:20CC6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4
+:20CC8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4
+:20CCA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94
+:20CCC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74
+:20CCE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54
+:20CD0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33
+:20CD2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13
+:20CD4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3
+:20CD6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3
+:20CD8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3
+:20CDA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93
+:20CDC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73
+:20CDE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53
+:20CE0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32
+:20CE2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12
+:20CE4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2
+:20CE6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2
+:20CE8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2
+:20CEA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92
+:20CEC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72
+:20CEE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52
+:20CF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31
+:20CF2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11
+:20CF4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1
+:20CF6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1
+:20CF8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1
+:20CFA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91
+:20CFC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71
+:20CFE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51
+:20D00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30
+:20D02000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10
+:20D04000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0
+:20D06000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0
+:20D08000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0
+:20D0A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90
+:20D0C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70
+:20D0E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50
+:20D10000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F
+:20D12000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F
+:20D14000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF
+:20D16000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF
+:20D18000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF
+:20D1A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F
+:20D1C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F
+:20D1E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F
+:20D20000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E
+:20D22000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E
+:20D24000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE
+:20D26000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE
+:20D28000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE
+:20D2A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E
+:20D2C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E
+:20D2E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E
+:20D30000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D
+:20D32000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D
+:20D34000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED
+:20D36000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD
+:20D38000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD
+:20D3A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D
+:20D3C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D
+:20D3E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D
+:20D40000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C
+:20D42000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C
+:20D44000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC
+:20D46000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC
+:20D48000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC
+:20D4A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C
+:20D4C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C
+:20D4E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C
+:20D50000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B
+:20D52000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B
+:20D54000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB
+:20D56000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB
+:20D58000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB
+:20D5A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B
+:20D5C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B
+:20D5E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B
+:20D60000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A
+:20D62000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A
+:20D64000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA
+:20D66000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA
+:20D68000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA
+:20D6A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A
+:20D6C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A
+:20D6E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A
+:20D70000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29
+:20D72000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09
+:20D74000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9
+:20D76000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9
+:20D78000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9
+:20D7A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89
+:20D7C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69
+:20D7E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49
+:20D80000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28
+:20D82000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08
+:20D84000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8
+:20D86000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8
+:20D88000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8
+:20D8A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88
+:20D8C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68
+:20D8E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48
+:20D90000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27
+:20D92000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07
+:20D94000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7
+:20D96000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7
+:20D98000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7
+:20D9A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87
+:20D9C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67
+:20D9E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47
+:20DA0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26
+:20DA2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06
+:20DA4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6
+:20DA6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6
+:20DA8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6
+:20DAA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86
+:20DAC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66
+:20DAE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46
+:20DB0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25
+:20DB2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05
+:20DB4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5
+:20DB6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5
+:20DB8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5
+:20DBA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85
+:20DBC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65
+:20DBE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45
+:20DC0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24
+:20DC2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04
+:20DC4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4
+:20DC6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4
+:20DC8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20DCA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84
+:20DCC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64
+:20DCE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44
+:20DD0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23
+:20DD2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03
+:20DD4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3
+:20DD6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3
+:20DD8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3
+:20DDA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83
+:20DDC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63
+:20DDE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43
+:20DE0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22
+:20DE2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02
+:20DE4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2
+:20DE6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2
+:20DE8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2
+:20DEA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82
+:20DEC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62
+:20DEE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42
+:20DF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21
+:20DF2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01
+:20DF4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1
+:20DF6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1
+:20DF8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1
+:20DFA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81
+:20DFC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61
+:20DFE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41
+:20E00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20E02000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00
+:20E04000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20E06000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20E08000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20E0A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:20E0C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:20E0E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:20E10000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20E12000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20E14000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20E16000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20E18000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20E1A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:20E1C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:20E1E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:20E20000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20E22000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20E24000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20E26000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20E28000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20E2A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:20E2C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:20E2E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:20E30000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:20E32000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20E34000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20E36000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20E38000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20E3A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:20E3C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:20E3E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:20E40000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C
+:20E42000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC
+:20E44000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC
+:20E46000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC
+:20E48000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C
+:20E4A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C
+:20E4C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C
+:20E4E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C
+:20E50000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B
+:20E52000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB
+:20E54000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB
+:20E56000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB
+:20E58000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B
+:20E5A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B
+:20E5C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B
+:20E5E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B
+:20E60000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A
+:20E62000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA
+:20E64000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA
+:20E66000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA
+:20E68000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A
+:20E6A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A
+:20E6C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A
+:20E6E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A
+:20E70000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19
+:20E72000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9
+:20E74000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9
+:20E76000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9
+:20E78000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99
+:20E7A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79
+:20E7C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59
+:20E7E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39
+:20E80000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18
+:20E82000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8
+:20E84000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8
+:20E86000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8
+:20E88000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98
+:20E8A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78
+:20E8C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58
+:20E8E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38
+:20E90000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17
+:20E92000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7
+:20E94000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7
+:20E96000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7
+:20E98000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97
+:20E9A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77
+:20E9C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57
+:20E9E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37
+:20EA0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16
+:20EA2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6
+:20EA4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6
+:20EA6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6
+:20EA8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96
+:20EAA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76
+:20EAC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56
+:20EAE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36
+:20EB0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15
+:20EB2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5
+:20EB4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5
+:20EB6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5
+:20EB8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95
+:20EBA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75
+:20EBC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55
+:20EBE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35
+:20EC0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14
+:20EC2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4
+:20EC4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4
+:20EC6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4
+:20EC8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94
+:20ECA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74
+:20ECC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54
+:20ECE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34
+:20ED0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13
+:20ED2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3
+:20ED4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3
+:20ED6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3
+:20ED8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93
+:20EDA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73
+:20EDC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53
+:20EDE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33
+:20EE0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12
+:20EE2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2
+:20EE4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2
+:20EE6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2
+:20EE8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92
+:20EEA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72
+:20EEC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52
+:20EEE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32
+:20EF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11
+:20EF2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1
+:20EF4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1
+:20EF6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1
+:20EF8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91
+:20EFA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71
+:20EFC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51
+:20EFE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31
+:20F00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10
+:20F02000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0
+:20F04000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0
+:20F06000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0
+:20F08000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90
+:20F0A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70
+:20F0C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50
+:20F0E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30
+:20F10000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F
+:20F12000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF
+:20F14000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF
+:20F16000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF
+:20F18000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F
+:20F1A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F
+:20F1C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F
+:20F1E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F
+:20F20000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E
+:20F22000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE
+:20F24000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE
+:20F26000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE
+:20F28000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E
+:20F2A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E
+:20F2C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E
+:20F2E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E
+:20F30000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D
+:20F32000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED
+:20F34000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD
+:20F36000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD
+:20F38000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D
+:20F3A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D
+:20F3C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D
+:20F3E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D
+:20F40000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C
+:20F42000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC
+:20F44000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC
+:20F46000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC
+:20F48000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C
+:20F4A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C
+:20F4C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C
+:20F4E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C
+:20F50000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B
+:20F52000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB
+:20F54000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB
+:20F56000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB
+:20F58000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B
+:20F5A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B
+:20F5C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B
+:20F5E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B
+:20F60000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A
+:20F62000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA
+:20F64000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA
+:20F66000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA
+:20F68000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A
+:20F6A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A
+:20F6C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A
+:20F6E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A
+:20F70000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09
+:20F72000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9
+:20F74000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9
+:20F76000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9
+:20F78000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89
+:20F7A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69
+:20F7C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49
+:20F7E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29
+:20F800000C943E7C0C945B7C0C945B7C0C945B7C0C945B7C0C945B7C0C945B7C0C945B7C4D
+:20F820000C945B7C0C945B7C0C945B7C0C945B7C0C945B7C0C945B7C0C945B7C0C945B7C10
+:20F840000C945B7C0C945B7C0C945B7C0C945B7C0C945B7C0C945B7C0C945B7C0C945B7CF0
+:20F860000C945B7C0C945B7C0C945B7C0C945B7C0C945B7C0C945B7C0C945B7C11241FBE35
+:20F88000CFEFD0E1DEBFCDBF11E0A0E0B1E0E6E6FFEF02C005900D92A030B107D9F712E0D4
+:20F8A000A0E0B1E001C01D92A930B107E1F70E94537D0C94B27F0C94007CCF93DF93CDB7A7
+:20F8C000DEB724970FB6F894DEBF0FBECDBF882309F481E020E0482F55274115510509F4EC
+:20F8E0003DC0289A19821A821B821C820BC089819A81AB81BC810196A11DB11D89839A8332
+:20F90000AB83BC8389819A81AB81BC8180589E43A040B04060F3289819821A821B821C82DE
+:20F920000BC089819A81AB81BC810196A11DB11D89839A83AB83BC8389819A81AB81BC8127
+:20F9400080509A4FA040B04060F32F5F822F992787FD9095841795070CF4C3CF19821A8223
+:20F960001B821C8289819A81AB81BC8180509147A240B040A0F489819A81AB81BC8101968B
+:20F98000A11DB11D89839A83AB83BC8389819A81AB81BC8180509147A240B04060F3249630
+:20F9A0000FB6F894DEBF0FBECDBFDF91CF910895EF92FF920F931F93EE24FF2487012898B0
+:20F9C0008091C00087FD17C00894E11CF11C011D111D81E0E81689E0F8068DE3080780E064
+:20F9E000180770F3E0910001F091010109958091C00087FFE9CF289A8091C600992787FD06
+:20FA000090951F910F91FF90EF900895982F8091C00085FFFCCF9093C60008950E94D87C63
+:20FA2000803271F0809102018F5F80930201853009F00895E0910001F09101010995089520
+:20FA400084E10E94067D80E10E94067D0895CF93C82F0E94D87C803241F0809102018F5FC5
+:20FA600080930201853081F40AC084E10E94067D8C2F0E94067D80E10E94067D05C0E09156
+:20FA80000001F09101010995CF910895282F90E007C08091C0008823E4F78091C6009F5F8D
+:20FAA0009217B8F30895CFEFD0E1DEBFCDBF000094B714BE80916000886180936000109231
+:20FAC000600091FF74C189E18093C4001092C50088E18093C10086E08093C2005098589A07
+:20FAE000209A83E00E945D7C81E00E945D7C0E94D87C8033B9F18133C1F1803409F456C012
+:20FB0000813409F45CC0823409F46EC0853409F471C0803539F1813509F4F3C0823511F151
+:20FB2000853509F4D3C0863509F4CBC0843609F465C0843709F4EBC0853709F4D2C08637F1
+:20FB400009F44AC0809102018F5F80930201853071F6E0910001F091010109950E94D87CE1
+:20FB6000803349F60E940E7DC2CF0E94D87CC82F803241F784E10E94067D81E40E94067DEA
+:20FB800086E50E94067D82E50E94067D8C2F0E94067D89E40E94067D83E50E94067D80E5E5
+:20FBA0000E94067D80E10E94067DA1CF0E94D87C8638C0F20E94D87C0E940E7D98CF0E9498
+:20FBC000D87C803809F407C1813809F400C1823809F4F9C0883921F080E00E94277D87CF9F
+:20FBE00083E00E94277D83CF84E10E94467D0E940E7D7DCF85E00E94467DF9CF0E94D87C3F
+:20FC0000809306020E94D87C80930502809108028E7F809308020E94D87C853409F44BC0BD
+:20FC2000E5E0F1E0119281E0E438F807D9F3D0F3C0E0D0E0809105029091060218161906A2
+:20FC400078F405E011E00E94D87CF80181938F0121968091050290910602C817D90798F38D
+:20FC60000E94D87C803209F06DCF8091080280FFB6C0C0E0D0E0209105023091060212169E
+:20FC80001306B8F4E0910301F0910401A5E0B1E0F999FECFF2BDE1BD8D9180BDFA9AF99A60
+:20FCA00031962196C217D30798F3F0930401E093030184E175CF80910802816080930802C7
+:20FCC000AFCF84E00E94467D80E087CF0E94D87C809303010E94D87C809304010E940E7DDF
+:20FCE00006CF0E94D87C803209F02CCF84E10E94067D8EE10E94067D86E90E94067D8AE077
+:20FD00004FCF0E940E7D88E080936000FFCF0E94D87C809306020E94D87C809305020E942C
+:20FD2000D87C853409F449C0809108028E7F809308028091030190910401880F991F9093BE
+:20FD40000401809303010E94D87C803209F0CFCE84E10E94067DC0E0D0E0209105023091F6
+:20FD600006021216130608F01DCFE0910301F09104018091080280FF96C0F999FECFF2BD5D
+:20FD8000E1BDF89A80B50E94067DE0910301F09104013196F0930401E09303012091050260
+:20FDA000309106022196C217D30718F3FBCEE0910001F0910101099586CE80910802816059
+:20FDC00080930802C0CF80E10E94277D90CE81E00E94277D8CCE82E00E94277D88CE809162
+:20FDE000030190910401880F991F90930401809303018091050280FF09C0809105029091B2
+:20FE0000060201969093060280930502F999FECF1127E0910301F0910401C5E0D1E0809105
+:20FE2000050290910602103091F40091570001700130D9F303E000935700E8950091570045
+:20FE400001700130D9F301E100935700E895099019900091570001700130D9F301E00093DF
+:20FE60005700E8951395103898F011270091570001700130D9F305E000935700E8950091CB
+:20FE8000570001700130D9F301E100935700E8953296029709F0C7CF103011F00296E5CFD7
+:20FEA000112484E17DCE869580FF06C03196F0930401E093030176CF84910E94067D209107
+:20FEC000050230910602E0910301F0910401EECF1F93CF930E94D87CC82F0E94067D0E94D2
+:20FEE000D87C182F0E94067DC1362CF0C75511363CF0175508C0C033D4F3C0531136CCF795
+:20FF000010330CF01053C295C07FC10F8C2F992787FD9095CF911F910895CF93282F99278F
+:20FF200087FD9095807F9070959587959595879595958795959587958A303CF0895AC22F47
+:20FF4000CF70CA303CF0C95A06C0805DC22FCF70CA30CCF7C05D0E94067D8C2F0E94067D67
+:06FF6000CF910895FFCFD0
+:00000001FF
diff --git a/firmware-hfuse.hex b/firmware-hfuse.hex
new file mode 100644 (file)
index 0000000..1a73311
--- /dev/null
@@ -0,0 +1,2 @@
+:0100000000FF
+:00000001FF
diff --git a/firmware-lfuse.hex b/firmware-lfuse.hex
new file mode 100644 (file)
index 0000000..1a73311
--- /dev/null
@@ -0,0 +1,2 @@
+:0100000000FF
+:00000001FF
diff --git a/floating-phases.scad b/floating-phases.scad
new file mode 100644 (file)
index 0000000..b419918
--- /dev/null
@@ -0,0 +1,103 @@
+// -*- C -*-
+
+// caller should define
+//
+//   z_pause = 2;    // higher than any thing in the model
+//   total_sz = [ 200, 141 ];
+//   phases=4;   // how many phases
+//   colours = [
+//        "blue",
+//        "black",
+//        "red",          
+//        "yellow",
+//        ];
+//
+// Pause height                z_pause value from caller
+// head park X         15
+// head park Y         0
+// head move Z         1
+// min head park Z     1
+// retraction          1
+// re-home             [ ] [ ]
+
+// when z pause occurs
+//   * set feed rate to 10% (or whatever minimum is)
+//   * press knob, printer will start
+//   * quickly, press again, select "stop"
+//   * set temp
+//   * set feed rate back to 100%
+//   * now change filament etc., start next file
+
+include <utils.scad>
+
+$test_thicker = 1.0; // 2.5;
+
+th_l0 = .425 * $test_thicker;
+th_l1 = .250 * $test_thicker;
+frame_w = 0.8;
+tower_w = 2.0;
+
+th_most = th_l0 + th_l1*2;
+
+noz_gap = 8;
+
+
+multicolour_gap = 0.15; // each side
+
+underlayer_smaller = 0.35; // each side
+
+total_sz_actual = $test ? $test : total_sz;
+
+module Interlayer_Bigger(){
+  offset(r=multicolour_gap, $fn=20){
+    union(){ children(); }
+  }
+}
+
+module Interlayer_Smaller(){
+  offset(r=-multicolour_gap, $fn=20){
+    union(){ children(); }
+  }
+}
+
+module Underlayer_Smaller(){
+  offset(r=-underlayer_smaller, $fn=20){
+    union(){ children(); }
+  }
+}
+
+module FloatingPhaseFraming(phase, zmin) {
+  frame_inner =
+    total_sz_actual
+    + noz_gap * [2,2] * (phase > 0 ? phase : 0.5);
+
+  frame_outer =
+    frame_inner
+    + frame_w * [2,2];    
+
+  tower_pos =
+    -0.5 * frame_inner
+    + noz_gap * [1,0] * (phases - phase)
+    + -[1,0] * tower_w + 0.5 * [0,-1] * frame_w;
+
+  // frame for alignment
+  linear_extrude(height= th_l0)
+    difference(){
+      square(frame_outer, center=true);
+      square(frame_inner, center=true);
+  }
+
+  // tower to avoid diagonal traverse to start position
+  linear_extrude(height= zmin + th_l1)
+    translate(tower_pos)
+    square([1,1] * tower_w);
+
+  // trick to pause rather than finishing
+  if (phase != phases-1)
+    translate([0,0, z_pause+th_l1])
+      linear_extrude(th_l1)
+      translate(tower_pos)
+      square([1,1] * tower_w);
+}
+
+echo(str("SET PAUSE AT Z HEIGHT TO ",z_pause));
diff --git a/floating-test.scad b/floating-test.scad
new file mode 100644 (file)
index 0000000..5f4000c
--- /dev/null
@@ -0,0 +1,19 @@
+// -*- C -*-
+
+frameth=0.8;
+
+l0= 0.425;
+l1= 0.25;
+
+del=l0+l1*2;
+
+for (r=[0,90,180,270])
+  rotate([0,0,r])
+  translate([10,-(10+frameth),0])
+    cube([frameth, 20+2*frameth, 0.4]);
+
+translate([-11,-11,0])
+cube([2,2, del + l1]);
+
+translate([-3,-3, del])
+  cube([6,6, l1*2]);
diff --git a/flyscreen-handle.scad b/flyscreen-handle.scad
new file mode 100644 (file)
index 0000000..28753b7
--- /dev/null
@@ -0,0 +1,258 @@
+// -*- C -*-
+
+opening_height = 7.84;
+opening_depth = 7.88;
+openingedge_dia = 2.00;
+opening_protrh = 2.00;
+
+pivot_x = 6;
+inside_len = 4;
+
+pivoting_gap = 0.1;
+
+outside_gap = 3;
+outside_len = 13;
+outend_height = 3;
+
+outside_len_bot = 23;
+
+outside_pushh = 4;
+outside_pushslope = 1.4;
+outside_push_inadj = 0.82;
+
+ourcirc_r = 0.5 / 2;
+
+ribble_dia = 2.2;;
+
+opening_protr_slop = 0.1;
+
+intooth_top_slop = 0.1;
+inside_h_xgap = 1;
+
+pivot_r = 2;
+pivot_slop = 0.25;
+
+strap_above = 0.1;
+strap_th = 2.5;
+strap_below = 3;
+strap_width = 5;
+
+width = 35;
+nstraps = 2;
+
+test_width = 5;
+
+// calculated
+
+inside_h = opening_height/2 - opening_protrh - inside_h_xgap/2;
+
+edge_or = openingedge_dia/2 + opening_protr_slop;
+
+Q0 = [ openingedge_dia/2,
+       openingedge_dia/2 + opening_height/2 ];
+
+p4p5d = [edge_or + ourcirc_r, 0];
+
+P0 = [ pivot_x, pivoting_gap ];
+P4 = Q0 - p4p5d;
+P3t = [ P4[0], Q0[1] - openingedge_dia/2 + opening_protrh
+       - intooth_top_slop - ourcirc_r ];
+P2 = P4 + [ -(inside_len - ourcirc_r*2), 0 ];
+P1 = [ P2[0], P3t[1] - (inside_h + ourcirc_r*2) ];
+
+P5 = Q0 + p4p5d;
+
+P8t = [ outside_len - ourcirc_r, P5[1] ];
+P9t = P8t + [ 0, -(strap_above + strap_th + strap_below - ourcirc_r*2) ];
+
+P9b = [ P9t[0], -P9t[1] + outside_gap ];
+P8b = P9b + [ 0, outend_height ];
+
+P89eadj = [ outside_len_bot - outside_len, 0 ];
+P8eb = P8b + P89eadj;
+P9eb = P9b + P89eadj;
+
+P6t = P5 + [ 0, outside_pushh - ourcirc_r*2 ];
+P7 = [ P6t[0] + (P6t[1] - P1[1]) / outside_pushslope,
+       P1[1] ];
+
+P3a = P3t + [ -outside_push_inadj, 0 ];
+P6a = P6t + [ -outside_push_inadj, 0 ];
+
+outside_push_inadj_slope = (P3t[1]-P4[1]) / (P6a[1]-P5[1]);
+
+ribble_rad = ribble_dia/2;
+
+kit_adj_shift = -opening_height - 2.0;
+
+module ExtrusionSect(){
+  cr = openingedge_dia/2;
+  toph = opening_height/2 + opening_protrh;
+
+  for (my=[0,1]) {
+    mirror([0,my]) {
+      translate(Q0) {
+       hull(){
+         circle(r=cr, $fn=20);
+         translate([-cr,10]) square([cr*2, 1]);
+       }
+      }
+    }
+  }
+  translate([-opening_depth, -toph]) {
+    difference(){
+      translate([-5,-5])
+       square([opening_depth+6, toph*2+10]);
+      square([opening_depth+2, toph*2]);
+    }
+  }
+}
+
+module PsHull(ps) {
+  hull(){
+    for (p = ps) {
+      translate(p)
+       circle(r = ourcirc_r, $fn=10);
+    }
+  }
+}
+
+module LeverSect(top, inadj=false){
+  P3 = inadj ? P3a : P3t;
+  P6 = inadj ? P6a : P6t;
+  P8 = top ? P8t : P8b;
+  P9 = top ? P9t : P9b;
+  difference(){
+    union(){
+      PsHull([P2,P3,P4]);
+      PsHull([P0,P1,P2,P5,P8,P9]);
+    }
+    hull(){
+      for (dp = [ [0,0],
+                 (P6-P5),
+                 (P3-P4)
+                 ]) {
+       translate(Q0 + 5*dp) circle(r=edge_or, $fn=20);
+      }
+    }
+  }
+}
+
+module StrapSectTop(){
+  translate(P9t + ourcirc_r * [+1,-1]) {
+    difference(){
+      circle(r = strap_below + strap_th, $fn=40);
+      circle(r = strap_below,            $fn=40);
+    }
+  }
+}
+
+module StrapSectBot(inadj=false){
+  mirror([0,1]){
+    for (dx = [ -(strap_below + strap_th),
+               0 ]) {
+      translate(P9b + [ ourcirc_r + dx, -10 ]) {
+       square([strap_th, 20]);
+      }
+    }
+  }
+}
+
+module Ribbles(xmax, xmin, y){
+  for (x = [ xmax + ourcirc_r - ribble_rad :
+            -ribble_rad * 4 :
+            xmin ]) {
+    translate([x, y])
+      circle(r = ribble_rad, $fn=20);
+  }
+}
+
+module LeverSectTop(){
+  difference(){
+    union(){
+      LeverSect(true, false);
+      Ribbles(P8t[0],
+             Q0[0] + edge_or + ribble_rad*2,
+             P5[1] + ourcirc_r);
+    }
+    translate([pivot_x,0]) circle(r= pivot_r + pivot_slop, $fn=20);
+  }
+}
+
+module LeverSectBot(inadj=false){
+  P6 = inadj ? P6a : P6t;
+  mirror([0,1]) {
+    LeverSect(false, inadj);
+    PsHull([P5,P6,P7]);
+    PsHull([P8b,P8eb,P9eb,P9b]);
+    Ribbles(P8eb[0],
+           P9b[0],
+           P8eb[1]);
+    translate([pivot_x,0]) circle(r=pivot_r, $fn=20);
+  }
+}
+
+module Demo(){
+  translate([0,0,-5]) color("white") ExtrusionSect();
+  LeverSectTop();
+  translate([0,0,5]) LeverSectBot();
+  color("black") LeverSectBot(true);
+  color("blue") translate([0,0,10]) StrapSectTop();
+  color("purple") translate([0,0,-10]) StrapSectBot();
+}
+
+module SomeLever() {
+  // SomeLever(){ LeverBot(inadj); LeverSectBot(); }
+  difference(){
+    linear_extrude(height=width, convexity=100) children(0);
+    for (i = [ 0 : nstraps - 1 ]) {
+      translate([0,0, (i + 0.5) / nstraps * width - strap_width/2])
+       linear_extrude(height=strap_width, convexity=10)
+       children(1);
+    }
+  }
+}
+
+module Test(){
+  linear_extrude(height=test_width, convexity=100) {
+    translate([0,2,0]) LeverSectTop();
+    LeverSectBot();
+    translate([0,kit_adj_shift]) LeverSectBot(true);
+  }
+}
+
+module LeverTop(){ ////toplevel
+  SomeLever(){
+    LeverSectTop();
+    StrapSectTop();
+  }
+}
+module LeverBotOutside(){ ////toplevel
+  SomeLever(){
+    LeverSectBot();
+    StrapSectBot();
+  }
+}
+module LeverBotInside(){ ////toplevel
+  SomeLever(){
+    LeverSectBot(true);
+    StrapSectBot(true);
+  }
+}
+
+module KitOutside(){ ////toplevel
+  translate([0,2,0]) LeverTop();
+  LeverBotOutside();
+}
+
+module KitInside(){ ////toplevel
+  translate([0,2,0]) LeverTop();
+  LeverBotInside();
+}
+
+//LeverSectBot(true);
+//Demo();
+//LeverTop();
+//Test();
+//Kit();
+//KitInside();
diff --git a/flyscreen-wall-spacer.scad b/flyscreen-wall-spacer.scad
new file mode 100644 (file)
index 0000000..4abe1ef
--- /dev/null
@@ -0,0 +1,100 @@
+// -*- C -*-
+
+include <utils.scad>
+
+bracket_th = 2.70;
+left_inboard_to_wall = 9.78;
+right_inboard_to_wall = 13.21;
+
+plug_dia = 10;
+screw_dia = 5;
+bucket_wall = 2.5;
+bucket_floor = 2.5;
+whole_dia = plug_dia + bucket_wall *2;
+
+min_spacing = 8;
+max_spacing = 19;
+
+general_spacer_height = 10;
+
+// calculated
+
+module Oval(r, dc) {
+  hull(){
+    circle(r);
+    translate([0, dc])
+      circle(r);
+  }
+}
+
+module MainCircle() {
+  difference(){
+    circle(r = whole_dia/2);
+    circle(r = screw_dia/2);
+  }
+}
+
+module MultiSpacer() {
+  difference(){
+    linextr(0, $inboard_to_wall - bracket_th){
+      Oval(whole_dia/2, max_spacing);
+    }
+
+    linextr(bucket_floor, 100) {
+      Oval(plug_dia/2, max_spacing);
+    }
+
+    linextr(-1, 100) {
+      circle(screw_dia/2);
+
+      translate([0, min_spacing])
+       Oval(screw_dia/2, max_spacing - min_spacing);
+    }
+  }
+}
+
+module AnySpacer(max_z) {
+  linextr(0, bucket_wall)
+    MainCircle();
+  linextr(0, max_z){
+    difference(){
+      MainCircle();
+      circle(r = plug_dia/2);
+    }
+  }
+}
+
+module Spacer($inboard_to_wall) {
+  AnySpacer($inboard_to_wall - bracket_th);
+}
+
+module Spacers1() {
+  for (dy = [0, 30]) {
+    translate([0,dy,0]) {
+      Spacer($inboard_to_wall = left_inboard_to_wall);
+      translate([0, 70, 0])
+       Spacer($inboard_to_wall = right_inboard_to_wall);
+    }
+  }
+
+  translate([40, 0, 0])
+    MultiSpacer($inboard_to_wall = left_inboard_to_wall);
+  translate([40, 70, 0])
+    MultiSpacer($inboard_to_wall = right_inboard_to_wall);
+}
+
+module Spacers2() {
+  for (dy = 30 * [0]) {
+    echo(dy);
+    translate([0, dy, 0])
+      AnySpacer(general_spacer_height);
+  }
+}
+
+module Spacers3() {
+  AnySpacer(6.08);
+  translate([0, 30, 0])
+    AnySpacer(8.18);
+}
+
+Spacers3();
diff --git a/fruit-bowl-stand.scad b/fruit-bowl-stand.scad
new file mode 100644 (file)
index 0000000..634f4d6
--- /dev/null
@@ -0,0 +1,51 @@
+// -*- C -*-
+
+include <utils.scad>
+
+across = 12.5;
+r0 = 71;
+h = 36;
+feet = 5;
+
+// calculated
+
+r_eff_across = across/2 * cos(360/8/2);
+
+effective_corner = across/2 * [ sin(360/8/2), cos(360/8/2) ];
+
+r_mid = r0 + effective_corner[0];
+
+h_mid = h - effective_corner[1] - r_eff_across;;
+
+module XSection(){
+  rotate(360/8/2)
+    circle(r= across/2, $fn=8);
+}
+
+module Ring(){
+  rotate_extrude($fa=1)
+    translate([r_mid, 0,0])
+    XSection();
+}
+
+module Foot(){
+  rotate([180,0,0])
+    linear_extrude(h_mid)
+    XSection();
+}
+
+module Stand(){
+  Ring();
+  intersection(){
+    for (a=[0:feet-1]) {
+      rotate([0,0, 360*a/feet])
+       translate([-r_mid, 0,0])
+       Foot();
+    }
+
+    linextr(-across - h, across)
+      circle(r= r_mid + r_eff_across, $fa=1);
+  }
+}
+
+Stand();
diff --git a/funcs.scad.cpp b/funcs.scad.cpp
new file mode 100644 (file)
index 0000000..c34d157
--- /dev/null
@@ -0,0 +1,86 @@
+// -*- C -*-
+
+function vecdiff2d(a,b) = [b[0]-a[0], b[1]-a[1]];
+function vecdiff(a,b)   = [b[0]-a[0], b[1]-a[1], b[2]-a[2]];
+
+#define dsq(i) (a[i]-b[i])*(a[i]-b[i])
+function dist2d(a,b) = sqrt(dsq(0) + dsq(1));
+function dist(a,b)   = sqrt(dsq(0) + dsq(1) + dsq(2));
+#undef dsq
+
+#define vsq(i) (v[i]*v[i])
+function vectorlen2d(v) = sqrt(vsq(0) + vsq(1));
+function vectorlen(v)   = sqrt(vsq(0) + vsq(1) + vsq(2));
+#undef vsq
+
+function unitvector2d(v) = v / vectorlen2d(v);
+function unitvector(v)   = v / vectorlen(v);
+
+function atan2vector(v) = atan2(v[1], v[0]);
+
+// | m[0][0] m[0][1] |
+// | m[1][0] m[1][1] |
+function determinant2(m) = (m[0][0] * m[1][1] - m[0][1] * m[1][0]);
+
+function clockwise2d(v) = [v[1], -v[0]];
+
+function rotate2d(theta, v) = [ v[0] * cos(theta) - v[1] * sin(theta),
+                               v[1] * cos(theta) + v[0] * sin(theta) ];
+
+// intersection of lines p1..p2 and p3..p4
+function line_intersection_2d(p1,p2,p3,p4) = [
+  // from https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line
+  #define XY12 determinant2([p1,p2])
+  #define XY34 determinant2([p3,p4])
+  #define XU12 QU(0,1,2)
+  #define XU34 QU(0,3,4)
+  #define YU12 QU(1,1,2)
+  #define YU34 QU(1,3,4)
+  #define QU(c,i,j) determinant2([[ p##i[c], 1 ], \
+                                [ p##j[c], 1 ]])
+  #define DENOM \
+   determinant2([[ XU12, YU12 ], \
+                [ XU34, YU34 ]])
+   //----
+   determinant2([[ XY12, XU12 ],
+                [ XY34, XU34 ]]) / DENOM,
+   determinant2([[ XY12, YU12 ],
+                [ XY34, YU34 ]]) / DENOM,
+   ];
+  #undef XY12
+  #undef XY34
+  #undef XU12
+  #undef XU34
+  #undef YU12
+  #undef YU34
+  #undef QU
+  #undef DENOM
+
+
+function circle_point(c, r, alpha) = [ c[0] + r * cos(alpha),
+                                      c[1] + r * sin(alpha) ];
+
+#define d     (dist2d(a,c))
+#define alpha (atan2(a[1]-c[1],a[0]-c[0]))
+#define gamma (asin(r / d))
+#define beta  (alpha + 90 - gamma)
+
+function tangent_intersect_beta(c,r,a) =
+              beta;
+
+function tangent_intersect_b(c,r,a) =
+              circle_point(c, r, beta);
+#undef d
+#undef alpha
+#undef gamma
+#undef beta
+
+function tangents_intersect_beta(cbig,rbig,csmall,rsmall) =
+        tangent_intersect_beta(cbig,rbig-rsmall,csmall);
+
+function reflect_in_y(p) = [-p[0], p[1]];
+
+function angle_map_range(in,base) =
+  in <  base       ? in + 360 :
+  in >= base + 360 ? in - 360 :
+  in;
diff --git a/hole-repair-20191117.scad b/hole-repair-20191117.scad
new file mode 100644 (file)
index 0000000..0138c67
--- /dev/null
@@ -0,0 +1,49 @@
+// -*- C -*-
+
+post_dia = 23.0;
+post_height = 20;
+
+th = 4;
+
+nom_hole = 25;
+min_r = 15 + nom_hole/2;
+maj_r = 15 + nom_hole/2;
+
+postwall_th = 2;
+
+$fa=1;
+$fs=1;
+
+module Profile(r) {
+  polygon([ [0,    0],
+           [r,    0],
+           [r-th, th],
+           [0,    th] ]);
+}
+
+module Body(){
+  hull(){
+    for (x= [-1,+1] * (maj_r-min_r))
+      translate([x,0,0])
+       rotate_extrude()
+       Profile(min_r);
+  }
+}
+
+module Post(){
+  difference(){
+    translate([0,0,-1])
+      cylinder(r= post_dia/2, h= post_height+1);
+    translate([0,0,-2])
+      cylinder(r= post_dia/2 - postwall_th, h= post_height+3);
+  }
+}
+
+module Cover(){
+  rotate([0,180,0]) Body();
+  Post();
+}
+
+//Body();
+//Post();
+Cover();
diff --git a/hole-transfer-punch.scad b/hole-transfer-punch.scad
new file mode 100644 (file)
index 0000000..d9e4859
--- /dev/null
@@ -0,0 +1,32 @@
+// -*- C -*-
+
+include <utils.scad>
+
+dia = 6;
+
+point = 3;
+solid = 3;
+
+$fa= 1;
+$fs = 0.1;
+
+// calculated
+
+depth = dia * 1.5;
+
+module Blivet(){
+  rotate_extrude(){
+    polygon([[ 0,0 ],
+            [ point, 0 ],
+            [ point, solid ],
+            [ 0, solid + point]]);
+  }
+  linextr(0, solid){
+    square(center=true, [ dia*3, dia ]);
+  }
+  linextr(-depth, solid){
+    circle(r= dia/2);
+  }
+}
+
+Blivet();
diff --git a/holetest.scad b/holetest.scad
new file mode 100644 (file)
index 0000000..328ef2d
--- /dev/null
@@ -0,0 +1,52 @@
+// -*- C -*-
+
+h=2;
+
+$fa=1;
+$fs=0.1;
+
+label=true;
+
+spc= 7;
+l = 50;
+w = 10;
+
+lt = 0.5;
+lw = 10;
+
+// calculated
+
+// Actual sizes (according to calipers) of things that fit
+//  A
+//  C - 1.88mm (M3 screw)
+//  E - 2.97mm (3mm HSS bit shank)
+//  G - 
+//  I - 3.15mm tight fit (Yale padlock from extra padlocks tray)
+//  K - 3.33mm (M3.5 screw)
+
+ly0 = -w/2 -lw;
+
+difference(){
+  union(){
+    cube([l,w,h], center=true);
+    if (label)
+      translate([-l/2, ly0, -h/2])
+       cube([l, lw, lt]);
+  }
+
+  for (i=[0:2:10]) {
+
+    sz = 3 + 0.5 * i/10;
+
+    echo(sz);
+    translate([(i-5)/2 * spc, 0, -7 ]) {
+      cylinder(r= sz/2, h=14);
+      linear_extrude(height=14, convexity=100) {
+       translate([0, ly0 + lw * .2])
+         text(halign="center",
+              size= lw * .6, font="DejaVu Sans:style=Bold",
+              chr(i + 65));
+      }
+    }
+  }
+}
diff --git a/hotel-piece-model.scad b/hotel-piece-model.scad
new file mode 100644 (file)
index 0000000..2f8af81
--- /dev/null
@@ -0,0 +1,29 @@
+// -*- C -*-
+
+h = 15;
+w = 20;
+l = 30;
+roof = 10;
+eave = 1;
+peakw = 1;
+overhang = 3.0;
+chimnd= 7;
+chimnhr = 1.00;
+
+$fs = 0.1;
+
+module Hotel(){
+  cube([w,l,h + 0.1]);
+  hull(){
+    translate([0,0, h] + overhang * [-1,-1,0])
+      cube([w,l,eave] + overhang * [2,2,0]);
+    translate([0,0, h] + overhang * [0,-1,0]
+             + (w-peakw) * 0.5 * [1,0,0])
+      cube([peakw, l, roof] + overhang * [0,2,0]);
+  }
+  translate([w/4, l/2, h] + overhang * [-0.5, 0,0])
+    cylinder(r= chimnd/2, h = roof * chimnhr);
+}
+
+scale(1.11)
+Hotel();
diff --git a/itx-aperture-grommet.scad b/itx-aperture-grommet.scad
new file mode 100644 (file)
index 0000000..53f74fa
--- /dev/null
@@ -0,0 +1,56 @@
+// -*- C -*-
+
+include <utils.scad>
+include <threads.scad>
+
+ap_width = 21.30;
+ap_height = 16.45;
+tot_height = 22.74 + 1.0;
+screw_ctr_to_tr = [ 7.89, 3.87 ];
+
+tab_th = 2.5;
+wall_th = 1;
+wall_h = 2;
+
+app_slop = 0.60; // total across both sides
+
+// calculated
+
+tab_h = tot_height - ap_height;
+
+real_main_sz = [ ap_width, ap_height ] - app_slop * [ 1,1 ];
+real_all_sz = real_main_sz + tab_h * [0,1];
+real_inner_sz = real_main_sz - wall_th * [ 2,1 ];
+
+screw_pos = real_all_sz - (screw_ctr_to_tr - 0.5 * app_slop * [1,1]);
+
+module GapPlan() {
+  rectfromto([ wall_th, -1 ],
+            real_main_sz - wall_th * [1,1]);
+}
+
+module MainPlan() {
+  rectfromto([0,0],
+            real_main_sz);
+}
+
+module AllPlan() {
+  rectfromto([0,0], real_all_sz);
+}
+
+module Grommet(){ ////toplevel
+  difference(){
+    union(){
+      linextr(0, tab_th + wall_h) MainPlan();
+      linextr(0, tab_th) AllPlan();
+    }
+    linextr(-1, tab_th + wall_h + 1) GapPlan();
+    translate(concat(screw_pos, [-1]))
+      english_thread(diameter = 0.1380,
+                    threads_per_inch = 32,
+                    length = tab_th + 2,
+                    internal = true);
+  }
+}
+
+Grommet();
diff --git a/keyring-kay.scad b/keyring-kay.scad
new file mode 100644 (file)
index 0000000..9ebb34a
--- /dev/null
@@ -0,0 +1,59 @@
+// -*- C -*-
+
+letterheight = 25;
+linewidth = 3.5;
+letterthick = 2.5;
+
+basethick = 2.5;
+
+xborder = 5;
+yborder = 5;
+
+kdiag = 1;
+kprop = 0.50;
+
+diaglinewidth = linewidth * sqrt(1 + kdiag*kdiag);
+
+ringholerad = 2.5;
+ringedgewidth = 3;
+
+module kay_leg (transamount, llen, mir) {
+  translate([0,transamount,0])
+    mirror([0,mir,0])
+    translate([0,-0.1,0])
+    multmatrix([[1,kdiag,0,0],
+               [0,1,0,0],
+               [0,0,1,0],
+               [0,0,0,1]])
+    cube([diaglinewidth, llen + 0.1, letterthick]);
+}  
+
+module kay () {
+  translate([0.1,0,0])
+    cube([linewidth, letterheight, letterthick]);
+  kay_leg(letterheight*kprop, letterheight*(1-kprop), 0);
+  kay_leg(letterheight*kprop, letterheight*kprop, 1);
+}
+
+totalw = letterheight*kprop + diaglinewidth + xborder*2;
+totalh = letterheight + yborder*2;
+basez = -(basethick-0.1);
+
+module main () {
+  kay();
+  translate([-xborder, -yborder, basez])
+    cube([totalw, totalh, basethick]);
+}
+
+module ring (rad, extra) {
+  translate([totalw/2 - xborder, totalh - yborder, basez-extra])
+    cylinder(r=rad, h=basethick + extra*2, $fn=30);
+}
+
+difference(){
+  union() {
+    main();
+    ring(ringholerad + ringedgewidth/2, 0);
+  }
+  ring(ringholerad, 1);
+}
diff --git a/knifeblock,BlockPrint.auto.slic3r b/knifeblock,BlockPrint.auto.slic3r
new file mode 100644 (file)
index 0000000..22dacf8
--- /dev/null
@@ -0,0 +1 @@
+fill_density = 0.1
diff --git a/knifeblock-knives-filter b/knifeblock-knives-filter
new file mode 100755 (executable)
index 0000000..17cff74
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/bin/perl -w
+
+use strict;
+use POSIX;
+
+our %want;
+
+our $nknives = 3;
+
+our @part_order = qw(h b l);
+
+my $want = shift @ARGV;
+my ($wknife,$wparts) = $want =~ m/^(\d)([a-z]+)$/ or die;
+
+sub want ($) {
+    my ($colournum) = @_;
+    my $knife = $nknives-1 - ($colournum-1) % $nknives;
+    return 0 unless $knife == $wknife;
+    my $part = $part_order[ floor(($colournum-1) / $nknives) ];
+    die "huh colour $colournum?" unless defined $part;
+    return 0 unless $part =~ m/[$wparts]/o;
+    print STDERR "$0: including colour $colournum ($knife $part)\n";
+    return 1;
+}
+
+our $drop;
+while (<>) {
+    if (m/^\S/) {
+       $drop = 
+           m/^2 5 / ||
+           (m/^(?:3 1|2 3) \d+ \d+ (\d+) / && !want($1));
+    }
+    next if $drop;
+
+    print or die $!;
+}
diff --git a/knifeblock-knives-photo.jpg b/knifeblock-knives-photo.jpg
new file mode 100644 (file)
index 0000000..0d757cc
Binary files /dev/null and b/knifeblock-knives-photo.jpg differ
diff --git a/knifeblock-knives-trace.fig b/knifeblock-knives-trace.fig
new file mode 100644 (file)
index 0000000..c42c2c6
--- /dev/null
@@ -0,0 +1,66 @@
+#FIG 3.2  Produced by xfig version 3.2.5b
+Landscape
+Center
+Metric
+A4      
+100.00
+Single
+-2
+1200 2
+2 5 0 1 0 -1 50 -1 -1 0.000 0 0 -1 0 0 5
+       0 knifeblock-knives-photo.jpg
+        585 270 11557 270 11557 8499 585 8499 585 270
+2 3 1 1 9 7 30 -1 -1 4.000 0 0 7 0 0 6
+        3093 6395 6633 6470 10898 6641 5808 7070 5818 6778 3093 6395
+2 3 1 1 8 7 30 -1 -1 4.000 0 0 7 0 0 6
+        3038 4586 6578 4661 10843 4832 5753 5261 5763 4969 3038 4586
+2 3 1 1 7 7 30 -1 -1 4.000 0 0 7 0 0 6
+        2978 2996 6518 3071 10783 3242 5693 3671 5703 3379 2978 2996
+3 1 0 1 2 7 40 -1 -1 0.000 0 0 0 21
+        5858 5445 6063 5290 6283 5115 6523 4865 6658 4670 6768 4510
+        6758 4405 6438 4365 5923 4315 5423 4295 4823 4290 4418 4300
+        4003 4335 3648 4380 3593 5170 3883 5190 4368 5185 4598 5160
+        4993 5075 5718 5365 5838 5435
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000
+3 1 0 1 3 7 40 -1 -1 0.000 0 0 0 23
+        5878 6218 5318 6198 4858 6198 4463 6208 4118 6223 3908 6258
+        3908 6953 4293 6978 4668 6938 5078 6873 5693 7028 5718 7113
+        5783 7143 5873 7128 6028 7028 6243 6833 6453 6608 6583 6458
+        6638 6398 6598 6348 6548 6303 6418 6283 6033 6243
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000
+3 1 0 1 1 7 40 -1 -1 0.000 0 0 0 20
+        5918 3753 6120 3555 6435 3285 6615 3060 6840 2745 6975 2520
+        6975 2475 6615 2430 5985 2385 5490 2355 4950 2340 4498 2360
+        3883 2385 3458 2425 3443 3430 3838 3435 4248 3410 5593 3723
+        5783 3858 5773 3848
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000
+3 1 0 1 4 7 40 -1 -1 0.000 0 0 0 23
+        9223 2689 8811 2659 8398 2621 7993 2606 7603 2584 7228 2569
+        6861 2561 5871 2539 5121 3529 5818 3694 5991 3769 6583 3776
+        7206 3769 8113 3746 8826 3709 9298 3649 9816 3581 10311 3431
+        10737 3204 10702 3099 10551 3026 10243 2869 9606 2734
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000
+3 1 0 1 5 7 40 -1 -1 0.000 0 0 0 23
+        7191 5366 8098 5329 8586 5291 9051 5194 9298 5119 9652 4993
+        9802 4898 9812 4828 9722 4748 9283 4676 9028 4616 8706 4594
+        8278 4549 7851 4534 7371 4534 6883 4511 6141 4511 5998 4744
+        5661 4969 5593 5366 6043 5351 6478 5366 7026 5374
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000
+3 1 0 1 6 7 40 -1 -1 0.000 0 0 0 21
+        6816 7069 7191 7047 7558 6994 7896 6927 8097 6852 8277 6772
+        8267 6677 8067 6622 7881 6544 7611 6499 7356 6454 7093 6432
+        6838 6409 6561 6409 6096 6387 5901 6454 5578 6784 5668 7024
+        5811 7069 6141 7084 6388 7077
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000
diff --git a/knifeblock.scad b/knifeblock.scad
new file mode 100644 (file)
index 0000000..c1f1869
--- /dev/null
@@ -0,0 +1,346 @@
+// -*- C -*-
+
+// properties of the knives
+nknives = 3;
+widths = [15.5, 15.8, 19.0];
+handlelenbase = 75;
+handlelendelta = [-15, 0, 10];
+locations = [-35, 0, 37];
+bladew = 5; // 2.5
+maxhandledepth = 45;
+
+templatescale = 27.2 / 19.6;
+
+coverlonglen = 120; // xxx
+covershortlen = 70; // xxx
+
+// other tuneables
+front = 5;
+back = 5;
+height = 50;
+minsidein = 4;
+minsideout = 4;
+
+frontbackslop = 0.25;
+
+knifewidthslop = 2.0;
+
+screwbackdepth = 6.0 - 1.0;
+screwdia =       4.0 + 0.5;
+screwcsinkdia =  9.8 + 1.0;
+
+screwabove = 15;
+
+coverthick = 2.4;
+coverside = coverthick;
+
+covertopwing = 15;
+covertopwingbase = 20;
+coveredge = 3;
+
+holesize = 12.5;
+holestrut = 7;
+holeedge = 4;
+
+holeoffx = 0.33;
+holeoffy = 0.23;
+
+indentdepth = 1;
+indentoutersize = holesize + 2.15;
+indentinnersize = indentoutersize - indentdepth * 3.0;
+
+pegstem = 3.5;
+peghead = 10;
+pegstemheight = 2;
+pegheight = 9;
+peglen = 12;
+
+recessblockwidth = peghead + pegstem*3;
+recessblockheight = peglen/2 + pegstem*1.5;
+
+pegsloph = 0.5;
+pegslopv = 0.5;
+pegslopl = 0.5;
+
+pegdepthproportion = 0.80;
+
+// computed
+
+function width(k) = widths[k] + knifewidthslop;
+
+side = minsidein + screwcsinkdia + minsideout;
+totaldepth = front + maxhandledepth + back;
+
+minkx = locations[0] -         width(0)        /2;
+maxkx = locations[nknives-1] + width(nknives-1)/2;
+
+minx = minkx - side;
+maxx = maxkx + side;
+
+holepitch = holesize+holestrut;
+
+pegrecess = pegdepthproportion*totaldepth - 0.5*peglen;
+
+module ImportTemplate(w,k,t) {
+  fn = str("knifeblock-knives-t",k,t,".dxf");
+  echo(fn);
+  translate([0,0, -w/2])
+    linear_extrude(height=w)
+    scale(templatescale) import(file=fn, convexity=100);
+}
+
+module Knife(k){
+  ImportTemplate(bladew, k,"bl");
+  hull(){
+    ImportTemplate(width(k), k,"hl");
+    translate([-100,0,0])
+      ImportTemplate(width(k), k,"hl");
+  }
+}
+
+module DoKnife(k){
+  translate([locations[k],0,0]){
+    rotate([0,90,0])
+      translate([-(handlelenbase + handlelendelta[k]),0,0])
+      Knife(k);
+  }
+}
+
+module DoKnives(){
+  for (k=[0:nknives-1])
+    DoKnife(k);
+}
+
+module ScrewHole(){
+  translate([0,-50,0])
+    rotate([-90,0,0])
+    cylinder(r=screwdia/2, h=150, $fn=40);
+  translate([0, totaldepth-front - screwbackdepth, 0])
+    rotate([90,0,0])
+    cylinder(r=screwcsinkdia/2 / (sqrt(3)/2), h=100, $fn=6);
+}
+
+module PegTemplate(apex){
+  for (mx=[0,1]) for (my=[0,1]) {
+      mirror([mx,0,0]) mirror([0,my,0])
+       polygon([[-0.1,      -0.1],
+                [pegstem/2, -0.1],
+                [pegstem/2, pegstemheight/2],
+                [peghead/2, pegheight    /2],
+                [-0.1,      pegheight    /2 + apex]]);
+    }
+}
+
+module AtSides(){
+  translate([minx,0,0])                 child(0);
+  translate([maxx,0,0]) mirror([1,0,0]) child(1);
+}
+
+module BlockPegSlot(){
+  translate([recessblockwidth/2, pegrecess - peglen, -height]){
+    rotate([-90,0,0]) linear_extrude(height=totaldepth){
+      PegTemplate(peghead/2 * 1.2);
+    }
+  }
+}
+
+module DecorativeIndents(){
+  translate([0, -front, 0])
+  rotate([90,0,0])
+  HexGrid(-height, 0, minx,maxx) {
+    hull(){
+      translate([0, 0, -indentdepth])
+       cylinder(r=indentinnersize/2, h=indentdepth, $fn=40);
+      cylinder(r=indentoutersize/2, h=indentdepth, $fn=40);
+    }
+  }
+}
+
+module Block(){
+  sidemidx = minsideout + screwcsinkdia/2;
+
+  difference(){
+    mirror([0,0,1]) {
+      translate([minx, -front, 0])
+       cube([maxx-minx, totaldepth, height]);
+    }
+    for (x=[minx + sidemidx, maxx - sidemidx]) {
+      translate([x, 0, -screwabove])
+       ScrewHole();
+    }
+    for (yshift=[-1,1])
+      translate([0, yshift * frontbackslop, 0])
+       DoKnives();
+    AtSides() { BlockPegSlot(); BlockPegSlot(); }
+    DecorativeIndents();
+  }
+}
+
+module BlockPrint(){ ////toplevel
+  rotate([0,0,90])
+    Block();
+}
+
+module CoverTemplate(){
+  linear_extrude(height=coverthick)
+    polygon([[minx, 0],
+            [maxx, 0],
+            [maxx, coverlonglen+0.1],
+            [maxx - coverside, coverlonglen+0.1],
+            [minx, covershortlen+0.1]]);
+}
+
+module CoverSide(len){
+  translate([0, 0 ,0]) {
+    rotate([90,0,90])
+      linear_extrude(height=coverside)
+      polygon([[0,                      0],
+              [0,                      totaldepth],
+              [covertopwing,           totaldepth],
+              [covertopwingbase,       coverside + coverthick],
+              [len - covertopwingbase, coverside + coverthick],
+              [len - covertopwing,     totaldepth],
+              [len,                    totaldepth],
+              [len,                    0]]);
+    cube([recessblockwidth, recessblockheight, totaldepth]);
+  }
+}
+
+module Peg(){
+  echo("peg angle slope (must be <1)",
+       (peghead-pegstem)/(pegheight-pegstemheight));
+  dx = pegsloph;
+  dy = pegslopv;
+  rotate([90,0,0]) {
+    linear_extrude(height=peglen-pegslopl) {
+      intersection(){
+       translate([-dx,-dy,0]) PegTemplate(0);
+       translate([-dx,+dy,0]) PegTemplate(0);
+       translate([+dx,+dy,0]) PegTemplate(0);
+       translate([+dx,-dy,0]) PegTemplate(0);
+      }
+    }
+  }
+}
+
+module CoverPegSlot(coverlen){
+  translate([recessblockwidth/2, 0, -1]){
+    linear_extrude(height= 1 + pegrecess + 0.5*peglen){
+      PegTemplate(0);
+    }
+  }
+}
+
+module HolesScope(){
+  intersection_for (dx=[-1,+1]) {
+    intersection_for (dy=[-1,+1]) {
+      translate([dx * holeedge, dy * holeedge, -5])
+       scale([1,1,10])
+       CoverTemplate();
+    }
+  }
+}
+
+module HexGrid(xa,xb,ya,yb) {
+  imin = floor(xa / holepitch);
+  imax =  ceil(xb / holepitch);
+  jmin = floor(ya / (sqrt(3)*holepitch));
+  jmax =  ceil(yb / (sqrt(3)*holepitch));
+  echo("HexGrid ",imin,imax,jmin,jmax);
+  for (i=[imin:imax]) {
+    for (j=[jmin:jmax]) {
+      translate([(j * sqrt(3) + holeoffx) * holepitch,
+                (i +     0.5 + holeoffy) * holepitch,
+                0]) {
+       child();
+       translate([sqrt(3)/2 * holepitch, -0.5 * holepitch, 0])
+         child();
+      }
+    }
+  }
+}
+
+module Hole(){
+  cylinder(r=holesize/2, h=40, $fn=40);
+}
+
+module Holes(){
+  intersection(){
+    translate([0, 0, -20])
+      HexGrid(0, coverlonglen, minx, maxx)
+      Hole();
+    HolesScope();
+  }
+}
+
+module CoverCover(){
+  difference(){
+    CoverTemplate();
+    Holes();
+  }
+}
+
+module Cover(){
+  difference(){
+    union(){
+      CoverCover();
+      AtSides() { CoverSide(covershortlen); CoverSide(coverlonglen); }
+    }
+    AtSides() { CoverPegSlot(); CoverPegSlot(); }
+  }
+}
+
+module CoverAligned(){
+  translate([0,-front,-height])
+    rotate([-90,0,0])
+    Cover();
+}
+
+module DemoPeg(){
+  translate([recessblockwidth/2, pegrecess, -height])
+    Peg();
+}
+
+module Demo(){ ////toplevel
+  %Block();
+  DoKnives();
+  color([0,0,1]) CoverAligned();
+  color([1,0,0]) AtSides() { DemoPeg(); DemoPeg(); }
+}
+
+module Pegs(){ ////toplevel
+  Peg();
+  translate([-peghead-3, 0,0]) Peg();
+}
+
+module CoverParts(){ ////toplevel
+  Cover();
+  translate([0, coverlonglen, pegheight/2-pegslopv])
+    Pegs();
+}
+
+module FrontDemo(){ ////toplevel
+  color([1,0,1]) Block();
+  color([1,0,1]) CoverAligned();
+  color([0,0,0]) DoKnives();
+}
+
+module BlockFrontTest(){ ////toplevel
+  intersection(){
+    Block();
+    translate([minx-10, -front-10, -height-10]) {
+      cube([75,14,35]);
+      cube([75,25,13]);
+    }
+  }
+}
+
+//Block();
+//Demo();
+//Cover();
+//CoverParts();
+//Peg();
+//Cover();
+//Holes();
+//%CoverTemplate();
+//Pegs();
diff --git a/ksafe-base.scad b/ksafe-base.scad
new file mode 100644 (file)
index 0000000..c2e2704
--- /dev/null
@@ -0,0 +1,373 @@
+// -*- C -*-
+
+// from actual ksafe
+bolt_above = 8.50 - 0.50;
+bolthole_height = 4.24 + 1.00;
+wall_thick = 4.50;
+bolthole_width = 16.62 + 1.00;
+main_sz = 149.06 - 0.25;
+cnr_rad = 19.5;
+lidinner_thick_allow = 20.78 + 0.50;
+display_width = 69.81 - 0.50;
+
+dpp3 = [ -5.5, 8.5 ];
+dpp2 = [ -11.0, 7.0 ];
+dpp1 = [ -34.0, 14.0 ];
+
+// other parameters
+web_thick = 4;
+web_height = 20; // excluding wall and base thick
+bolthole_rhs = 20;
+bolthole_lhs = 20;
+boltreinf_thick = 6;
+anchor_wall_space = 25;
+base_thick = 4;
+space = 25;
+anchor_thick = 4;
+anchor_rad = 4;
+bevel = 8;
+string_slot = 3.0;
+string_depth = 6.0;
+thumbslot_depth = 5.0;
+thumbslot_width = 15.0;
+thumbslot_between = 10;
+ksafecover_lip = 4.62;
+display_rhs_secs = 15;
+dcover_endthick = 3.0;
+dcover_mainthick = 5.0;
+dcover_slop_height = 0.35;
+dcover_slop_depth = 0.25;
+dcover_slop_inside = 1.50;
+dcover_commonvertoff = 0.00; // slop_height or slop_inside is added too
+dcover_edge_gap_more_width = 2.0; // each side
+
+// ----- calculated -----
+
+hsz = main_sz/2;
+cut = main_sz + 20;
+
+gppA = [0,0];
+gppB = gppA - [ wall_thick, 0 ];
+
+gppL = [ gppB[0], -(lidinner_thick_allow + space + base_thick) ];
+
+yw1 = -web_thick/2;
+yw2 = yw1 - bolthole_rhs;
+yw3 = yw2 - anchor_thick;
+yw4 = yw3 - anchor_wall_space;
+yw5 = yw4 - wall_thick;
+yw6 = -(hsz - cnr_rad + 0.1);
+
+yw10 = web_thick/2;
+yw11 = yw2 + anchor_wall_space;
+yw12 = yw11 + wall_thick;
+yw13 = -yw6;
+
+cpp1 = dpp1 + [  dcover_slop_depth, dcover_slop_height ];
+cpp2 = [ dpp2[0] - dcover_slop_depth, dpp3[1] + dcover_slop_height ];
+cppH = cpp1 + [ 0, dcover_endthick ];
+cppA = [ cpp2[0], dpp3[1] + dcover_slop_inside ];
+cppK = cppA + [ 0, dcover_mainthick ];
+cppZ = [ -ksafecover_lip, -dcover_commonvertoff ];
+cppD = cppZ + [ 0, -dcover_slop_inside ];
+cppC = [ dcover_slop_inside, cppD[1] ];
+cppF = cppC + dcover_mainthick * [1,-1];
+cppB = [ cppC[0], cppA[1] ];
+cppG = [ cppF[0], cppK[1] ];
+cppE = [ cppD[0], cppF[1] - (cppF[0] - cppD[0]) ];
+
+// anchor
+
+anchor_b = anchor_thick + anchor_rad;
+appM = gppL + anchor_b * [1,1];
+
+a_bevel = 2 * anchor_b * (1 + sqrt(0.5));
+
+module upp_app_Vars(t_bevel){
+  $xppE = gppL + t_bevel * [0,1];
+  $xppF = gppL + t_bevel * [1,0];
+
+  $xppJ = $xppE + wall_thick * [ 1, tan(22.5) ];
+  $xppI = $xppF + base_thick * [ tan(22.5), 1 ];
+
+  // must pass a_bevel for t_bevel for these to be valid
+  $gppP = gppA + [0,-1] * lidinner_thick_allow;
+  $gppQ = $gppP + [1,-1] * web_height;
+  $gppR = $xppJ + [ 1, tan(22.5) ] * web_height;
+  $gppS = $xppI + [ tan(22.5), 1 ] * web_height;
+  $gppT = [ $gppQ[0], $xppE[1] ];
+
+  children();
+}
+
+module upp_app_Profile(){
+  polygon([ gppA,
+           gppB,
+           $xppE,
+           $xppF,
+           $xppF + [1,0],
+           $xppI + [1,0],
+           $xppJ ],
+         convexity=10);
+}
+
+
+module UsualProfile(){
+  upp_app_Vars(bevel) upp_app_Profile();
+}
+
+module NearAnchorProfile(){
+  upp_app_Vars(a_bevel) upp_app_Profile();
+}
+
+module AnchorProfile(){
+  upp_app_Vars(a_bevel) {
+
+    upp_app_Profile();
+
+    difference(){
+      hull(){
+       polygon([ $xppE,
+                 $xppF,
+                 $xppF + [0,1],
+                 $xppE + [1,0] ],
+         convexity=10);
+       translate(appM) circle(r= anchor_b);
+      }
+      translate(appM) circle(r= anchor_rad);
+    }
+  }
+}
+
+module AnchorWallProfile(){
+  UsualProfile();
+  NearAnchorProfile();
+  hull(){
+    for (bev = [bevel, a_bevel]) {
+      upp_app_Vars(bev) {
+       polygon([ $xppE,
+                 $xppF,
+                 $xppI,
+                 $xppJ ],
+         convexity=10);
+      }
+    }
+  }
+}
+
+module WebProfile(){
+  upp_app_Vars(a_bevel){
+    if ($gppR[1] <= $gppP[1]) {
+      polygon([ $gppP,
+               $xppE,
+               $gppT,
+               $gppQ ]);
+      polygon([ $gppP,
+               $xppE,
+               $xppF,
+               $gppS,
+               $gppR ],
+         convexity=10);
+    } else {
+      polygon([ $gppP,
+               $xppE,
+               $xppF,
+               $gppS,
+               $gppP + web_height * [1,0] ],
+         convexity=10);
+    }
+    polygon([ $gppS,
+             $xppF,
+             $xppF + [1,0],
+             $gppS + [1,0] ],
+         convexity=10);
+  }
+}
+
+module SomeBaseProfile(I, F){
+  polygon([ I,
+           F,
+           [ hsz+1, F[1] ],
+           [ hsz+1, I[1] ] ]);
+}
+
+module BaseProfile(){
+  SomeBaseProfile($xppI, $xppF);
+}
+
+module DCoverProfileRaw(){
+  polygon([ cpp1,
+           cpp2,
+           cppA,
+           cppB,
+           cppC,
+           cppD,
+           cppE,
+           cppF,
+           cppG,
+           cppK,
+           cppH ],
+         convexity = 10);
+}
+
+module DCoverProfile(){
+  mirror([1,0])
+    translate(-cppZ)
+    DCoverProfileRaw();
+}
+
+module SWalls(ymin, ymax, t_bevel) {
+  upp_app_Vars(t_bevel) {
+    translate([0,ymin,0])
+      mirror([0,1,0])
+      rotate([90,0,0])
+      linear_extrude(height= ymax-ymin, convexity=10)
+      for (xm=[0,1])
+       mirror([xm,0])
+         translate([-hsz, 0])
+           children();
+  }
+}
+
+module AtTwoCorners(){
+  for (xm=[0,1]) {
+    mirror([xm,0,0]) 
+    translate((hsz - cnr_rad) * [1,1])
+    intersection(){
+      rotate_extrude(convexity=10)
+       translate([-cnr_rad,0])
+       children();
+      translate([0,0,-250])
+       cube([50,50,500]);
+    }
+  }
+}
+
+module Box(){
+  /// corners, and front and back of base
+  for (ym=[0,1]) mirror([0,ym,0]) {
+    AtTwoCorners(){
+      UsualProfile();
+    }
+    hull() AtTwoCorners(){
+      upp_app_Vars(bevel){
+       polygon([ $xppI,
+                 $xppF,
+                 $xppF + [0.1, 0],
+                 $xppI + [0.1, 0]
+                 ]);
+      }
+    }
+  }
+
+  // side walls and base
+  SWalls(yw6 , yw4 , bevel  ) { UsualProfile();      BaseProfile(); }
+  SWalls(yw5 , yw4 , a_bevel) { AnchorWallProfile(); BaseProfile(); }
+  SWalls(yw5 , yw12, a_bevel) { NearAnchorProfile(); BaseProfile(); }
+  SWalls(yw3 , yw2 , a_bevel) { AnchorProfile();     BaseProfile(); }
+  SWalls(yw11, yw12, a_bevel) { AnchorWallProfile(); BaseProfile(); }
+  SWalls(yw11, yw13, bevel  ) { UsualProfile();      BaseProfile(); }
+  SWalls(yw1,  yw10, a_bevel) { WebProfile(); SomeBaseProfile($gppS, $xppF); }
+
+  // front and rear walls
+  rotate([0,0,90]) SWalls(yw6, yw13, bevel) UsualProfile();
+}
+
+module DCover(){ ////toplevel
+  translate([ -display_width/2, -hsz, 0 ])
+    rotate([0,90,0])
+    rotate([0,0,90])
+    linear_extrude( display_width - display_rhs_secs, convexity = 10)
+    DCoverProfile();
+}
+
+module DCoverSupportAllowance(){
+  translate([0, -hsz, 0])
+    cube(center=true,
+        [ display_width + 2 * dcover_edge_gap_more_width,
+          wall_thick * 2,
+          dcover_slop_inside * 2 + 0.01 ]);
+}
+
+module BoltHoles(){
+  translate([0,0, -(bolt_above + 0.5 * bolthole_height)])
+    cube(center=true, [ cut, bolthole_width, bolthole_height ]);
+}
+
+module KsafeBase(){ ////toplevel
+  DCover();
+
+  difference(){
+    Box();
+
+    BoltHoles();
+
+    // string slot
+    translate([ -cut,
+               -(bolthole_width/2 + bolthole_rhs),
+               1 ])
+      mirror([0,1,0]) mirror([0,0,1])
+      cube([ cut*2,
+            string_slot,
+            lidinner_thick_allow + string_depth + 1 ]);
+
+    // thumb slots
+    for (mx=[0,1]) mirror([mx,0,0]) {
+      translate([ thumbslot_between/2,
+                 0,
+                 -thumbslot_depth ])
+       cube([ thumbslot_width,
+              cut,
+              thumbslot_depth+1 ]);
+    }
+
+    DCoverSupportAllowance();
+  }
+}
+
+module DemoProfiles(){ ////toplevel
+  translate([0,0,-2]) color("yellow") AnchorWallProfile();
+  color("red") AnchorProfile();
+  translate([0,0,2]) color("black") NearAnchorProfile();
+  translate([0,0,4]) color("blue") UsualProfile();
+  translate([0,0,-4]) color("pink") WebProfile();
+  translate([0,0,6]) color("purple") DCoverProfile();
+}
+
+module RimTest(){ ////toplevel
+  intersection(){
+    Box();
+    cube(center=true, [ main_sz*2, main_sz*2,
+                       2.5 ]);
+  }
+}
+
+module DCoverTest(){ ////toplevel
+  intersection(){
+    difference(){
+      union(){
+       Box();
+       DCover();
+      }
+      DCoverSupportAllowance();
+      BoltHoles();
+    }
+    translate([0,0,60])
+    cube(center=true, [ main_sz*2, main_sz*2,
+                       2 * (60 + 10) ]);
+  }
+}
+
+module BoltTest(){ ////toplevel
+  dy = 0.5 * (bolthole_width+4);
+  intersection(){
+    KsafeBase();
+    translate([ 0, -dy, -(bolt_above + bolthole_height + 1) ])
+      cube([ main_sz, dy*2, 50 ]);
+  }
+}
+
+//DemoProfiles();
+//Box();
+//KsafeBase();
+//RimTest();
diff --git a/laptop-camera-tripod-bracket.scad b/laptop-camera-tripod-bracket.scad
new file mode 100644 (file)
index 0000000..cc6ffb2
--- /dev/null
@@ -0,0 +1,76 @@
+// -*- C -*-
+
+// High Detail
+// support enabled
+//   distance x/y 2.5mm
+// fill density 30%
+
+laptop_w       = 310;
+laptop_th_rear  = 14;
+laptop_th_front = 11;
+laptop_halfdepth_real = 125;
+laptop_halfdepth = 115; //125;
+laptop_inner_x_clear = 95;
+
+include <utils.scad>
+include <camera-mount.scad>
+
+bar_w = 15;
+bar_th = 12;
+flex_allow = 3;
+claw_th = 6;
+claw_overlap = 15;
+mount_dia = 50;
+
+min_ceil = 1;
+
+//$test=true;
+$test=false;
+
+module ClawProfile(laptop_th){
+  laptop_zmin = bar_th + flex_allow;
+  laptop_zmax = laptop_zmin + laptop_th;
+  difference(){
+    rectfromto([-claw_overlap, 0],
+              [claw_th, laptop_zmax + claw_th]);
+    rectfromto([-claw_overlap-1, laptop_zmin ],
+              [ 0, laptop_zmax ]);
+  }
+}
+
+module ClawBar(inner_len, laptop_th){
+  rotate([0,0,180]) linextr_y_xz(-bar_w/2, +bar_w/2) {
+    rectfromto([0,0],
+              [inner_len, bar_th]);
+    translate([inner_len, 0])
+      ClawProfile(laptop_th);
+  }
+}
+
+module Body(){
+  translate([0, laptop_halfdepth - laptop_halfdepth_real, 0])
+    for (m=[0,1]) {
+      mirror([m,0]) {
+       ClawBar(laptop_w/2, laptop_th_rear);
+       translate([ laptop_w/2 - laptop_inner_x_clear + bar_w/2, 0])
+         rotate([0,0,-90])
+         ClawBar(laptop_halfdepth, laptop_th_front);
+      }
+    }
+  cylinder(r = mount_dia/2, h= bar_th);
+}
+
+module Bracket(){
+  difference(){
+    Body();
+    rotate([0,180,0])
+      CameraMountThread( bar_th + 2 );
+  }
+  translate([0,0, bar_th-min_ceil])
+    cylinder(r= mount_dia/2-1, h=min_ceil);
+}
+
+//ClawProfile(laptop_th_front);
+//ClawBar(125, laptop_th_front);
+//Body();
+Bracket();
diff --git a/laptop-sound-cable-hooks.scad b/laptop-sound-cable-hooks.scad
new file mode 100644 (file)
index 0000000..8cfd468
--- /dev/null
@@ -0,0 +1,145 @@
+// -*- C -*-
+
+include <utils.scad>
+
+wall_th = 2;
+hook_th = 4;
+hook_hole = 4;
+hook_w_min = 6;
+hook_hook = 12;
+
+plug_entry_gap = 2.0;
+
+plug_l_d = [[ 27.78,
+             10.62 + 0.75 ],
+           [ 40.88,
+             8.56 + 0.75 ],
+           ];
+
+plug_stem = [ 2.72 + 0.50,
+             5.20 + 0.50 ];
+
+palmrest_from_plug_z = 3.98;
+laptop_th = 16.31 + 0.75;
+
+tongue_len = 50;
+
+// calculated
+
+hook_th_plug_holder =
+  plug_l_d[0][1]/2 + wall_th * sin(22.5);
+
+hook_tongue_h = hook_hole + wall_th*2;
+
+plug_l_d_smallest = plug_l_d[len(plug_l_d)-1];
+plug_hook_x_min = -plug_l_d_smallest[0] - wall_th;
+plug_hook_z_start = -plug_l_d_smallest[1]/2 - wall_th;
+
+z_laptop_base = palmrest_from_plug_z - laptop_th;
+z_hook_min = z_laptop_base - hook_tongue_h;
+
+module PlugMainPlan() {
+  for (l_d = plug_l_d) {
+    l = l_d[0];
+    d = l_d[1];
+    rectfromto([ -l, -d/2 ],
+              [  0, +d/2 ]);
+  }
+}
+
+module PlugHolderPlan() {
+  intersection(){
+    hull()
+      offset(r= wall_th)
+      PlugMainPlan();
+
+    rectfromto([-100,-100], [-plug_entry_gap,+100]);
+  }
+}
+
+module PlugHookHookPlan(){
+  polygon([ [ plug_hook_x_min, 0 ],
+           [ plug_hook_x_min, plug_hook_z_start ],
+           [ plug_hook_x_min + (plug_hook_z_start - z_hook_min),
+             z_hook_min ],
+           [ -plug_entry_gap, z_hook_min ],
+           [ -plug_entry_gap, 0 ],
+           ]);
+}
+
+module TonguePlan(){
+  difference(){
+    rectfromto([ -plug_entry_gap - 1, z_hook_min ],
+              [ tongue_len, z_laptop_base ]);
+    translate([ tongue_len - wall_th - hook_hole/2,
+               z_hook_min + wall_th + hook_hole/2 ])
+      circle(r = hook_hole/2);
+  }
+}
+
+module FarHookPlan(){
+  mirror([1,0,0])
+    TonguePlan();
+
+  rectfromto([ 0, z_hook_min ],
+            [ hook_w_min, palmrest_from_plug_z + 0.1]);
+
+  translate([0, palmrest_from_plug_z])
+    rectfromto([ -hook_hook, 0 ],
+              [ hook_w_min, hook_w_min ]);
+}
+
+module RotateIntersect(n=6){
+  intersection_for (r = [0:n-1]) {
+    rotate([r/n * 360,0,0])
+      linextr(-100,100) children(0);
+  }
+}
+
+module PlugHolder(){
+  difference(){
+    union(){
+      RotateIntersect(8)
+       PlugHolderPlan();
+
+      rotate([0,0,180]) {
+       linextr_y_xz(-hook_th_plug_holder/2,
+                    +hook_th_plug_holder/2)
+         PlugHookHookPlan();
+
+       linextr_y_xz(-hook_th/2,
+                    +hook_th/2)
+         TonguePlan();
+      }
+    }
+
+    RotateIntersect(6)
+      PlugMainPlan();
+
+    linextr(-plug_stem[1]/2, 100)
+      rectfromto([ -100, -plug_stem[0]/2 ],
+                [ +100, +plug_stem[0]/2 ]);
+  }
+}
+
+module PlugHolderPrint(){ ////toplevel
+  render() PlugHolder();
+}
+
+module FarHookPrint(){ ////toplevel
+  linextr(0, hook_th)
+    FarHookPlan();
+}
+
+module DemoPlan() { ////toplevel
+  translate([0,0,-10]) color("grey") PlugHolderPlan();
+  PlugMainPlan();
+  translate([0,0,-5]) color("blue") {
+    PlugHookHookPlan();
+    TonguePlan();
+  }
+
+  translate([0,40,0]) {
+    FarHookPlan();
+  }
+}
diff --git a/led-panel-ceiling-bracket.scad b/led-panel-ceiling-bracket.scad
new file mode 100644 (file)
index 0000000..8f39a5b
--- /dev/null
@@ -0,0 +1,56 @@
+// -*- C -*-
+
+holespc = 20;
+
+len = 50;
+width = 20;
+backspc = 8;
+extra_height = 2;
+light_height = 12.5;
+hole_from_back = 7.2 + 0.5;
+hole_dia = 3.5 + 0.5;
+bolthead_dia = 7 + 1.0;
+bolthead_depth = 5 + 20 - 12 + 2.7/2;
+
+plasfix_dia = 4.5 + 0.5 + 1.1;
+plasfix_head = 8.7 + 0.5 + 1.1;
+plasfix_headdep = plasfix_dia;
+plasfix_sink = 8;
+
+height = light_height + backspc + extra_height;
+
+octagon_fudge = 1/cos(22.5);
+
+echo("remain", width - bolthead_depth);
+
+module Bracket(){
+  difference(){
+    translate([-len/2, 0, 0])
+      cube([len, width, height]);
+    for (xsgn=[-1,+1]) {
+      translate([xsgn * holespc/2, -1,
+                light_height - hole_from_back + extra_height]) {
+       rotate([-90,0,0]) {
+         rotate([0,0,360/8/2]) {
+           cylinder(r=bolthead_dia/2 * octagon_fudge,
+                    h= bolthead_depth +1, $fn=8);
+           cylinder(r=hole_dia/2 * octagon_fudge,
+                    h=50, $fn=8);
+         }
+       }
+      }
+    }
+    translate([0, width/2, 0]) {
+      cylinder(r= plasfix_dia/2, h=50, $fn=20);
+      translate([0,0,-1])
+       cylinder(r= plasfix_head/2, h= plasfix_sink + 1, $fn=20);
+    }
+  }
+}
+
+module BracketPrint(){
+  rotate([0,180,0])
+    Bracket();
+}
+
+BracketPrint();
diff --git a/led-panel-ceiling-bracket.slic3r b/led-panel-ceiling-bracket.slic3r
new file mode 100644 (file)
index 0000000..612eb56
--- /dev/null
@@ -0,0 +1,3 @@
+bottom_solid_layers = 3
+top_solid_layers = 3
+perimeters = 3
diff --git a/lemon-stand.scad.pl b/lemon-stand.scad.pl
new file mode 100755 (executable)
index 0000000..8c1d10f
--- /dev/null
@@ -0,0 +1,164 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Math::Trig;
+use Math::Vector::Real;
+use IO::File;
+use Data::Dumper;
+use constant tau => pi*2;
+
+my $ellipse = 25 / 2;
+my $circle = 7 / 2;
+my $channelh = 3;
+my $channelw = 4;
+my $xscale = 35 / 25;
+my $N = 180; # around ellipse
+my $M = 80; # around each circle
+my @channeldistprops = (0, 1/3, 2/3);
+
+my $NMdiv = $ENV{'LEMONSTAND_COARSE'} || 1;
+
+$M /= $NMdiv;
+$N /= $NMdiv;
+
+print <<END;
+// -*- C -*-
+// *** AUTOGENERATED - DO NOT EDIT ***
+END
+
+print "torusyup = ", ($circle / sqrt(2)), ";\n";
+
+our @ellipse = map {
+    my $theta = tau * $_ / $N;
+    V( cos($theta) * $ellipse * $xscale, sin($theta) * $ellipse, 0 )
+} 0..($N-1);
+
+#print Dumper(\@ellipse);
+
+our @alongs = map {
+    my $i = $_;
+    $ellipse[ ($i+1) % $N ] - $ellipse[ ($i-1) % $N];
+} 0..($N-1);
+
+our @circles = map {
+    my $i = $_;
+    my $centre = $ellipse[$i];
+    my $axis = $alongs[$i]->versor();
+    my $rad0 = $axis x V(0,0,1);
+    my $rad1 = $rad0 x $axis;
+    [ map {
+       my $theta = tau * $_ / $M;
+       $centre + $circle * ($rad0 * cos($theta) + $rad1 * sin($theta));
+    } 0..($M-1) ];
+} 0..($N-1);
+
+sub scadvec ($) {
+    my ($v) = @_;
+    return "[ ".(join ", ", @$v)." ]"
+}
+
+sub torusy () {
+    print "module Torusy(){ polyhedron(points=[";
+    my $ptix = 0;
+    my @cirpt;
+    foreach my $i (0..$N-1) {
+       foreach my $j (0..$M-1) {
+           print "," if $ptix;
+           print "\n";
+           print "    ",(scadvec $circles[$i][$j]);
+           $cirpt[$i][$j] = $ptix++;
+       }
+    }
+    print "\n  ],\n";
+
+    print "  faces=[";
+    foreach my $i (0..$N-1) {
+       my $i2 = ($i+1) % $N;
+       foreach my $j (0..$M-1) {
+           my $j2 = ($j+1) % $M;
+           print "," if $i || $j;
+           print "\n";
+           print "   [ ", (join ", ",
+                           $cirpt[ $i  ][ $j  ],
+                           $cirpt[ $i  ][ $j2 ],
+                           $cirpt[ $i2 ][ $j  ],
+                          ), " ],";
+           print "   [ ", (join ", ",
+                           $cirpt[ $i  ][ $j2 ],
+                           $cirpt[ $i2 ][ $j2 ],
+                           $cirpt[ $i2 ][ $j  ],
+                          ), " ]";
+       }
+    }
+    print "\n  ]);\n}\n";
+}
+
+torusy();
+
+
+our @distances;
+push @distances, 0;
+foreach my $i (1..$N) {
+    my $dist = $distances[ $i-1 ];
+    $dist += abs($ellipse[$i % $N] - $ellipse[$i-1]);
+    $distances[$i] = $dist;
+}
+
+sub infodistprop ($) {
+    my ($distprop) = @_;
+    # returns
+    #   ( $ellipse_centreline_point,
+    #     $along_vector )
+    my $dist = $distprop * $distances[$N];
+    foreach my $i (0..$N-1) {
+       my $prorata =
+           ($dist - $distances[$i]) /
+           ($distances[$i+1] - $distances[$i]);
+       next unless 0 <= $prorata && $prorata <= 1;
+       print "// infodistprop $distprop => #$i=$ellipse[$i] $prorata $ellipse[$i+1]\n";
+       return (
+               (1-$prorata) * $ellipse[$i] + ($prorata) * $ellipse[$i+1],
+               $alongs[$i],
+              );
+    }
+    die "$distprop ?";
+}
+
+sub channels(){
+    print "module Channels(){\n";
+    
+    foreach my $cdp (
+                    (map { 0.5 *  $_    } @channeldistprops),
+                    (map { 0.5 * ($_+1) } @channeldistprops),
+                   ) {
+       my ($ctr, $along) = infodistprop($cdp);
+       my $angle = atan2(-$along->[0], $along->[1]);
+       print "  translate(",scadvec($ctr),")\n";
+       print "  rotate([0,0,$angle*360/",tau,"])\n";
+       print "  rotate([0,90,0])\n";
+       print "  translate([0,0, -2*$circle])\n";
+       print "  scale([1, $channelw/$channelh/2, 1])\n";
+       print "  rotate([0,0,360/8/2])\n";
+       print "  cylinder(r=$channelh, h=4*$circle, \$fn=8);\n";
+    }
+    print "}\n";
+}
+
+channels();
+
+while (<DATA>) { print };
+
+STDOUT->error and die $!;
+STDOUT->flush or die $!;
+
+__DATA__
+module Stand(){
+    difference(){
+       translate([0,0,torusyup])
+           Torusy();
+       Channels();
+       translate([-200,-200,-50])
+           cube([400,400,50]);
+    }
+}
+Stand();
diff --git a/light-bracket.scad b/light-bracket.scad
new file mode 100644 (file)
index 0000000..c49dc09
--- /dev/null
@@ -0,0 +1,139 @@
+shrinkage = 1.0126; // width of 56.2 gives 55.5
+remote_width= 56.35 * shrinkage;
+remote_height=124.7 * shrinkage;
+remote_thick=6.1; // height of 6.8 gives 6.3
+mainhole_thick=remote_thick+1;
+hook_hook_thick=1.5;
+hook_stem_thick=1.5;
+hook_hook_len=1.5;
+base_thick=3.5;
+base_margin=3.0;
+base_width=remote_width-base_margin*2;
+base_height=remote_height-base_margin*2;
+base_edgewidth=4;
+
+screw_ys=[ 28, remote_height-28 ];
+
+// origin is base of mainhole
+
+module mainhole() {
+       translate([ -remote_width/2, 0, 0 ])
+       cube(center=false,
+               size=[ remote_width, remote_height, mainhole_thick ] );
+}
+
+module hhook(extent) {
+       translate([ -hook_stem_thick, 0, -base_thick*2 ])
+               cube(center=false,
+                       size=[
+                               hook_stem_thick,
+                               extent,
+                               base_thick*2 + mainhole_thick + hook_hook_thick
+                       ]);
+       translate([ -hook_stem_thick, 0, -base_thick*2 ])
+               cube(center=false,
+                       size=[
+                               hook_stem_thick+base_margin+base_edgewidth-1,
+                               extent,
+                               base_thick*2
+                       ]);
+       translate([ -hook_stem_thick+1.0, 0, mainhole_thick ])
+               rotate(v=[0,1,0], a=-30)
+               cube(center=false,
+                       size=[
+                               3,
+                               extent,
+                               hook_hook_thick
+                       ]);
+       //difference() {
+       //      #translate([ -hook_stem_thick, 0, -base_thick*2 ])
+       //              cube(center=false,
+       //                      size=[
+       //              hook_stem_thick+base_margin+base_edgewidth-1,
+       //              extent,
+       //              base_thick*2 + mainhole_thick + hook_hook_thick
+       //                              ]);
+       //      translate([hook_hook_len, -5, 0])
+       //               cube(center=false, size=[ 20, extent+10, 30 ]);
+       //}
+}
+
+module hhookside(extent) {
+       translate([ -remote_width/2, 0, 0 ])
+               hhook(extent);
+}
+
+module hhookbot(extent) {
+       rotate(a=90, v=[0,0,1]) hhook(extent);
+}
+
+module hstuff() {
+       translate([0,70,0]) hhookside(15);
+       translate([0,10,0]) hhookside(15);
+       translate([-10,0,0]) hhookbot(15);
+}
+
+module slashes() {
+       for (y=[-30 : 60 : +40])
+               translate([0,y,0])
+                       rotate(v=[0,0,1],a=45)
+                               cube(center=true, [ 5,200,200 ]);
+}
+
+module base() {
+       translate([ 0, base_height/2 + base_margin, -base_thick/2 ])
+       intersection() {
+               cube(center=true,
+                       [ base_width, base_height, base_thick+10 ]);
+               union() {
+                       difference() {
+                               cube(center=true, [ 200,200,200 ]);
+                               cube(center=true,
+                                       [ base_width - base_edgewidth*2,
+                                         base_height - base_edgewidth*2,
+                                         base_thick + 15 ]);
+                               
+                       }
+                       slashes();
+                       mirror([1,0,0]) slashes();
+               }
+       }
+//     translate([-base_width/2, base_margin, -base_thick*2])
+//             cube(center=false, [base_width,base_height,base_thick+10]);
+}
+
+module stuff() {
+       hstuff();
+       mirror([1,0,0]) hstuff();
+       base();
+       for (y=screw_ys) translate([0, y, -20])
+               cylinder(r=6.5, h=21);
+}
+
+module screwhole(holedia, csdia) {
+       // screw goes along z axis downwards
+       // origin is top of head
+       // results are positive, so this should be subtracted
+       translate([0,0,-100]) cylinder(h=200, r=holedia/2);
+       csdist=(csdia-holedia)/2;
+       translate([0,0,-csdist]) cylinder(h=csdist, r1=holedia/2, r2=csdia/2);
+       cylinder(h=100, r=csdia/2);
+}
+
+module bracket() {
+       // this is the actual thing we want
+       difference() {
+               stuff();
+               mainhole();
+               for (y=screw_ys) translate([0, y, 0])   
+                       screwhole(5.4,10); //dia=4 gives 2.9
+                                        //holedia=10 gives 9.0 want 7.0
+               translate([0,0,-50 - base_thick])
+                       cube(center=true,[300,300,100]); // print bed
+       }
+}
+
+intersection() {
+       !bracket();
+       translate([-50,6,-50]) #cube(center=false, [100,27,100]);
+}
diff --git a/lipo-flat-mount.scad b/lipo-flat-mount.scad
new file mode 100644 (file)
index 0000000..865b6d3
--- /dev/null
@@ -0,0 +1,184 @@
+// -*- C -*-
+
+include <nutbox.scad>
+include <utils.scad>
+
+// pimoroni 3000mAh
+battery = [ 51.5,
+           81.3,
+           8.0 ];
+
+battery_wall = 2.0;
+battery_wall_unsupp = 4.0;
+battery_wall_top_gap = 0.75;
+
+battery_keeper_m_th = 4;
+battery_keeper_m_w = 4;
+battery_keeper_x_gap = 0.75; // each side
+battery_keeper_y_gap_overhang = 0.75;
+battery_keeper_y_gap_nutbox = 1.0;
+battery_keeper_z_gap_nutbox = 0.50;
+battery_keeper_z_gap_overhang = 0.75;
+
+battery_keeper_x_th_min = 1.5 * 1.5;
+battery_keeper_x_th_max = 2.5 * 1.5;
+battery_keeper_x_w  = 5;
+battery_keeper_x_n  = 5;
+
+battery_keeper_screw_x_off = -2.5;
+
+battery_wire_y = 4;
+
+battery_nutbox = nutbox_data_M3;
+
+// calculated
+
+battery_fix_sz = NutBox_outer_size(battery_nutbox);
+
+battery_nutbox_z = max( battery[2] + battery_wall_top_gap,
+                       NutBox_h_base(battery_nutbox) );
+battery_keeper_overhang_z = battery[2] + battery_keeper_m_th
+  + battery_keeper_z_gap_overhang;
+battery_keeper_overhang_wall = battery_keeper_m_w;
+battery_keeper_overhang_th = battery_keeper_m_th;
+
+// calculated outputs
+
+battery_keeper_tab_top_z = battery_nutbox_z
+     + battery_keeper_z_gap_nutbox + battery_keeper_m_th;
+     // NB does not include screw head
+
+battery_keeper_legs_top_z = battery_keeper_overhang_z
+                            + battery_keeper_overhang_th;
+
+battery_keeper_frame_top_z = battery[2] + battery_keeper_m_th;
+
+battery_mount_y_min = -battery_fix_sz;
+battery_mount_y_max = battery[1] + battery_wall;
+battery_mount_x_sz  = battery[0] + battery_wall_unsupp*2;
+
+module BatteryPlan(){
+  rectfromto([ -battery[0]/2, 0          ],
+            [ +battery[0]/2, battery[1] ]);
+}
+
+module BatteryBase(){ ////toplevel
+  // wall
+  linextr(-0.1, battery[2] - battery_wall_top_gap, convexity=7) {
+    difference(){
+      union(){
+       offset(r=battery_wall) BatteryPlan();
+       rectfromto([ 0,0 ],
+                  [ -(battery[0]/2 + battery_wall_unsupp), battery[1]/3 ]);
+      }
+      BatteryPlan();
+      rectfromto([ battery_fix_sz/2 - 0.5
+                  + battery_keeper_screw_x_off, -30 ],
+                [ -battery[0], battery_wire_y ]);
+    }
+  }
+
+  // nutbox
+  translate([battery_keeper_screw_x_off, -battery_fix_sz/2, battery_nutbox_z])
+    NutBox(battery_nutbox, battery_nutbox_z + 0.1);
+
+  // overhang for legs at rear
+  for (m=[0,1]) {
+    mirror([m,0,0]) {
+      translate([ battery[0]/2, battery[1], 0]) {
+       difference(){
+         linextr(-0.1,
+                 battery_keeper_overhang_z
+                 + battery_keeper_overhang_th,
+                 convexity=1)
+           rectfromto([ -battery_keeper_m_w*2, -battery_keeper_m_w ],
+                      [ battery_keeper_overhang_wall, battery_wall ]);
+         linextr(-1, battery_keeper_overhang_z,
+                 convexity=1)
+           rectfromto([-20, -20], [0,0]);          
+       }
+      }
+    }
+  }
+}
+
+module BatteryKeeper(){ ////toplevel
+  // A-frame
+  translate([0,0, battery[2]]) {
+    linextr(0, battery_keeper_m_th) {
+      intersection(){
+       // main legs
+       translate([0,  +battery[1], 0])
+         multmatrix([[ 1, -battery_keeper_screw_x_off/battery[1], 0, 0, ],
+                     [ 0,1,0, 0, ],
+                     [ 0,0,1, 0, ]])
+         translate([0, -battery[1], 0])
+       for (sx=[-1,+1]) {
+         multmatrix([[ 1,0,0, 0, ],
+                     [ 0,1,0, 0, ],
+                     [ 0,0,1, 0, ]] +
+                    sx *
+                    ( battery[0]/2 - 0.5 * battery_keeper_m_w
+                      - battery_keeper_x_gap ) /
+                    ( battery[1] - 0.5 * battery_keeper_m_w )
+                    *
+                    [[ 0,1,0, 0, ],
+                     [ 0,0,0, 0, ],
+                     [ 0,0,0, 0, ]])
+           rectfromto([ -battery_keeper_m_w/2,
+                        battery_keeper_y_gap_nutbox ],
+                      [ +battery_keeper_m_w/2,
+                        battery[1] - battery_keeper_y_gap_overhang ]);
+       }
+
+       // shape to round off the leg end corners
+       hull(){
+         for (sx=[-1,+1]) {
+           translate([ sx * ( battery[0]/2 - battery_keeper_m_w/2
+                              -battery_keeper_x_gap) ,
+                       battery[1] - battery_keeper_m_w/2
+                       -battery_keeper_y_gap_overhang ])
+             circle(r = battery_keeper_m_w/2);
+         }
+         square(center=true, [ battery[0], 1 ]);
+       }
+      }
+    }
+
+    // x struts
+    for (i=[0 : battery_keeper_x_n-1]) {
+      linextr(0,
+             battery_keeper_x_th_min +
+             (battery_keeper_x_th_max - battery_keeper_x_th_min)
+             * pow( i/(battery_keeper_x_n-1)*2 - 1 , 2)
+             ) {
+       difference(){
+         translate([0, battery[1] * ((i + 0.5) / battery_keeper_x_n)])
+           square(center=true, [ battery[0], battery_keeper_x_w ]);
+         children(0);
+       }
+      }
+    }
+  }
+
+  // tab for screw and nutbox
+  translate([battery_keeper_screw_x_off,
+            0,
+            battery_nutbox_z + battery_keeper_z_gap_nutbox])
+    linextr(0, battery_keeper_m_th, convexity=4) {
+    difference(){
+      rectfromto([ -battery_fix_sz/2, -battery_fix_sz ],
+                [ +battery_fix_sz/2,
+                  0.5 * battery[1] / battery_keeper_x_n +
+                  0.5 * battery_keeper_m_w ]);
+      translate([ 0, -battery_fix_sz/2 ])
+       circle(r = battery_nutbox[0]/2);
+    }
+  }
+}
+
+module BatteryDemo(){ ////toplevel
+  color("grey") BatteryBase();
+  BatteryKeeper() { union(){ } }
+}
+
diff --git a/lock-inframe-bracket.scad b/lock-inframe-bracket.scad
new file mode 100644 (file)
index 0000000..bfa6e39
--- /dev/null
@@ -0,0 +1,349 @@
+// -*- C -*-
+
+// use shell thickness 1.50
+// use fill density 40%
+
+include <funcs.scad>
+
+tube_dia = 27.5 + 1.625 + 1.32;
+lock_w = 42.5 + 0.5;
+lock_d = 28.0 + 0.5;
+main_h = 45.0;
+backflange_d = 12;
+backflange_hole_dy = -1;
+lockshaft_dia = 14.35;
+
+cliprecess_h = 16;
+total_h = 75;
+
+back_gap = 12.5;
+main_th = 4.50;
+tube_th = 5.50;
+
+midweb_d = 3;
+clip_th = 3.5;
+clip_gap = 2.5;
+clip_d = 22.0;
+
+mountscrew_dia = 4 + 0.5;
+clipbolt_dia = 5 + 0.6;
+
+mountscrew_washer = 10;
+
+backflange_th = 4.5;
+
+$fn=50;
+
+join_cr = 9;
+
+tube_rear_extra_th = 1;
+
+divide_shaft_w = 1.75;
+divide_shaft_l = 1.5;
+divide_head_dx = 1.75;
+divide_head_th = 1.5;
+divide_gap = 0.75;
+
+divide_angle = 26;
+divide_fudge_r = 4.75;
+
+backflange_angle = 20;
+
+// calculated
+
+lockshaft_r = [1, 1] * lockshaft_dia / 2;
+front_th = main_th;
+
+tube_or = tube_dia/2 + tube_th;
+back_ohw = back_gap/2 + backflange_th;
+backflange_ymin = tube_dia/2 + backflange_d;
+
+lock_0y = tube_dia/2 + lock_d/2 + midweb_d;
+lock_0x = lock_w/2 - lock_d/2;
+lock_0 = [lock_0x,lock_0y];
+
+lock_or = [lock_w, lock_d]/2 + [front_th,front_th];
+
+module oval(sz){ // sz[0] > sz[1]
+  xr = sz[0];
+  yr = sz[1];
+  hull(){
+    for (sx=[-1,+1]) {
+      translate([sx * (xr-yr), 0])
+       circle(r=yr);
+    }
+  }
+}
+
+module JoinCircs(jr){
+  // http://mathworld.wolfram.com/Circle-CircleIntersection.html
+  R = tube_or + join_cr;
+  r = lock_or[1] + join_cr;
+  d = dist2d( [0,0], lock_0 );
+  x = (d*d - r*r + R*R) / (2*d);
+  y = sqrt( R*R - x*x );
+
+  echo(lock_0x, lock_0y, R,r, d, x,y);
+
+  for (m=[0,1]) {
+    mirror([m,0]) {
+      rotate(atan2(lock_0y, lock_0x)) {
+       translate([x,-y])
+         circle(r= jr);
+      }
+    }
+  }
+}
+
+module DivideHook(){ ////toplevel
+  w = tube_th/2;
+  d = divide_gap;
+
+  translate([-1,0] * (w + d + w)){
+    for (sx=[-1,+1])
+      translate([-(w + w+d) * sx, 0]) circle(r= w);
+    difference(){
+      circle(r = 3*w + d);
+      circle(r =   w + d);
+      translate([-10*w, -10*w]) square([20*w, 10*w]);
+    }
+  }
+}
+
+module DivideCut(){
+  w = tube_th/2;
+  d = divide_gap;
+  br = tube_dia/2 + tube_th;
+
+  difference(){
+    offset(r=divide_gap) DivideHook();
+    DivideHook();
+    translate([-2*w,0]) mirror([0,1]) square([4*w, 4*w]);
+  }
+}
+
+module DivideCutB(){
+  w = tube_th/2;
+  d = divide_gap;
+  br = tube_dia/2 + tube_th;
+  
+  intersection(){
+    translate([br - tube_th/2,0]) {
+      difference(){
+       circle(r=br + d);
+       circle(r=br);
+      }
+    }
+    translate([-2*w, 0]) mirror([0,1]) square(4*w);
+  }
+}
+
+module DivideSurround(){
+  w = tube_th/2;
+  d = divide_gap;
+
+  offset(r= w*2) {
+    hull() {
+      DivideCut();
+      translate([-(4*w + 2*d), 8*w]) circle(r=w);
+    }
+  }
+}
+
+module DivideInPlace(){
+  rotate([0,0, -divide_angle])
+    translate([ -tube_dia/2 -tube_th/2, 0])
+    children();
+}
+
+module MainPlan(){ ////toplevel
+  difference(){
+    union(){
+      difference(){
+       union(){
+         hull(){
+           for (t=[0, tube_rear_extra_th])
+             translate([0, -t])
+               circle(r = tube_or);
+         }
+         rotate([0,0, backflange_angle])
+           translate([-back_ohw,0]) mirror([0,1])
+           square([back_ohw*2, backflange_ymin]);
+
+         translate([0, lock_0y]){
+           oval(lock_or);
+         }
+
+         hull(){
+           JoinCircs(0.01);
+           polygon([[0,0], lock_0, [-lock_0[0], lock_0[1]]]);
+         }
+       }
+
+       rotate([0,0, backflange_angle])
+         translate([-back_gap/2,1]) mirror([0,1])
+         square([back_gap, backflange_ymin+2]);
+
+       JoinCircs(join_cr);
+      }
+
+      DivideInPlace() DivideSurround();
+    }
+    translate([0, lock_0y]){
+      oval([lock_w/2, lock_d/2]);
+    }
+
+    circle(r = tube_dia/2);
+
+    DivideInPlace() DivideCut();
+    DivideInPlace() DivideCutB();
+  }
+}
+
+lockshaft_or = lockshaft_r + [clip_th,clip_th];
+cliprecess_ymax = cliprecess_h - lockshaft_r[1];
+clip_ymin = cliprecess_ymax - main_h;
+clip_ogap = clip_gap + clip_th*2;
+
+module ClipElevationPositive(){
+  hull(){
+    oval(lockshaft_or);
+    translate([0, -lockshaft_or[1] * sqrt(2)])
+      square(center=true, 0.5);
+  }
+  translate([-lockshaft_or[0], 0])
+    square([lockshaft_or[0]*2, cliprecess_ymax]);
+  translate([-clip_ogap/2, 0]) mirror([0,1]) square([clip_ogap, -clip_ymin]);
+}
+
+module ClipElevationNegative(){
+  hull(){
+    for (y=[0, cliprecess_ymax+1])
+      translate([0, y])
+       oval(lockshaft_r);
+  }
+  translate([-clip_gap/2, 1]) mirror([0,1]) square([clip_gap, 2-clip_ymin]);
+}
+
+module ClipElevation(){
+  difference(){
+    ClipElevationPositive(1);
+    ClipElevationNegative(0);
+  }
+}
+
+module ExtrudeClipElevation(extra=0){
+  translate([0,
+            lock_0y + lock_d/2 + clip_d + extra,
+            -clip_ymin])
+    rotate([90,0,0])
+    linear_extrude(height= clip_d + extra*2, convexity=100)
+    children(0);
+}
+
+module ThroughHole(r, y, z, x=-50) {
+  translate([x, y, z])
+    rotate([0, 90, 0])
+    cylinder(r=r, h=100, $fn=20);
+}
+
+module MountingHoleCylinders(r, x=-50){
+  for (z=[ 1/4, 3/4 ]) {
+    rotate([0,0, backflange_angle])
+      ThroughHole( r,
+                  -tube_dia/2 -0.5*backflange_d + backflange_hole_dy,
+                  total_h * z,
+                  x);
+  }
+}
+
+module ThroughHoles(){
+  MountingHoleCylinders(mountscrew_dia/2);
+
+  ThroughHole( clipbolt_dia/2,
+              lock_0y + lock_d/2 + clip_d/2 + front_th/2,
+              main_h - cliprecess_h - clip_th - clip_d/2 );
+}
+
+module SlopeTrimElevation(){
+  far_corner_nom = [ lock_0y + lock_d/2, main_h ];
+  round_centre = far_corner_nom + lock_d/2 * [0,1];
+  hull(){
+    translate(round_centre) circle(r= lock_d/2);
+    translate([ lock_0y - lock_d/2, total_h ]) square([ lock_d + clip_d, 1 ]);
+    translate(far_corner_nom) square([clip_d*2, 1]);
+  }
+}
+
+module SlopeTrim(){
+  rotate([0,90,0])
+    rotate([0,0,90])
+    translate([0,0, -lock_w])
+    linear_extrude(convexity=100, height=lock_w*2)
+    SlopeTrimElevation();
+}
+    
+module MainPositive(){
+  difference(){
+    union(){
+      linear_extrude(height=total_h, convexity=100) MainPlan();
+      ExtrudeClipElevation() ClipElevationPositive();
+    }
+    ExtrudeClipElevation(1) ClipElevationNegative();
+  }
+}
+
+module Bracket(){ ////toplevel
+  difference(){
+    MainPositive();
+    ThroughHoles();
+    SlopeTrim();
+  }
+}
+
+module TestTopEdge(){ ////toplevel
+  intersection(){
+    translate([0,0, -total_h])
+      translate([0,0, 4])
+      Bracket();
+    translate([-200,-200,0])
+      cube([400,400,100]);
+  }
+}
+
+module TestClipBoltHole(){ ////toplevel
+  intersection(){
+    union(){
+      translate([0, 0, -5])
+       Bracket();
+      translate([-4, lock_0y + lock_d/2 + 1, 0])
+       cube([8, 4, 1.5]);
+    }
+    translate([-200, lock_0y + lock_d/2 + 0.1])
+      cube([400, 400, total_h-20]);
+  }
+}
+
+module Demo(){ ////toplevel
+  Bracket();
+  color("blue") MountingHoleCylinders(mountscrew_dia/2 - 0.1);
+  color("black") MountingHoleCylinders(mountscrew_washer/2,
+                                      back_ohw + 0.25);
+}
+
+module DivideDemo(){ ////toplevel
+  color("black") translate([0,0,-2]) MainPlan();
+  color("grey") DivideInPlace() DivideHook();
+  color("blue") translate([0,0,-4]) DivideInPlace() DivideCut();
+}
+
+//MainPlan();
+//ClipElevationPositive();
+//ClipElevation();
+//MainPositive();
+//%ThroughHoles();
+//TestTopEdge();
+//TestClipBoltHole();
+
+//Bracket();
+
diff --git a/maglite-holder-photo.jpg b/maglite-holder-photo.jpg
new file mode 100644 (file)
index 0000000..c0d19ce
Binary files /dev/null and b/maglite-holder-photo.jpg differ
diff --git a/maglite-holder-torch.fig b/maglite-holder-torch.fig
new file mode 100644 (file)
index 0000000..9c25deb
--- /dev/null
@@ -0,0 +1,19 @@
+#FIG 3.2  Produced by xfig version 3.2.6
+Landscape
+Center
+Metric
+A4
+100.00
+Single
+-2
+1200 2
+5 1 0 1 4 4 50 -1 -1 0.000 0 0 0 0 707.697 1147.762 -1207 1110 -1113 554 -858 45
+2 2 0 1 2 1 75 -1 -1 0.000 0 0 -1 0 0 5
+        -1800 -1350 2025 -1350 2025 3825 -1800 3825 -1800 -1350
+2 5 0 1 0 -1 100 -1 -1 0.000 0 0 -1 0 0 5
+       0 maglite-holder-photo.jpg
+        -1572 -1185 1891 -1185 1891 3762 -1572 3762 -1572 -1185
+2 2 0 1 2 1 75 -1 -1 0.000 0 0 -1 0 0 5
+        -859 13 857 13 857 -795 -859 -795 -859 13
+2 3 0 1 1 1 50 -1 -1 0.000 0 0 -1 0 0 6
+        -1201 1100 -1204 2667 -231 1187 -225 -159 -846 17 -1201 1100
diff --git a/maglite-holder.scad b/maglite-holder.scad
new file mode 100644 (file)
index 0000000..9aed425
--- /dev/null
@@ -0,0 +1,173 @@
+// -*- C -*-
+
+dxf_off = [ -40, -85 ];
+
+torch_lit_dia = 37.5;
+torch_big_dia = 56.5;
+torch_tot_len = 256;
+torch_big_len = 60;
+
+torch_clear = 30;
+torch_clear_below = 10;
+
+stem_width = 20;
+stem_thick = 8;
+
+torch_recess = 11;
+arm_width = 10;
+block_thick = 15;
+
+torch_out_more = 10;
+
+brace = [ 40, 20, 20 ];
+
+hole_dia = 4 + 0.5;
+hole_slot = 5;
+
+movement_extra_angle = 5;
+
+slop = 4; // total, not each side
+
+torch_min_xcoord_fig_cm = -2.7; // coordinates of bottom left of curve
+torch_min_ycoord_fig_cm = -5.9; // & big part in fig file.  mid top is origin
+torch_smm_xcoord_fig_cm = -1.9; // x coord of lhs of narrow part
+
+miniature = 1; // can adjust this to get commitid size right and rescale *Print
+
+$fa=5;
+
+// calculated
+
+include <commitid.scad>
+
+torch_dxf_scale =
+  [ (torch_big_dia - torch_lit_dia) /
+    (-(torch_min_xcoord_fig_cm - torch_smm_xcoord_fig_cm) * 10 * 2),
+    torch_big_len / (-torch_min_ycoord_fig_cm * 10) ];
+
+echo(torch_dxf_scale);
+
+above = torch_big_len + torch_clear + torch_clear_below;
+
+holes = [ 172, 265 ];
+
+stem_below = stem_width/2;
+
+stem_len = holes[1] - above + stem_below;
+
+torch_out = stem_thick + torch_big_dia/2 + torch_out_more;
+
+block_width = arm_width*2 + torch_big_dia;
+
+block_out = torch_out + torch_big_dia/2/sqrt(2);
+
+module TorchOrig(){
+  mirror([0,0,1]){
+    hull(){
+      rotate_extrude()
+       translate([-torch_lit_dia/2, 0])
+       scale(torch_dxf_scale)
+       translate(dxf_off)
+       translate([-torch_smm_xcoord_fig_cm * 10, 0])
+       import(file="maglite-holder-torch-curve.dxf",
+              convexity=10, center=true);
+    }
+    translate([0,0, -1])
+      cylinder(r=torch_lit_dia/2, h= torch_tot_len - torch_big_len + 1);
+  }
+}
+
+module Torch(){
+  scale(slop/torch_lit_dia + 1.0)
+    TorchOrig();
+}
+
+module ScrewHole(y, rot) {
+  translate([0,0, above -y]){
+    rotate([0,rot,0]){
+      hull(){
+       for (d= [-1,+1] * hole_slot/2) {
+         translate([d,0,0])
+           rotate([90,0,0])
+           translate([0,0,-stem_thick*2])
+           cylinder(r= hole_dia/2, h= stem_thick*4);
+       }
+      }
+    }
+  }
+}    
+
+module TorchMovement(){
+  translate([0, -torch_out, 0]) {
+    translate([0, 0, -torch_recess])
+      Torch();
+    for (as=[-1,+1]) {
+      rotate([0,0, as*movement_extra_angle])
+       rotate([90,0,0])
+       linear_extrude(height= block_out)
+       projection() rotate([-90,0,0]) Torch();
+    }
+  }
+}
+
+module Bracket(){ ////toplevel
+  cid_w = stem_width * .75;
+  hole_near = hole_slot + hole_dia;
+
+  difference(){
+    mirror([0,1,0]) {
+      translate([-stem_width/2, 0, -stem_len])
+       cube([stem_width, stem_thick, stem_len]);
+      translate([0,0, -block_thick]) hull(){
+       translate([-stem_width/2, 0, -brace[2]])
+         cube([stem_width, stem_thick, 1]);
+       translate([-brace[0]/2, 0, 0])
+         cube([brace[0], brace[1], 1]);
+      }
+    }
+    ScrewHole(holes[0], 90);
+    ScrewHole(holes[1], 0);
+    translate([-cid_w/2, 0, above - holes[0] - hole_near])
+      rotate([-90,0,0])
+      scale([miniature, miniature, 1])
+      Commitid_BestCount([cid_w, holes[1]-holes[0] - hole_near*2]
+                        / miniature);
+  }
+  difference(){
+    mirror([0,1,0])
+      translate([-block_width/2, 0, -block_thick])
+      cube([block_width, block_out, block_thick]);
+    TorchMovement();
+  }
+}
+
+module BracketPrint(){ ////toplevel
+  scale(1/miniature)
+    rotate([-90,0,0])
+    Bracket();
+}
+
+module TestTorchPrint(){ ////toplevel
+  scale(1/miniature)
+  intersection(){
+    translate([0,0, torch_lit_dia / 2 / sqrt(2)])
+      rotate([-90,0,0])
+      Torch();
+    translate([-100, -torch_tot_len*2, 0])
+      cube([200, torch_tot_len*4, 200]);
+  }
+}
+
+module Demo(){ ////toplevel
+  color("red")
+    translate([0, -torch_out, 0])
+    TorchOrig();
+  color("blue")
+    translate([0, -torch_out, above])
+    cylinder(r=torch_big_dia/2, h=1);
+  Bracket();
+}
+
+//Demo();
+//BracketPrint();
+//TestTorchPrint();
diff --git a/makita-drill-handle-blivet.scad b/makita-drill-handle-blivet.scad
new file mode 100644 (file)
index 0000000..831b83f
--- /dev/null
@@ -0,0 +1,42 @@
+// -*- C -*-
+
+include <utils.scad>
+
+hex_across = 12.70 - 0.3;
+screw_dia = 8.0 + 0.0;
+
+min_th = 0.425;
+extra_th = 1.0;
+
+// calculated
+
+total_th = min_th + extra_th;
+hex_rad = hex_across / 2 / cos(30);
+
+module Plan(){
+  difference(){
+    circle(r = hex_rad, $fn = 6);
+    circle(r = screw_dia/2);
+  }
+}
+
+module Elevation(){
+  hull(){
+    rectfromto([ -hex_rad, -1],
+              [ 0.1, min_th]);
+    translate([ hex_rad, 0 ])
+      rectfromto([ 0, -1 ],
+                [ 1, total_th]);
+  }
+}
+
+module Blivet(){
+  intersection(){
+    linextr(0, total_th + 1)
+      Plan();
+    linextr_y_xz(-hex_across, hex_across)
+      Elevation();
+  }
+}
+
+Blivet();
diff --git a/manual-gcode-generator b/manual-gcode-generator
new file mode 100755 (executable)
index 0000000..ba9a0cd
--- /dev/null
@@ -0,0 +1,142 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+our @array;
+our %procs;
+
+sub readdata () {
+    my $l = '';
+    my $current = \@array;
+    while (<>) {
+       chomp or die;
+       s/\s+$//;
+       s/^\s*\!\s*/!/;
+       $l .= $_;
+       next if $l =~ s/\\$//;
+
+       $_=$l; $l='';
+       if (m/^\!(\w+)\s*\(\)\s*\{$/) {
+           my $pname=$1;
+           die if $current ne \@array;
+           die if exists $procs{$pname};
+           $current = $procs{$pname} = [];
+           next;
+       }
+       if (m/^\!\}$/) {
+           $current = \@array;
+           next;
+       }
+       push @$current, $_;
+    }
+}
+readdata();
+
+our %c;
+
+sub defvar ($;$) {
+    my ($cv,$v) = @_;
+    $c{$cv} = $v;
+}
+
+defvar('extruderate',0.097200);
+defvar('feedrate',540);
+defvar('jerkfeedrate',7800);
+defvar('retract',4.5);
+defvar('restart',4.5);
+defvar('restartfeedrate',1800);
+defvar('retractfeedrate',1800);
+defvar('movefeedrate',7800);
+defvar('zlift',0.1);
+defvar('zprint');
+defvar('orgx',0);
+defvar('orgy',0);
+
+sub float_g ($) {
+    my ($f) = @_;
+    return sprintf "%.5f", $f;
+}
+sub coords_g ($) {
+    my ($coords) = @_;
+    return "X".float_g($coords->[0])." Y".float_g($coords->[1]);
+}
+
+sub p ($) { print "$_[0]" or die $!; }
+sub pl ($) { p("$_[0]\n"); }
+
+sub proc ($);
+
+sub proc ($) {
+    my ($aref) = @_;
+    local ($_);
+    foreach (@$aref) {
+       if (!m/^\!/) {
+           pl($_);
+           next;
+       }
+       pl(";$_");
+       if (m/^\!(\w+)\s*\(\)$/) {
+           my $pname = $1;
+           die "$pname ?" unless $procs{$pname};
+           proc($procs{$pname});
+       } elsif (m/^\!draw\s+/) {
+           my @coords = split /\s+/, $'; #';
+           my @undefs = grep { !defined $c{$_} } qw(zprint);
+           die "@undefs ?" if @undefs;
+           @coords = map {
+               my $jerk = s/^\*//;
+               m/\,/ or die $!;
+               [ $`, $', !!$jerk ]; # '];
+           } @coords;
+           foreach my $co (@coords) {
+               foreach my $xy (qw(0 1)) {
+                   my $xyv = $co->[$xy];
+                   next unless $xyv =~ s/^\@//;
+                   my $orgxy = ($c{orgx},$c{orgy})[$xy];
+                   $co->[$xy] = float_g($xyv + $orgxy);
+               }
+           }
+           my $extrudepos=$c{restart};
+           pl("G92 E0");
+           my $zmove=$c{zprint}+$c{zlift};
+           pl("G1 F$c{movefeedrate} Z$zmove");
+           pl("G1 ".coords_g($coords[0]));
+           pl("G1 Z$c{zprint}");
+           pl("G1 F$c{restartfeedrate} E".float_g($extrudepos));
+           my $lastfeedrate=-1;
+           foreach (my $ci=1; $ci<@coords; $ci++) {
+               my $g = "G1 ".coords_g($coords[$ci]);
+               my $wantfeedrate;
+               if (!$coords[$ci][2]) {
+                   $wantfeedrate=$c{feedrate};
+                   my $dist = 0;
+                   foreach my $xy (qw(0 1)) {
+                       my $dxy = $coords[$ci][$xy] - $coords[$ci-1][$xy];
+                       $dist += $dxy * $dxy;
+                   }
+                   $dist = sqrt($dist);
+                   $extrudepos += $dist * $c{extruderate};
+                   $g .= " E".float_g($extrudepos);
+               } else {
+                   $wantfeedrate=$c{jerkfeedrate};
+               }
+               if ($wantfeedrate != $lastfeedrate) {
+                   $g .= " F$wantfeedrate";
+                   $lastfeedrate = $wantfeedrate;
+               }
+               pl($g);
+           }
+           $extrudepos -= $c{retract};
+           pl("G1 F$c{retractfeedrate} E".float_g($extrudepos));
+           pl("G1 F$c{movefeedrate} Z$zmove");
+           next;
+       } elsif (m/^\!(\w+)\=(\S+)$/) {
+           die "$1 ?" unless exists $c{$1};
+           $c{$1} = $2;
+       } else {
+           die "$_ ?";
+       }
+    }
+}
+
+proc(\@array);
diff --git a/mic-camera-adapter.scad b/mic-camera-adapter.scad
new file mode 100644 (file)
index 0000000..a6cb9f6
--- /dev/null
@@ -0,0 +1,54 @@
+// -*- C -*-
+
+// print on High Detail
+// but adjust infill to 50%, shell thickness to 2mm
+
+include <threads.scad>
+include <camera-mount.scad>
+
+positive_dia = inch * 3/8. - 0.375;
+positive_l = inch * 1/2.;
+
+negative_l = negative_default_l;
+
+negative_wall = 4;
+midsection = 4;
+
+spanner = 12;
+
+base_dia = 35;
+base_th_min = 1;
+base_th_max = 4;
+
+//$test = true;
+$test = false;
+$fs=0.1;
+$fa=5;
+
+module Adapter(){
+  translate([0,0,-0.1])
+    english_thread(diameter=positive_dia/inch, threads_per_inch=16,
+                  leadin=1, test=$test,
+                  length= (positive_l + 0.1) / inch);
+  rotate([180,0,0]) {
+    difference(){
+      union(){
+       cylinder(r= spanner/2 * 1/(0.5 * sqrt(3)),
+                h = negative_l + midsection,
+                $fn=6);
+       translate([0,0, midsection+negative_l]) {
+         mirror([0,0,1]) {
+           hull(){
+             cylinder(r= base_dia/2, h = base_th_min);
+             cylinder(r= 0.1,        h = base_th_max);
+           }
+         }
+       }
+      }
+      translate([0,0, midsection + negative_l])
+       CameraMountThread(negative_l);
+    }
+  }
+}
+
+Adapter();
diff --git a/mic-table-clamp.scad b/mic-table-clamp.scad
new file mode 100644 (file)
index 0000000..b88e8d1
--- /dev/null
@@ -0,0 +1,229 @@
+// -*- C -*-
+
+// print Stem and Wingnut on High Detail
+// but adjust shell thickness to 2mm
+
+// others on Standard
+
+include <utils.scad>
+include <threads.scad>
+include <camera-mount.scad>
+
+positive_dia = inch * 3/8. - 0.375;
+positive_l = inch * 1/2.;
+
+positive_blank_dia = 8.12;
+blank_l = 17;
+blank_taper = 1.0;
+
+stem_l = 40;
+stem_dia = 12;
+stem_th = 3;
+stem_ceil = 2;
+stem_base_th  = 4;
+stem_base_dia = 25;
+stem_inner_l = 15;
+
+thread_nom = 8;
+thread_pitch = 1.25;
+thread_act = thread_nom + 0.600;
+
+clamp_l = 40;
+clamp_top_th = 7;
+clamp_bot_th = 10;
+clamp_bot_tooth_h = 2.5;
+clamp_bot_tooth_d  = 10;
+clamp_bot_collar = 20;
+clamp_bot_collar_th = 3.0;
+clamp_reg_sz1 = 3;
+clamp_reg_sz2 = 5;
+clamp_w = 15;
+clamp_max_table_th = 35;
+
+clamp_hole_dia = thread_nom + 0.30;
+
+clamp_reg_clear_x = 2.5;
+clamp_reg_clear_y = 0.75; // each side
+clamp_reg_extra_x = 4;
+
+//ct_h = 7;
+
+wingnut_th = 5;
+wingnut_wall = 4;
+wingnut_wing_mindia = 17.0;
+wingnut_wing_xrad = 8;
+wingnut_wing_xh = 5;
+wingnut_wing_th = 3;
+
+//$test= true;
+$test= false;
+
+$fa= 3;
+$fs= 0.2;
+
+// calculated
+
+wingnut_cnr = wingnut_wing_th/2 -0.1;
+
+clamp_reg_bot_x_min = -stem_base_dia/2 -clamp_reg_clear_x -clamp_reg_sz2;
+clamp_collar_r = thread_nom/2 + clamp_bot_collar_th;
+
+module OurThread(l){
+  translate([0,0,-0.01])
+    metric_thread(diameter=thread_act, pitch=thread_pitch,
+                 leadin=3, internal=true,
+                 test=$test, length=l);
+}
+
+module StemWith(){
+  translate([0,0, stem_l -0.1])
+    children();
+
+  difference(){
+    union(){
+      cylinder(r= stem_dia/2 * 1/(0.5 * sqrt(3)),
+              h = stem_l,
+              $fn=6);
+      cylinder(r= stem_base_dia/2,
+              h = stem_base_th);
+    }
+    OurThread(stem_inner_l);
+  }
+}  
+
+module Stem(){ ////toplevel
+  StemWith()
+    english_thread(diameter=positive_dia/inch, threads_per_inch=16,
+                  leadin=1, test=$test,
+                  length= (positive_l + 0.1) / inch);
+}
+
+module StemBlankPart(){
+  hull(){
+    cylinder(h = blank_l + 0.1 - blank_taper,
+            r = positive_blank_dia/2);
+    cylinder(h = blank_l + 0.1,
+            r = positive_blank_dia/2 - blank_taper);
+  }
+}
+
+module BlankStem(){ ////toplevel
+  StemWith()
+    StemBlankPart();
+}
+
+module Wingnut(){ ////toplevel
+  difference(){
+    union(){
+      cylinder(r= (thread_nom+wingnut_wall)/2,
+              h= wingnut_th);
+      minkowski(){
+       sphere(r= wingnut_cnr);
+       translate([0,0, wingnut_cnr*0.5])
+         linear_extrude(height= wingnut_wing_xh + wingnut_th
+                        - wingnut_cnr*1.5)
+         square([wingnut_wing_mindia + wingnut_wing_xrad*2 - wingnut_cnr*2,
+                 wingnut_wing_th - wingnut_cnr*2],
+                center=true);
+      }
+    }
+    translate([0,0, wingnut_th])
+      linear_extrude(height= wingnut_wing_xh+1)
+      square(wingnut_wing_mindia, center=true);
+    translate([0,0, wingnut_th])
+      rotate([180,0,0])
+      OurThread(wingnut_th+3);
+    mirror([0,0,1])
+      linear_extrude(height=5)
+      square(center=true, wingnut_wing_mindia*2);
+  }
+}
+
+module ClampCollarPlan(){
+  circle(r= clamp_collar_r);
+}
+module ClampHolePlan(){
+  circle(r= clamp_hole_dia/2);
+}
+module ClampArmPlan(){
+  r = clamp_collar_r;
+  hull(){
+    rectfromto([r,       -clamp_w/2],
+              [clamp_l, +clamp_w/2]);
+    ClampCollarPlan();
+  }
+}
+
+module ClampTop(){ ////toplevel
+  linear_extrude(height = clamp_top_th, convexity=4) {
+    difference(){
+      union(){
+       ClampArmPlan();
+       ClampCollarPlan();
+      }
+      ClampHolePlan();
+    }
+  }
+  linear_extrude(height = clamp_reg_sz1, convexity=4) {
+    difference(){
+      for (m=[0,1]){
+       mirror([0,m,0])
+         translate([0, clamp_reg_sz2/2 + clamp_reg_clear_y, 0])
+         rectfromto([clamp_reg_bot_x_min - clamp_reg_extra_x, 0 ],
+                    [0,                           clamp_reg_sz1 ]);
+      }
+      ClampHolePlan();
+    }
+  }
+}
+
+module ClampBot(){ ////toplevel
+  linear_extrude(height = clamp_bot_th, convexity=4) {
+    difference(){
+      ClampArmPlan();
+      ClampHolePlan();
+    }
+  }
+  translate([clamp_l, 0, clamp_bot_th-0.1])
+    linear_extrude(height = clamp_bot_tooth_h +0.1)
+    rectfromto([ -clamp_bot_tooth_d, -clamp_w/2 ],
+              [  0,                 +clamp_w/2 ]);
+  translate([0,0, clamp_bot_th])
+    mirror([0,0,1])
+    linear_extrude(height = clamp_bot_collar)
+    difference(){
+    ClampCollarPlan();
+    ClampHolePlan();
+  }
+  translate([0, 0, clamp_bot_th]) {
+    linextr(-clamp_reg_sz2, clamp_max_table_th+clamp_reg_sz2) {
+      translate([clamp_reg_bot_x_min, 0]) {
+       rectfromto([ 0,             -clamp_reg_sz2/2 ],
+                  [ clamp_reg_sz2, +clamp_reg_sz2/2 ]);
+      }
+    }
+    linextr(-clamp_reg_sz2, 0) {
+      difference(){
+       rectfromto([ clamp_reg_bot_x_min, -clamp_reg_sz2/2 ],
+                  [  0,                  +clamp_reg_sz2/2 ]);
+       ClampHolePlan();
+      }
+    }
+  }
+}
+
+module StemBlankTest(){ ////toplevel
+  StemBlankPart();
+  linextr(-1.5,0) square(center=true, [10,35]);
+}
+
+module Demo(){ ////toplevel
+  color("blue") translate([0,0, clamp_top_th+0.5]) BlankStem();
+  color("red") ClampTop();
+  color("grey") translate([0,0, -(clamp_bot_th + 5)]) ClampBot();
+  translate([0,0, -(clamp_bot_collar +10)])
+    rotate([180,0,0]) Wingnut();
+}
+
+//Wingnut();
+//Stem();
diff --git a/nook-case-test.scad b/nook-case-test.scad
new file mode 100644 (file)
index 0000000..3f4cc51
--- /dev/null
@@ -0,0 +1,6 @@
+// -*- C -*-
+
+//// toplevels-from:
+include <nook-case.scad>
+
+$test = true;
diff --git a/nook-case.scad b/nook-case.scad
new file mode 100644 (file)
index 0000000..ecbbbc2
--- /dev/null
@@ -0,0 +1,351 @@
+// -*- C -*-
+
+// Infill density: 20%
+
+include <funcs.scad>
+include <utils.scad>
+
+nook_th = 12.41 + 0.50 - 1.50 + 1.35 - .25;
+nook_w = 127.12 + 0.75 - .95 - .50;
+nook_h = 123.44 + 21.88 + 21.05 + 0.75 - 1.90 - 0.50 - 0.50;
+
+edge_ledge_w = 9.60;
+edge_ledge_h = 2.44 - .25;
+edge_ledge_inc_ang = 10; // degrees
+
+usb_w = 14.5;
+usb_below = 1.5;
+
+open_recess_w = 12.5;
+open_recess_h = 2.5;
+
+open_recess_2_len = 15;
+open_recess_2_d_tooth = 30;
+
+nook_cnr_rad = 10;
+
+case_th = 2.5;
+ledge_w = 4;
+tape_th = 1.75;
+tape_inside = 2.0;
+
+gap = 0.5 * [1,1];
+
+tape_w = 15;
+
+test_pillar = 4;
+
+engage_l0 = 10;
+engage_l1 = 10;
+engage_l2 = 3;
+
+tooth_inward = gap[0] * 1.0 + 0.25 + 0.25;
+tooth_w = 15;
+
+diag_near_hinge_slope = 0.5;
+
+$test = false;
+
+$fa = $test ? 10 : 3;
+$fs = $test ? 0.1 : 1;
+
+// calculated
+
+tooth_height = nook_th;
+ledge_h = case_th;
+lid_th = case_th;
+tooth_th = case_th;
+
+spp0 = [0,0];
+spp1 = spp0 + case_th * [-1,0];
+spp9 = spp0 + ledge_h * [0,-1];
+spp8 = spp9 + nook_th * [0,-1];
+spp7 = spp8 + case_th * [-1,-1];
+
+spp11y = spp1[1] - tape_th;
+spp4y  = 0.5 * (spp0[1] + spp7[1]);
+spp3y = spp4y + tape_inside/2;  spp5y = spp4y - tape_inside/2;
+spp2y = spp3y + tape_th;        spp6y = spp5y - tape_th;
+
+spp20 = spp8 + nook_cnr_rad * [1,0];
+spp20x = spp20[0];
+
+tppA = spp0 + gap;
+tppB = spp1 + [0, gap[1]];
+tppC = tppB + lid_th * [0,1];
+tppD = [ spp20x, tppC[1] ];
+tppE = [ spp20x, tppB[1] ];
+tppF = tppA + ledge_w * [1,0];
+tppG = tppF + ledge_h * [0,-1];
+tppH = [ tppA[0], tppG[1] ];
+
+tppJx = tppA[0] + tape_th;
+
+tppK = [ tppC[0], tppG[1] ];
+spp31 = tppK - [0, gap[1]];
+spp30 = [ spp8[0], spp31[1] ];
+
+nom_cnr = 0.5 * [nook_w, nook_h, 0] - nook_cnr_rad * [1,1,0];
+
+tooth_y = nom_cnr[1] - tooth_w/2;
+
+etxa = nom_cnr[0] - engage_l2;
+etxb = etxa - engage_l1;
+etxc = -(nom_cnr[0] - engage_l2);
+
+tapa = nom_cnr[1] - engage_l2;
+tapb = tapa - tape_w;
+
+opra = nom_cnr[1] - engage_l2;
+oprb = opra - open_recess_w;
+
+opqa = tooth_y - open_recess_2_d_tooth + open_recess_2_len/2;
+opqb = opqa - open_recess_2_len;
+
+tppS = tppB + [-gap[0], 0];
+tppP = [ tppS[0] - tooth_th, tppC[1] ];
+tppQ = tppP + tooth_height * [0,-1] + tooth_inward * [1,0];
+tppR = [ tppS[0] + tooth_inward, tppQ[1] ];
+tppM = (tppQ + tppR) * 0.5 + tooth_th * 0.5 * [0,1];
+
+edge_ledge_rad = edge_ledge_h;
+
+module RightSideMainProfile() {
+  rectfromto(spp7, spp0);
+  rectfromto(spp7, spp20);
+  EdgeLedgeProfile();
+}
+
+module LeftSideMainProfile() {
+  rectfromto(spp7, spp30);
+  rectfromto(spp7, spp20);
+  EdgeLedgeProfile();
+}
+
+module EdgeLedgeProfile() {
+  intersection(){
+    hull(){
+      for (t=[[0,0], [-20,0], [0,-10]]) {
+       translate(spp8
+                 + [edge_ledge_w, edge_ledge_h]
+                 + edge_ledge_rad * [ -sin(edge_ledge_inc_ang),
+                                      -cos(edge_ledge_inc_ang) ]
+                 + t)
+         circle(edge_ledge_rad);
+      }
+    }
+    translate(spp7)
+      square(30);
+  }
+}
+
+module TopTapeCutout() {
+  polygon([ tppA,
+           tppA + [-40, 0],
+           tppG + [-40,-1],
+           [ tppJx, tppH[1]-1 ],
+           [ tppJx, tppC[1]+1 ],
+           [ tppA[0], tppC[1]+1 ]]);
+}
+
+module RightTopMainProfile() {
+  l = [ tppA, tppB, tppC, tppD, tppE, tppF, tppG, tppH ];
+  polygon(l);
+}
+
+module LeftTopMainProfile() {
+  l = [ tppC, tppD, tppE, tppF, tppG, tppK ];
+  polygon(l);
+}
+
+module SideTapeCutout1(y0,y1) {
+  rectfromto([ spp7[0]-1, y0 ],
+            [ spp8[0]+1, y1 ]);
+}
+
+module SideTapeCutout() {
+  SideTapeCutout1(spp6y, spp5y);
+  SideTapeCutout1(spp3y, spp2y);
+  SideTapeCutout1(spp3y, spp2y);
+  SideTapeCutout1(spp11y, spp1[1] + 1); // obsolete I think
+}
+
+module ToothProfile(){
+  polygon([tppA,
+          tppB,
+          tppS + [-0.1,0],
+          tppP,
+          tppC]);
+  hull(){
+    polygon([tppP,
+            tppM,
+            tppS]);
+    translate(tppM)
+      circle(r= tooth_th/2);
+  }
+}
+
+module Demo(){ ////toplevel
+  translate([-1,0,0]) {
+    translate([0,0,-2]) LeftSideMainProfile(); 
+    translate([0,0,-2]) color("yellow") LeftTopMainProfile();
+    color("red") difference(){
+      LeftSideMainProfile();
+      SideTapeCutout();
+    }
+    translate([0,0,-4]) color("brown") EdgeLedgeProfile();
+    translate(concat(spp8 + [edge_ledge_w, edge_ledge_h], [2]))
+      rotate(-edge_ledge_inc_ang) {
+      color("blue") square(3);
+      color("lightblue") mirror([1,0]) square(3);
+    }
+  }
+  translate([0,0,0]) color("purple") difference(){
+    LeftTopMainProfile();
+    TopTapeCutout();
+  }
+  translate([nook_cnr_rad*2 + 5, 0,0]) mirror([1,0,0]) {
+    color("red") RightSideMainProfile();
+    color("purple") RightTopMainProfile();
+    color("grey") translate([0,0,-2]) ToothProfile();
+  }
+  //%SideTapeCutout();
+}
+
+module FaceCore(z0,z1, extra_left, extra_right){
+  difference(){
+    for (mx=[0,1]) mirror([mx,0,0]) {
+       for (my=[0,1]) mirror([0,my,0]) {
+           translate(-nom_cnr) {
+             rotate_extrude(angle=90, convexity=10) {
+               intersection(){
+                 translate(-[1,0,0] * nook_cnr_rad)
+                   children(mx);
+                 rectfromto([-100,-100], [0,100]);
+               }
+             }
+           }
+         }
+       translate([nook_w/2, 0,0])
+         linextr_y_xz(-nom_cnr[1]-0.1, nom_cnr[1]+0.1)
+         children(1-mx);
+      }
+    for (my=[0,1]) mirror([0,my,0]) {
+       translate([-nook_w/2, 0,0])
+         mirror([1,0,0])
+         linextr_y_xz(tapb, tapa)
+         children(2);
+      }
+  }
+  for (my=[0,1]) mirror([0,my,0]) {
+      translate([0, -nook_h/2, 0]) {
+       linextr_x_yz(-nom_cnr[0]-0.1,    etxc + extra_left)  children(0);
+       linextr_x_yz(etxc - extra_right, etxb + extra_right) children(1);
+       linextr_x_yz(etxb - extra_left,  etxa + extra_left)  children(0);
+       linextr_x_yz(etxa - extra_right, nom_cnr[0]+0.1)     children(1);
+      }
+    }
+  if (!$test) {
+    linextr(z0,z1)
+      rectfromto(-nom_cnr, nom_cnr);
+  }
+}
+
+module DiagonaliseNearHinge(wider){
+  sz = spp0[1] - spp30[1] + gap[1];
+
+  for (my=[0,1]) mirror([0,my,0]) {
+      translate([-etxa, -nook_h/2, 0])
+       mirror([1,0,0])
+       linextr_y_xz(spp31[0] - wider, spp30[0] + gap[0] + 0.1)
+       translate([ 0, spp30[1] ])
+       polygon([[  -1, 0 ],
+                [   0, 0 ],
+                [  sz/diag_near_hinge_slope, sz ],
+                [  sz/diag_near_hinge_slope, sz + 0.1 ],
+                [  -1, sz + 0.1 ]]);
+    }
+}    
+
+module Base(){ ////toplevel
+  difference(){
+    FaceCore(spp7[1],spp8[1], 0.3, 0) {
+      LeftSideMainProfile();
+      RightSideMainProfile();
+      SideTapeCutout();
+    }
+    translate([0, -nook_h/2, 0])
+      mirror([0,1,0])
+      linextr_x_yz(-usb_w/2, usb_w/2)
+      rectfromto(spp8 + [-40, usb_below], [40, 40]);
+    translate([ gap[0], 0,0 ])
+      DiagonaliseNearHinge(10);
+/*
+    translate([nook_w/2, 0, 0])
+      linextr_y_xz(oprb, opra)
+      translate(spp0)
+      rectfromto([-40, -open_recess_h], [40, 1]);
+*/
+  }
+  if ($test) {
+    linextr(spp7[1], spp8[1]) {
+      hull(){
+       for (r=[0,180])
+         rotate([0,0,r])
+           translate(nom_cnr + -1 * nook_cnr_rad*[1,1])
+           square(12);
+      }
+    }
+  }
+}
+
+module Top(){ ////toplevel
+  difference(){
+    FaceCore(tppE[1],tppD[1], -gap[0], gap[0] + 0.3) {
+      LeftTopMainProfile();
+      RightTopMainProfile();
+      TopTapeCutout();
+    }
+    translate([nook_w/2, 0,0])
+      linextr_y_xz(opqb, opqa)
+      rectfromto(spp8, tppC + [-1,1]);
+  }
+  translate([0,0, gap[1]])
+    DiagonaliseNearHinge(0);
+  translate([nook_w/2, tooth_y, 0])
+    linextr_y_xz(-tooth_w/2, +tooth_w/2)
+    ToothProfile();
+}
+module TopPrint(){ ////toplevel
+  rotate([0,0,90]) rotate([180,0,0]) Top();
+}
+module BasePrint(){ ////toplevel
+  rotate([0,0,90]) Base();
+}
+
+module TestExtrude(){
+  difference(){
+    linextr_y_xz(-test_pillar, tape_w+test_pillar) children(0);
+    linextr_y_xz(           0, tape_w            ) children(1);
+  }
+}
+
+module BaseTestRest(){ ////toplevel
+  cube([30,15, spp8[1]-spp7[1]]);
+}
+
+module Demo3(){ ////toplevel
+  color("purple") Top();
+  color("red") Base();
+}
+
+module TestSide(){ ////toplevel
+  TestExtrude() { LeftSideMainProfile(); SideTapeCutout(); }
+}
+
+module TestTop(){ ////toplevel
+  TestExtrude() { LeftTopMainProfile(); TopTapeCutout(); }
+}
+module TestTopPrint(){ ////toplevel
+  rotate([180,0,0]) TestTop();
+}
diff --git a/nutbox.scad.m4 b/nutbox.scad.m4
new file mode 100644 (file)
index 0000000..5384eff
--- /dev/null
@@ -0,0 +1,65 @@
+// -*- C -*-
+// edit nutbos.scad.m4, not nutbos.scad!
+// shaft, nut_across, nut_thick, nut_recess, wall, ceil
+
+nutbox_data_M4 = [
+                  4.0 + 0.5,
+                  6.89 + 0.45,
+                  3.10 + 0.75,
+                  0.75,
+                  2.0,
+                  2.5
+                  ];
+
+nutbox_data_M3 = [
+                  3.0 + 0.5,
+                  5.48 + 0.45,
+                  2.26 + 0.75,
+                  0.75,
+                  1.8,
+                  2.0
+                  ];
+
+m4_define(shaft,      (nutbox_data[0]))
+m4_define(nut_across, (nutbox_data[1]))
+m4_define(nut_thick,  (nutbox_data[2]))
+m4_define(nut_recess, (nutbox_data[3]))
+m4_define(wall,       (nutbox_data[4]))
+m4_define(ceil,       (nutbox_data[5]))
+
+m4_define(nut_dia, (nut_across / cos(30)))
+m4_define(outer_size, (nut_dia + wall * 2))
+m4_define(h_base, (ceil + nut_thick + nut_recess))
+
+function NutBox_shaft(nutbox_data) = shaft;
+function NutBox_outer_size(nutbox_data) = outer_size;
+function NutBox_h_base(nutbox_data) = h_base;
+function NutBox_wall(nutbox_data) = wall; // not sure why anyone needs this
+
+module NutBox(nutbox_data, h, h_above_extra=0) {
+  // origin is centre of top of mount
+  // entrance is to positive y
+  // height is h which must be at least h_base
+  // can be mad extra tall (with hole all the way through) with h_above_extra
+  w = outer_size;
+  difference(){
+    mirror([0,0,1]) translate([-w/2,-w/2, -h_above_extra])
+      cube([w,w, h + h_above_extra]);
+    mirror([0,0,1]) translate([0,0,-1 -h_above_extra])
+      cylinder(r = shaft/2, h = h+2 + h_above_extra, $fn=20);
+    for (offset = [ [0,0, -nut_recess],
+                   [0, outer_size, 0] ]) {
+      hull(){
+       for (toffset = [[0,0,0], offset]) {
+         translate(toffset)
+           translate([0,0, -ceil])
+           mirror([0,0,1])
+           rotate([0,0, 360/6/2])
+           cylinder(r = nut_dia/2,
+                    h = nut_thick,
+                    $fn = 6);
+       }
+      }
+    }
+  }
+}
diff --git a/osstest-arm-hub-bracket.scad b/osstest-arm-hub-bracket.scad
new file mode 100644 (file)
index 0000000..63fefed
--- /dev/null
@@ -0,0 +1,36 @@
+// -*- C -*-
+
+len = 80;
+basethick = 4;
+sidewall = 5;
+width = 40;
+
+strapthick = 4;
+strapwidth = 7;
+
+strapbotgap = 1;
+strapsidegap = 4;
+overstrap = 4;
+
+wallheight = strapbotgap + strapthick + overstrap;
+
+availlen = (len - strapsidegap);
+numstraps = floor(availlen / (strapwidth + strapsidegap));
+strapstride = availlen / numstraps;
+echo(numstraps, strapstride);
+
+module Bracket(){
+  difference(){
+    cube([len, width, basethick+wallheight]);
+    translate([-1, sidewall, basethick])
+      cube([len+2, width-sidewall*2, wallheight+1]);
+    for (i=[0:numstraps-1]) {
+      translate([ (0.5+i)*strapstride + strapsidegap/2,
+                 width/2,
+                 basethick + strapbotgap + strapthick/2 ])
+       cube([strapwidth, width*2, strapthick], center=true);
+    }
+  }
+}
+
+Bracket();
diff --git a/osstest-arm-net-bracket.scad b/osstest-arm-net-bracket.scad
new file mode 100644 (file)
index 0000000..54e7d3b
--- /dev/null
@@ -0,0 +1,94 @@
+// -*- C -*-
+
+holedist = 64;
+tonguewidth = 10;
+tongue2width = 15;
+totaldepth = 26;
+tongue2depth = 35;
+thick = 4;
+tabover = 7+6;
+tabunder = 15;
+
+tabsidel = 7+1;
+tabsider = 7+10;
+
+tonguethick = 4;
+tongue2thick = 5;
+strapthick = 2;
+strapwidth = 5 + 0.35;
+ridgewidth = 2;
+
+hstrengthick = 2.5;
+hstrengdepth = strapwidth;
+
+cutoutover = 7;
+rcutoutside = 7+2 - 0.5;
+lcutoutside = 7-6.5 - 0.5;
+
+t2strengwidth = 10;
+t2strengwidtht = 4;
+t2strenglen = tongue2depth + 5;
+t2strengthicker = 1;
+
+strapholethicker = 1.5;
+
+holedia = 3.5;
+
+tongue2x = tongue2width - holedist;
+
+module Tongue(tw,tt,ad,slots=2){
+  y0=thick+0.1;
+  yn=ad-ridgewidth-strapwidth;
+  difference(){
+    union(){
+      translate([-tw, 0, 0])
+       cube([tw, ad, tt+strapthick]);
+      child();
+    }
+    for (yi=[1:slots-1])
+      translate([-tw-1, y0 + (yn-y0)*yi/(slots-1), tt])
+       cube([tw+2, strapwidth, strapthick+strapholethicker]);
+  }
+}
+
+module Body(){
+  translate([-holedist-tabsider, 0, 0]) {
+    cube([tabsidel+tabsider+holedist, thick, tabunder+tabover]);
+    cube([tabsidel+tabsider+holedist, thick+hstrengdepth, hstrengthick]);
+  }
+  Tongue(tonguewidth,tonguethick,totaldepth,3);
+  translate([tongue2x,0,0])
+    Tongue(tongue2width,tongue2thick,tongue2depth+thick,5) {
+    mirror([1,0,0]) hull(){
+      translate([-(tongue2width-t2strengwidth)*0.05, 0,0]) 
+       cube([t2strengwidth,t2strenglen,
+             tongue2thick+strapthick+strapholethicker+t2strengthicker]);
+      cube([t2strengwidtht,thick+0.1,tabunder+tabover]);
+    }
+  }
+}
+
+module Object(){
+  difference(){
+    Body();
+    translate([0,-25,tabunder+cutoutover]) {
+      translate([-(holedist+rcutoutside), 0,0])
+       mirror([1,0,0])
+       cube([50,50,50]);
+      translate([lcutoutside, 0,0])
+       cube([50,50,50]);
+    }
+    for (x=[-holedist,0])
+      translate([x, 0, tabunder]) {
+       translate([0, -1, 0]) {
+         rotate([-90,0,0]) {
+           cylinder(r= holedia/2+0.5, h=thick+2, $fn=20);
+         }
+       }
+       translate([0, 19.95 + thick, 0])
+         cube(center=true,[10,40,10]);
+      }
+  }
+}
+
+Object();
diff --git a/osstest-arm-psu-bracket.scad b/osstest-arm-psu-bracket.scad
new file mode 100644 (file)
index 0000000..2703cbc
--- /dev/null
@@ -0,0 +1,88 @@
+// -*- C -*-
+
+mainlen = 33;
+straps = [10,23];
+width = 60;
+endwall = 5;
+sidewall = 8;
+basethick = 3;
+endwallheight = 20;
+morebase = 20;
+
+plugwidth = 35;
+plugstartheight = 10;
+
+strapthick = 4;
+strapwidth = 7;
+strapbotgap = 1;
+overstrap = 6;
+
+discdia = 60;
+discoff_rear = 10;
+discoff_front = 50;
+
+sidewallraise = strapbotgap + strapthick + overstrap;
+
+module Sides(){
+  difference(){
+    for (y=[0, width-sidewall]) {
+      translate([0,y,0])
+       cube([mainlen, sidewall, basethick + sidewallraise]);
+    }
+    for (x=straps) {
+      translate([x, 0, basethick + strapbotgap + strapthick/2])
+       cube([strapwidth, width*3, strapthick], center=true);
+    }
+  }
+}
+
+module Ell(baseoff){
+  translate([-endwall,0,0]) {
+    translate([baseoff,0,0])
+      cube([mainlen + endwall + morebase, width, basethick]);
+    cube([endwall+0.1, width, endwallheight + sidewallraise + basethick]);
+  }
+}
+
+module Plug(){
+  translate([0, width/2,
+            basethick + sidewallraise + plugstartheight + 50])
+    cube([endwall*3, plugwidth, 100], center=true);
+}
+
+module Disc(discoff){
+  translate([discoff + discdia/2, width/2, -1])
+    cylinder(r=discdia/2, h=50, $fn=100);
+}
+
+module Main(baseoff){
+  difference(){
+    union(){
+      Ell(baseoff);
+      Sides();
+    }
+    Plug();
+  }
+}
+
+module RearBlock(){
+  difference(){
+    Main(-morebase);
+    Disc(discoff_rear);
+  }
+}
+
+module FrontBlock(){
+  difference(){
+    Main(0);
+    Disc(discoff_front - endwall);
+  }
+}
+
+module Both(){
+  RearBlock();
+  translate([mainlen + endwall + 10, 0, 0])
+    FrontBlock();
+}
+
+Both();
diff --git a/pandemic-counter-letters.fig b/pandemic-counter-letters.fig
new file mode 100644 (file)
index 0000000..fb9c92a
--- /dev/null
@@ -0,0 +1,16 @@
+#FIG 3.2  Produced by xfig version 3.2.5b
+Landscape
+Center
+Metric
+A4      
+100.00
+Single
+-2
+1200 2
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3780 3870 675 675 3780 3870 4455 3870
+4 0 0 32 -1 14 56 0.0000 4 540 555 3522 4119 A\001
+4 0 0 30 -1 14 56 0.0000 4 570 555 3510 4140 C\001
+4 0 0 33 -1 14 56 0.0000 4 570 555 3510 4140 S\001
+4 0 0 35 -1 14 48 0.0000 4 465 960 3337 4098 DF\001
+4 0 0 34 -1 14 56 0.0000 4 540 555 3510 4140 T\001
+4 0 0 31 -1 14 56 0.0000 4 540 555 3537 4125 L\001
diff --git a/pandemic-counter.scad b/pandemic-counter.scad
new file mode 100644 (file)
index 0000000..4d427af
--- /dev/null
@@ -0,0 +1,115 @@
+// -*- C -*-
+
+tokenrad=13;
+tokenthick=1.9;
+
+joinwidth=1.0;
+
+circlerad=15;
+
+module Letter(depth) {
+  translate([-circlerad,-circlerad])
+    import(file=str("pandemic-counter-l",depth,".dxf"), convexity=100);
+}
+
+module Token(depth) {
+  rotate([0,180,0])
+  linear_extrude(height=tokenthick) union(){
+    difference(){
+      circle(tokenrad);
+      Letter(depth);
+    }
+    child();
+  }
+}
+
+module Token_CDC(){ ////toplevel
+  Token(30){};
+}
+module Token_Lab(){ ////toplevel
+  Token(31){};
+}
+module Token_Act(){ ////toplevel
+  Token(32){
+    translate([0, 1])
+      square([tokenrad*.75, joinwidth], center=true);
+  }
+}
+module Token_Spec(){ ////toplevel
+  Token(33){};
+}
+//module Token_Terr(){ ////toplevel
+//  Token(34){};
+//}
+//module Token_TerrMove(){ ////toplevel
+//  Token(35){
+//    translate([-tokenrad*.75, -1])
+//      square([tokenrad*.75, joinwidth]);
+//  };
+//}
+
+spacing = tokenrad * 2 + 2;
+
+module Tokens(rows=1,cols=1) {
+  for (i=[0:rows-1])
+    for (j=[0:cols-1])
+      translate([j*spacing, i*spacing, 0])
+       child(0);
+}
+
+module Tokens_Act(){ ////toplevel
+  // Print *twice*, LAPIS BLUE or SQUEEZED ORANGE
+  // ordinary actions
+  //  up to 4 for 5 players, plus 2 for Borrowed Time plus 1 for Generalist
+  //  so need 23, make 24
+  Tokens(4,3) Token_Act();
+}
+
+module Tokens_Spec(){ ////toplevel
+  // ELECTRIC BLUE or MELLOW YELLOW
+  // once-per-turn special action, one each for 5 players
+  Tokens(3) Token_Spec();
+  translate([spacing,0,0]) Tokens(2) Token_Spec();
+}
+
+module Tokens_CDC(){ ////toplevel
+  // STORM GREY
+  // CDC
+  // 1 action per turn + 2 Borrowed Time
+  Tokens(3) Token_CDC();
+}
+
+module Tokens_Lab(){ ////toplevel
+  // WHITE
+  // free Lab action (on building research station, etc)
+  // make 2 (probably want less than that)
+  Tokens(2) Token_Lab();
+}
+
+//module Tokens_Terr(){ ////toplevel
+//  // FIRE TRUCK RED
+//  // Bioterrorist general actions
+//  Tokens(2) Token_Terr();
+//}
+
+//module Tokens_TerrMove(){ ////toplevel
+//  // CLASSIC BLACK
+//  // Bioterrorist drive/ferry
+//  Tokens(1) Token_TerrMove();
+//}
+
+module PosToken(i,j){
+  translate([j*spacing, i*spacing, 0]) child();
+}
+
+module Demo(){ ////toplevel
+  PosToken(0,0) Token_CDC();
+  PosToken(1,0) Token_Lab();
+  PosToken(2,0) Token_Act();
+  PosToken(3,0) Token_Spec();
+//  PosToken(1,1) Token_Terr();
+//  PosToken(2,1) Token_TerrMove();
+}
+
+//Tokens_Act();
+//Demo();
diff --git a/pandemic-counter.slic3r b/pandemic-counter.slic3r
new file mode 100644 (file)
index 0000000..add6df7
--- /dev/null
@@ -0,0 +1 @@
+fill_density = 0.5
diff --git a/pandemic-quarantine-numbers.fig b/pandemic-quarantine-numbers.fig
new file mode 100644 (file)
index 0000000..1524380
--- /dev/null
@@ -0,0 +1,13 @@
+#FIG 3.2  Produced by xfig version 3.2.5b
+Landscape
+Center
+Metric
+A4      
+100.00
+Single
+-2
+1200 2
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+        1800 1800 2385 1800 2385 2385 1800 2385 1800 1800
+4 0 0 1 -1 14 24 0.0000 4 255 240 1980 2205 1\001
+4 0 0 2 -1 14 24 0.0000 4 255 240 1980 2205 2\001
diff --git a/pandemic-quarantines.scad b/pandemic-quarantines.scad
new file mode 100644 (file)
index 0000000..834d128
--- /dev/null
@@ -0,0 +1,65 @@
+// -*- C -*-
+
+prisml = 13;
+triedge = 13;
+
+etchdepth = 1.0;
+
+figboxsize = 13;
+
+// calculated
+
+triheight = triedge / 2 * sqrt(3);
+tricentre = triedge / 2 * tan(30);
+
+module Number(number) {
+  translate([-figboxsize/2, -figboxsize/2])
+    import(file=str("pandemic-quarantine-l",number,".dxf"), convexity=100);
+}
+
+module FaceTriangle(){
+  x = triedge / 2;
+  y = triheight;
+  polygon([[-x,  0],
+          [ 0,  y],
+          [ x,  0]]);
+}
+
+module Body(){
+  translate([0, prisml/2, 0])
+    rotate([90,0,0])
+    linear_extrude(height=prisml) FaceTriangle();
+}
+
+module NumberCut(number){
+  translate([0,0, -etchdepth])
+    linear_extrude(height= etchdepth + 1)
+    Number(number);
+}
+
+module Etchings(){
+  for (rot=[0,180]) {
+    rotate([0,0, rot])
+      translate([0, -prisml/2, triedge * 0.3])
+      rotate([90, 0, 0])
+      NumberCut(2);
+  }
+  for (rot=[0,120,240]) {
+    translate([0,0, tricentre])
+      rotate([0, rot, 0])
+      translate([0,0, -tricentre])
+      rotate([0,180,0])
+      rotate([0,0, rot==240 ? 90 : -90])
+      NumberCut(1);
+  }
+}
+
+module Counter(){
+  difference(){
+    Body();
+    Etchings();
+  }
+}
+
+Counter();
+//NumberCut(1);
diff --git a/pannierstay.scad b/pannierstay.scad
new file mode 100644 (file)
index 0000000..65538a6
--- /dev/null
@@ -0,0 +1,14 @@
+len    = 54.3 - 1;
+bigdep = 10.7 - 1;
+smalldep=  4.9 - 1;
+tallw1 =  3.5 - 1.5;
+tallw2 =  3.5 - 1.5; // ish
+totalw = 11.6 - 2;
+
+gapw = totalw - tallw1 - tallw2;
+
+difference() {
+  cube([len, totalw, bigdep]);
+  translate([-1, tallw2, smalldep])
+    cube([len+2, gapw, bigdep - smalldep + 1]);
+}
diff --git a/pattress-boxes-3-cover.scad b/pattress-boxes-3-cover.scad
new file mode 100644 (file)
index 0000000..7c1d4b8
--- /dev/null
@@ -0,0 +1,201 @@
+// -*- C -*-
+
+patbox_side = 87;
+patbox_centres = 60.3;
+
+lid_thinboxbase_overlap = 5;
+lid_fatbox_overlap = 12;
+
+lid_thinbox_h = 9;
+lid_fatbox_h = 24;
+lid_fatbox_switches_h = 6+4;
+
+lid_max_switches_w = 70;
+lid_switches_y_slop = 3;
+
+total_len = 260;
+thinbox_len = 87;
+
+rail_overlap = 8;
+
+lid_top_wall = 1.5;
+lid_front_wall = 1.5;
+lid_side_wall = 1.5;
+
+peg_engage_depth = 1;
+peg_engage_dia = 6.5;
+peg_main_dia = 9;
+peg_max_dia = 15;
+peg_inner_dia = 3.5;
+peg_top_thick = 1;
+peg_straight_len = 3;
+
+$peg_inner_slop = 0.75;
+$peg_outer_slop = -0.9;
+$peg_outer_slop_engage = 0.1;
+
+peg_slope = 1;
+
+lid_side_slop = 0.5;
+lid_rail_behindslop = 0.5;
+lid_rail_strongwall = 2.5;
+
+// computed
+
+lid_inner_max_h =
+  lid_thinboxbase_overlap + lid_fatbox_h + lid_fatbox_switches_h;
+lid_inner_min_h = lid_fatbox_overlap + lid_fatbox_switches_h;
+
+lid_inner_kink = [thinbox_len, thinbox_len*2];
+
+lid_inner_w_nom = patbox_side;
+lid_inner_w = lid_inner_w_nom + lid_side_slop * 2;
+
+lid_seatline_w = (lid_inner_w - lid_max_switches_w)/2 - lid_switches_y_slop;
+
+lid_seatline_h = lid_fatbox_switches_h;
+
+peg_main_height = peg_straight_len + (peg_max_dia - peg_main_dia)/2/peg_slope;
+
+thinbox_front_z = lid_fatbox_switches_h + lid_fatbox_h - lid_thinbox_h;
+
+raillen = patbox_side/2 + rail_overlap;
+
+module LidSideProfile(){
+  polygon([[-lid_top_wall,     lid_inner_max_h],
+          [min(lid_inner_kink[0],total_len), lid_inner_max_h],
+          [min(lid_inner_kink[1],total_len), lid_inner_min_h],
+          [total_len,         lid_inner_min_h],
+          [total_len,         -lid_front_wall],
+          [-lid_top_wall,     -lid_front_wall]]);
+}
+
+module RailProfile(lslop=0.1){
+  yt_base = thinbox_front_z;
+  yt = yt_base - lid_rail_behindslop;
+  pegx = (lid_inner_w_nom - patbox_centres)/2;
+  sw = lid_rail_strongwall;
+  
+  polygon([[-lslop,                  yt],
+          [pegx - peg_main_dia/2, yt],
+          [pegx - peg_main_dia/2, yt_base - peg_straight_len],
+          [sw,
+           yt_base - peg_straight_len - (pegx - peg_main_dia/2)/peg_slope
+           +sw],
+          [sw,
+           lid_seatline_h - 1],
+          [-lslop,
+           lid_seatline_h - 1]]);
+}
+
+module LidSide(){
+  overlap = [0.1, 0.1, 0.1];
+
+  // main side profile
+  rotate([90,0,0])
+    linear_extrude(height= lid_side_wall)
+    LidSideProfile();
+
+  // seatline
+  translate(-overlap)
+    cube(overlap + [total_len, lid_seatline_w, lid_seatline_h]);
+
+  // lid front
+  translate([-0.1, -0.1, -lid_front_wall])
+    cube([total_len+0.1, lid_inner_w/2 + 0.2, lid_front_wall]);
+
+  // lid top
+  translate([-lid_top_wall, -lid_side_wall, -lid_front_wall])
+    cube([lid_top_wall, lid_inner_w/2 + 10, lid_front_wall + lid_inner_max_h]);
+
+  // rail
+  rotate([90,0,90])
+    translate([0,0,-0.1])
+    linear_extrude(height=raillen+0.1) //todo
+    RailProfile();
+
+  // rail end
+  translate([raillen, 0,0])
+    intersection(){
+      rotate_extrude($fn=50)
+       RailProfile(lslop=0);
+      translate([0, 25-0.1, 0])
+       cube(50, center=true);
+    }
+}
+
+module Lid(){ ////toplevel
+  for (m=[0,1])
+    mirror([0,m,0])
+      translate([0, -lid_inner_w/2, 0]) LidSide();
+}
+
+module PegProfile(){
+  polygon([[-peg_engage_depth, (peg_engage_dia - $peg_outer_slop_engage)/2],
+          [0,                 (peg_engage_dia - $peg_outer_slop_engage)/2],
+          [0,                 (peg_main_dia - $peg_outer_slop)/2],
+          [peg_straight_len,  (peg_main_dia - $peg_outer_slop)/2],
+          [peg_main_height,   (peg_max_dia - $peg_outer_slop)/2],
+          [peg_main_height+peg_top_thick, (peg_max_dia - $peg_outer_slop)/2],
+          [peg_main_height+peg_top_thick, (peg_inner_dia + $peg_inner_slop)/2],
+          [-peg_engage_depth,  (peg_inner_dia + $peg_inner_slop)/2]]);
+}
+
+module Peg(){ ////toplevel
+  echo($peg_outer_slop_engage);
+  rotate_extrude($fn=50)
+    rotate([0,0,-90])
+    PegProfile();
+}
+
+module Pegs(){ ////toplevel
+  baseslop = 0.1;
+  dslops = [0, -0.5, -1.0, -1.5];
+  stride = peg_max_dia + 4;
+  for (i=[0:len(dslops)-1]) {
+    translate([i*stride,0,0])
+      assign($peg_outer_slop_engage= baseslop + dslops[i])
+      Peg();
+  }
+}
+
+module AtFixingCentres(){
+  for (fc=[-1,+1]) {
+    translate([patbox_side/2 + fc*patbox_centres/2,
+              patbox_side/2,
+              0])
+      children();
+  }
+}
+
+module TopPattressBox(){
+  difference(){
+    translate([0,0, -lid_thinbox_h])
+      cube([patbox_side, patbox_side, lid_thinbox_h]);
+    AtFixingCentres(){
+      translate([0,0,-10]) cylinder(r=peg_engage_dia/2, h=20);
+    }
+  }
+}
+
+module Demo(){
+  Lid();
+  translate([0,0, thinbox_front_z])
+    rotate([0,180,0]) translate([0, -patbox_side/2, 0])
+    rotate([0,0,90]) union(){
+      %TopPattressBox();
+      color("blue") AtFixingCentres(){
+       rotate([180,0,0]) Peg();
+      }
+  }
+}
+
+//LidSide();
+//PegProfile();
+//Peg();
+//Pegs();
+//TopPattressBox();
+//RailProfile();
+//Demo();
+//Lid();
+//translate([0,0,-lid_fatbox_switches_h]) Lid();
diff --git a/pawn.scad b/pawn.scad
new file mode 100644 (file)
index 0000000..7277dcd
--- /dev/null
+++ b/pawn.scad
@@ -0,0 +1,80 @@
+// -*- C -*-
+
+// shape parameters
+
+r1 =  2.85;
+r2 =  4; a2 = 27;
+r3 = r2;
+r4 =  4; a4 = 18;
+r5 = 30;
+h6 = 7; a6 = 4;
+
+// coordinates
+
+                                z1 =  0;
+h2 = r2 * sin(a2);              z2 = z1 - h2;
+h3 = r3 * sin(a2);              z3 = z2 - h3;
+h4 = r4 * sin(a4);              z4 = z3 - h4;
+
+zc5 = z4 - r5 * sin(a4);
+z5 = zc5 + r5 * sin(a6);        z6 = z5 - h6;
+
+x1 =  0 - r1;
+x2 = x1 + r2 * (1-cos(a2));
+x3 = x2 + r3 * (1-cos(a2));
+x4 = x3 - r4 * (1-cos(a4));
+
+xc5 = x4 + r5 * cos(a4);
+x5 = xc5 - r5 * cos(a6);
+
+x6 = x5 - h6 * tan(a6);
+
+htotal = r1 - z6;
+echo("height", htotal);
+
+d = 0.01;
+dx = 0.00;
+
+$fa=2;
+$fs=0.2;
+
+module SegmentBasisSquare(zmin, zmax, xmin){
+  translate([xmin, zmin-d]) square([-xmin+dx, zmax-zmin+d*2]);
+}
+module ConvexSegment(xc, zc, r, zmin, zmax){
+  intersection(){
+    translate([xc,zc]) circle(r=r);
+    SegmentBasisSquare(zmin,zmax,-50);
+  }
+}
+module ConcaveSegment(xc, zc, r, zmin, zmax){
+  difference(){
+    SegmentBasisSquare(zmin,zmax, xc);
+    translate([xc,zc]) circle(r=r);
+  }
+}
+
+module PawnTemplate(){
+  ConvexSegment(  x1 + r1,  z1,   r1, z1, 50);
+  ConvexSegment(  x1 + r2,  z1,   r2, z2, z1);
+  ConcaveSegment( x3 - r3,  z3,   r3, z3, z2);
+  ConcaveSegment( x3 - r4,  z3,   r4, z4, z3);
+  ConvexSegment(  xc5,      zc5,  r5, z5, z4);
+  polygon([[x6, z6],
+          [x5, z5+d],
+          [dx, z5+d],
+          [dx, z6]]);
+}
+
+module Pawn(h=htotal){
+  scale(h/htotal) {
+    rotate_extrude(convexity=10, $fn=50){
+      assign($fn=undef){
+       PawnTemplate();
+      }
+    }
+  }
+}
+
+Pawn(h=30);
+//PawnTemplate();
diff --git a/pawn.slic3r b/pawn.slic3r
new file mode 100644 (file)
index 0000000..be356f2
--- /dev/null
@@ -0,0 +1,7 @@
+first_layer_bed_temperature = 80
+bed_temperature = 70
+skirts = 3
+first_layer_temperature = 190
+temperature = 177
+extrusion_multiplier = 0.9
+fill_density = 1.0
diff --git a/pin-hinge.scad b/pin-hinge.scad
new file mode 100644 (file)
index 0000000..81d527e
--- /dev/null
@@ -0,0 +1,100 @@
+// -*- C -*-
+
+include <utils.scad>
+
+$hinge_pin_dia = 0.795 + 0.75;
+$hinge_main_dia = 4.0;
+$hinge_inter_gap = 0.50;
+$hinge_prong_minwidth = 3.0;
+$hinge_noncrit_gap = 1.0;
+
+$fa = 3;
+$fs = 0.05;
+
+module HingePinPlan(){
+  circle(r= $hinge_pin_dia/2);
+}
+
+module HingeProngPlan(behind){
+  hull(){
+    circle(r= $hinge_main_dia/2);
+    polygon([[0,0],
+            [-$hinge_main_dia/2, -behind],
+            [+$hinge_main_dia/2, -behind]]);
+  }
+}
+
+module HingeGapPlan() {
+  circle(r = $hinge_main_dia/2 + $hinge_inter_gap);
+}
+
+module PlanDemo(){
+  HingeProngPlan(5);
+  %HingeGapPlan();
+  translate([0,0,1]) color("red") HingePinPlan();
+}
+
+module HingePinUnitCell(l) {
+  eff_l = l + $hinge_inter_gap;
+  pairs = floor(eff_l / (2*($hinge_prong_minwidth + $hinge_inter_gap)));
+  stride = eff_l / pairs;
+  $hinge__prong_width = stride/2 - $hinge_inter_gap;
+  for (i=[0:pairs-1]) {
+    translate(stride * i * [1,0,0])
+      children(0);
+  }
+}
+
+module HingePositive(l, behind){
+  HingePinUnitCell(l){
+    linextr_x_yz(0, $hinge__prong_width)
+      HingeProngPlan(behind);
+  }
+}
+
+module HingeNegative(l){
+  linextr_x_yz(-0.1, l+0.1)
+    HingePinPlan();
+  HingePinUnitCell(l){
+    linextr_x_yz($hinge__prong_width,
+                $hinge__prong_width*2 + 2*$hinge_inter_gap)
+      HingeGapPlan();
+  }
+}
+
+test_l = 30;
+test_wb = 12;
+test_h = 12;
+
+test_face_gap = 0.75;
+
+module Demo(){
+  difference(){
+    HingePositive(test_l, test_h/2);
+    %HingeNegative(test_l);
+  }
+}
+
+module TestBody(){
+  linextr_x_yz(0, test_l){
+    offset(delta = -test_face_gap/2)
+      polygon([[0,0],
+              [-test_wb/2, -test_h],
+              [+test_wb/2, -test_h]]);
+  }
+}
+
+module Test(){
+  difference(){
+    union(){
+      TestBody();
+      HingePositive(test_l, test_h/2);
+    }
+    HingeNegative(test_l);
+  }
+}
+
+//PlanDemo();
+//Demo();
+//TestBody();
+Test();
diff --git a/poster-tube-lid-coarse.scad b/poster-tube-lid-coarse.scad
new file mode 100644 (file)
index 0000000..2ff8f3d
--- /dev/null
@@ -0,0 +1,6 @@
+// -*- C -*-
+
+//// toplevels-from:
+include <poster-tube-lid.scad>
+
+coarse = true;
diff --git a/poster-tube-lid-parametric.scad.pl b/poster-tube-lid-parametric.scad.pl
new file mode 100755 (executable)
index 0000000..64d4d00
--- /dev/null
@@ -0,0 +1,234 @@
+#!/usr/bin/perl -w
+use strict;
+
+use Math::Vector::Real;
+use Math::Trig qw(pi);
+use POSIX;
+use Data::Dumper;
+
+sub TAU () { pi*2; }
+
+my $thick = 2.5;
+
+my $small_dia = 20;
+my $large_dia = 30;
+my $slope_angle = 45 * TAU/360;
+my $jcurverad = 5;
+my $tall = 50;
+
+my $lin_len = 2;
+my $sine_size = 5;
+my $sine_angle = 1.20 * TAU/8;
+
+my $ballend_xr = $thick/2;
+
+my $skew_slope = 0.7;
+
+my @i_sections = qw(ball0  -6
+                   sine0  -10
+                   lin0    -2
+                   circle 40
+                   lin1    2
+                   sine1  10
+                   ball2   6
+                   -
+                   );
+
+my @j_sections = qw(lin0    2
+                   -
+                   curve1 10
+                   -
+                   curveE 20
+                   -
+                   curve2 -10
+                   -
+                   );
+
+sub point ($$$$) {
+    my ($ip,$it, $jp,$jt) = @_;
+
+    my ($i_offset);
+
+    my $i_outward = V( 0,
+                      ($ip =~ m/0$/ ? -1 : +1),
+                      0 );
+
+    my $i_j_y_angle = 0;
+
+    my $i_thickscale = 1.0;
+    my $sine_len = $sine_size * sin($sine_angle);
+    my $sine_height = $sine_size * (1 - cos($sine_angle));
+
+    if ($ip =~ m/^lin[01]$/) {
+       $i_offset = V( -$lin_len * $it,
+                      0,
+                      0 );
+    } elsif ($ip =~ m/^circle$/) {
+       $i_offset = V( 0,0,0 );
+       $i_outward = V(  sin($it * TAU/2),
+                        -cos($it * TAU/2),
+                        0 );
+    } elsif ($ip =~ m/^sine[01]$/) {
+       my $angle = $it * $sine_angle;
+       $i_offset = V( -$lin_len -$sine_size * sin($angle),
+                      0,
+                      +$sine_size * (1 - cos($angle))
+                    );
+       $i_j_y_angle = $angle;
+    } elsif ($ip =~ m/^ball[02]$/) {
+       $i_j_y_angle = $sine_angle;
+       my $angle = $it * TAU/4;
+       my $dx = sin($angle) * $ballend_xr;
+       $i_offset = V( -$lin_len -$sine_len - $dx * cos($sine_angle),
+                      0,
+                      +$sine_height + $dx * sin($sine_angle)
+                    );
+       $i_thickscale = cos($angle);
+    } else {
+       die "$ip ?";
+    }
+
+    my $i_j_y_vect = V( sin($i_j_y_angle),
+                       0,
+                       cos($i_j_y_angle ));
+
+    my $j_plus_th = $jp =~ m/2$/ ? $thick : 0;
+
+    my $i_thick = $thick * $i_thickscale;
+    my $j_p_x = $small_dia/2 + $thick/2;
+    my $j_rs_x = $large_dia/2 + $thick/2;
+    my $j_dqr_x = (1-cos($slope_angle)) * $jcurverad;
+    my $j_q_x = $j_rs_x - $j_dqr_x;
+    my $j_dpq = ($j_q_x - $j_p_x) / asin($slope_angle);
+    #print STDERR "($j_q_x - $j_p_x) / asin($slope_angle); => $j_dpq\n";
+    my $j_p_y = 0;
+    my $j_q_y = $j_p_y + $j_dpq * cos($slope_angle);
+    my $j_r_y = $j_q_y + sin($slope_angle) * $jcurverad;
+    my $j_s_y = $tall;
+    my $j_qrc_x = $j_rs_x - $jcurverad;
+    my $j_qrc_y = $j_r_y;
+
+    my $j_x;
+    my $j_y;
+
+    if ($jp =~ m/^curveE$/) {
+       my $angle = ($jt + 1) * TAU/2 - $slope_angle;
+       $j_x = $j_p_x + $i_thick/2 * cos($angle);
+       $j_y = $j_p_y + $i_thick/2 * sin($angle);
+    } elsif ($jp =~ m/^curve[12]$/) {
+       my $angle = $slope_angle * $jt;
+       my $outwards = $jp =~ m/1/ ? -1 : +1;
+       $j_x = $j_qrc_x + cos($angle) * ($jcurverad + $outwards * $i_thick/2);
+       $j_y = $j_qrc_y - sin($angle) * ($jcurverad + $outwards * $i_thick/2);
+    } elsif ($jp =~ m/^lin0$/) {
+       $j_x = $j_rs_x + $i_thick * (+0.5 - $jt);
+       $j_y = $j_s_y;
+       $i_offset->[2] = 0;
+    } else {
+       die "$jp ?";
+    }
+
+    $j_y -= $j_qrc_y;
+
+    if ($j_y > 0) {
+       $i_j_y_vect = V(0,0,1);
+    }
+
+#    print STDERR "@_ $j_x $j_y $i_offset $i_outward\n";
+    return
+       $i_offset +
+       $j_x * $i_outward +
+       $i_j_y_vect * $j_y +
+       V(0,0,1) * $j_qrc_y +
+       V(0,0,-$tall) ;
+}
+
+sub get_sections_ptvals {
+    my $last_ptval;
+    my @out;
+    while (my $name = shift @_) {
+       if ($name eq '-') {
+           push @out, $last_ptval;
+       } else {
+           my $count = shift @_;
+           my $neg = sub { $_[0] };
+           if ($count < 0) {
+               $count = -$count;
+               $neg = sub { 1- $_[0] };
+           }
+           foreach (my $ix = 0; $ix < $count; $ix++) {
+               push @out, [ $name, $neg->($ix/$count) ];
+           }
+           $last_ptval = [ $name, $neg->(1.0) ];
+       }
+    }
+    return @out;
+}
+
+our @points;
+our %point_indices;
+our @triangles;
+
+my @ipts;
+my @jpts;
+
+my $qi;
+my $qj;
+
+sub triangle {
+    my @pixs;
+    foreach my $pval (@_) {
+       my $pix = $point_indices{$pval}
+           //= ((push @points, $pval), $#points);
+       if (grep { $pix eq $_ } @pixs) {
+           print "// elide @{ $ipts[$qi] } @{ $jpts[$qj] }\n";
+           return;
+       }
+       push @pixs, $pix;
+    }
+    push @triangles, [ $qi,$qj, \@pixs ];
+}
+
+sub make_sheet () {
+    @ipts = get_sections_ptvals(@i_sections);
+    @jpts = get_sections_ptvals(@j_sections);
+    my @sheet;
+    foreach my $ipt (@ipts) {
+       my @row = ();
+       foreach my $jpt (@jpts) {
+           push @row, &point(@$ipt, @$jpt);
+       }
+       push @sheet, \@row;
+    }
+    foreach ($qi=0; $qi<$#ipts; $qi++) { # i direction does not wrap
+       my $qi2 = $qi+1;
+       foreach ($qj=0; $qj<@jpts; $qj++) { # j direction does wrap
+           my $qj2 = ($qj+1) % @jpts;
+           my $p0 = $sheet[$qi][$qj];
+           triangle($p0, $sheet[$qi2][$qj], $sheet[$qi2][$qj2]);
+           triangle($p0, $sheet[$qi2][$qj2], $sheet[$qi][$qj2]);
+       }
+    }
+}
+
+sub pv ($) {
+    my $v = shift @_;
+    return "[".(join ',', @$v)."]";
+}
+
+sub write_out () {
+    print "module ImplHeadCup(){ polyhedron(points=[\n" or die $!;
+    print pv($_),",\n" or die $! foreach @points;
+    print "],faces=[\n" or die $!;
+    foreach (@triangles) {
+       print pv($_->[2]),", // @{ $ipts[$_->[0]] } @{ $jpts[$_->[1]] }\n" or die $!;
+    }
+    print "],convexity=10); }\n" or die $!;
+    print <<END or die $!;
+implheadcup_large_dia = $large_dia;
+implheadcup_thick     = $thick;
+END
+}
+
+make_sheet();
+write_out();
diff --git a/poster-tube-lid.scad b/poster-tube-lid.scad
new file mode 100644 (file)
index 0000000..000dec7
--- /dev/null
@@ -0,0 +1,1043 @@
+// -*- C -*-
+
+// Print, for each end:
+//
+//   CoverPrint
+//   StrapMount
+//   CatchAssembly
+//
+// For attaching tube to wall, with base, for storing sticks etc.
+//
+//   WallMount             goes near top
+//   WallMountForBase      goes at bottom
+//   WallMountBase         attaches to bottom, glue some neoprene to it
+//   WallMountBaseCutJig   jig for cutting neoprene
+
+include <funcs.scad>
+include <utils.scad>
+
+coarse = false;
+enable_head_cups = false;
+
+main_dia = 71.2 + 0.50 - 2.26;
+top_thick_middle = 4;
+top_thick_by_oring = 3.0;
+top_middle_dr = 11;
+
+main_cnr = 6.0;
+
+min_wall = 3;
+
+rivet_posn = 6.0 + 0.30;
+rivet_thick = 1.67;
+rivet_width = 4.15 + 1.0;
+rivet_tall = 5.51 + 1.49;
+
+over_rivet_wall = 1.0;
+side_rivet_gap = 1.5;
+inside_rivet_gap = 1.5;
+
+bayo_interf = 0.30;
+bayo_behind = 8.5;
+bayo_interf_width = 2.0;
+bayo_interf_slope = 0.5;
+
+oring_thick = 5.0;
+oring_bore = 62.0;
+
+oring_upper_embed_angle = 80;
+oring_compress = 0.1; // proportion
+oring_compress_more = 0.2;
+
+oring_rm_beside = 8;
+oring_rm_scale = 2.0;
+oring_rm_angle = 20;
+
+side_taper = 1.0;
+
+bayo_gap = 6.0;
+
+bayo_entry = 1.167;
+bayo_inramp = 0.9;
+
+bayo_slice_size = coarse ? 5 : 1;
+
+brace_hole_width = 1.0;
+brace_above_below = 1.2;
+brace_end_shorter = 0.3;
+
+jig_thick = 1.4;
+jig_hole_dia = 3.0;
+jig_rim = 5;
+jig_mark = 5;
+
+strap_loop_thick = 6;
+strap_loop_inside = 10;
+strap_loop_strlen = 10;
+strap_loop_elevation = 45;
+
+sm_inner_circum = 218 - 1.90 - 1.00 - 0.50;
+sm_main_thick = 2.0;
+sm_main_width = 20;
+
+sm_bolt_dia = 3.5 + 0.1;
+sm_bolt_shaft = 21.0;
+sm_bolt_head_dia = 6.94 + 1.0;
+sm_bolt_head_thick = 2.14;
+sm_bolt_nut_width = 5.89 + 0.25;
+sm_bolt_nut_thick = 3.68;
+sm_bolt_tighten_allow = 2.0;
+
+sm_bolt_y_clear = 0.75;
+sm_bolt_y_over = 0.5;
+
+sm_closure_cnr = 3.0;
+
+wm_thick = 5;
+wm_screw_dia = 4.5; // Timco wood screw 40mm, use brown plug
+wm_screwdriver_dia = 6.3 + 1.5;
+wm_screw_around = 5.0;
+wm_screw_slot = 3.5;
+wm_screw_head = 8.0;
+
+wmb_screw_dia = 5;
+wmb_screw_head_dia = 8.7 + 0.5;
+wmb_screw_around_x = 4;
+wmb_screw_around_z = 6;
+wmb_screw_depth_min = 10;
+web_screw_len = 16 + 0.5;
+wmb_nut_across = 7.82 + 0.35;
+wmb_nut_around_min = 2;
+wmb_nut_behind_min = 5;
+wmb_nut_th = 3.84 + 0.75;
+wmb_mount_wall = 4.5;
+wmb_mount_gap_xy = 0.1;
+wmb_mount_gap_z = 0.2;
+wmb_mount_y_width = 10;
+wmb_bottom_gap = 35; // includes allowance for padding, etc.
+wmb_bottom_th = 7;
+wmb_bottom_th_min = 1;
+wmb_ring_gap = 1.0;
+wmb_base_extra_rad = 10;
+wmb_jig_th = 1;
+wmb_jig_around_gap = 1;
+
+catch_stalk_h = 4.5;
+catch_stalk_len = 50;
+catch_tip_th = 4;
+catch_head_th = 3;
+
+catch_pin_slop = 0.25; // each side, and above
+catch_pin_slop_x_extra = 0.0; // only on one side
+catch_stalk_above_gap = 1.5;
+catch_stalk_eff_bend_rad = catch_stalk_len * 0.75;
+
+catch_strap_width = 12;
+catch_stalk_base_width = 15;
+
+catch_knob_dia = 6;
+catch_knob_above_gap = 5;
+catch_knob_height = 3.0;
+
+catch_stalk_below_gap = 1.0;
+catch_stalk_beside_gap = 2.0;
+
+// calculated
+
+TAU = PI*2;
+
+bayo_entry_x = bayo_entry;
+bayo_entry_z = bayo_entry;
+bayo_inramp_x = bayo_inramp;
+bayo_inramp_z = bayo_inramp;
+
+oring_mid_dia = oring_bore + oring_thick;
+oring_outer_dia = oring_mid_dia + oring_thick;
+
+oring_oblate = (1 - oring_compress);
+
+oring_y_rad = oring_thick/2 * oring_oblate;
+oring_x_rad = oring_thick/2 / oring_oblate;
+
+by_oring_z = oring_y_rad * (1 + cos(oring_upper_embed_angle));
+
+side_height = rivet_posn + bayo_behind + rivet_thick/2;
+side_thick = rivet_tall + over_rivet_wall;
+
+top_z = top_thick_by_oring + oring_y_rad + by_oring_z;
+
+middle_bot_z = top_z - top_thick_middle;
+
+bayo_top_z = bayo_behind + bayo_gap;
+
+bayo_nom_rad = main_dia/2 + side_thick;
+bayo_real_rad = main_dia/2 + rivet_tall;
+
+rivet_entry_width = rivet_width + side_rivet_gap;
+
+jig_mark_rad = jig_mark + main_dia/2 + jig_thick;
+
+handling_dia = oring_bore + oring_thick*2 + min_wall*2;
+handling_angle = 45;
+
+sm_inner_rad = (sm_inner_circum + sm_bolt_tighten_allow/2) / TAU;
+sm_outer_rad = sm_inner_rad + sm_main_thick;
+
+wm_main_width = sm_main_width;
+wm_y_min = sqrt( pow(sm_inner_rad, 2) -
+                pow(sm_inner_rad - (wm_thick - sm_main_thick), 2) );
+wm_y_screw = wm_y_min + wm_screw_around + wm_screw_dia/2;
+wm_y_max = wm_y_screw + wm_screw_dia/2 + wm_screw_around;
+wm_lhs_y_min = -wm_y_max;
+wm_y_slotc_screw = wm_y_screw + wm_screw_slot/2;
+wm_y_slot1_screw = wm_y_screw + wm_screw_slot;
+wm_y_slot1_max = wm_y_max + wm_screw_slot/2;
+wm_z_slot0_screw = wm_main_width + wm_screwdriver_dia/2;
+wm_z_slotc_screw = wm_z_slot0_screw + wm_screw_slot/2;
+wm_z_slot1_screw = wm_z_slot0_screw + wm_screw_slot;
+wm_z_max = wm_z_slot1_screw + wm_screw_around;
+
+wmb_mount_cut_rad = sm_outer_rad + wmb_ring_gap;
+wmb_nut_rad = wmb_nut_across / cos(30) * 0.5;
+wmb_x_screw_plus_around_r = max(
+                               wmb_screw_around_x + wmb_screw_dia/2,
+                               wmb_nut_around_min + wmb_nut_across/2
+                               );
+wmb_x_screw = -sm_outer_rad + wmb_x_screw_plus_around_r;
+wmb_x_outer = -sm_outer_rad + wmb_x_screw_plus_around_r * 2;
+function wmb_screw_thing_y_min(dia) = sqrt(
+                      pow(wmb_mount_cut_rad, 2) -
+                      pow(wmb_x_screw + dia/2, 2)
+                      );
+wmb_y_screw_end = wmb_screw_thing_y_min(wmb_screw_dia);
+wmb_y_nut_min = max(
+    wmb_screw_thing_y_min(wmb_nut_across + wmb_nut_around_min*2),
+    wm_y_slot1_max
+                   );
+wmb_y_mount_max = max(
+                     wmb_y_nut_min + wmb_nut_th + wmb_nut_behind_min,
+                     wmb_y_screw_end + wmb_screw_depth_min
+                     );
+wmb_z_screw = max(
+                 wmb_screw_around_z + wmb_screw_dia/2,
+                 wmb_nut_around_min + wmb_nut_rad
+                 );
+wmb_z_max = wmb_z_screw * 2;
+wmbb_y_max = max(
+                wmb_y_mount_max + wmb_mount_gap_xy + wmb_mount_wall,
+                wmb_y_screw_end + web_screw_len
+                );
+wmbb_x_outer = wmb_x_outer + (wmb_mount_gap_xy + wmb_mount_wall);
+wmbb_z_flat_max = -wmb_bottom_gap;
+wmbb_z_flat_whole_min = wmbb_z_flat_max - wmb_bottom_th_min;
+wmbb_z_min      = wmbb_z_flat_max - wmb_bottom_th;
+wmbb_r_top = main_dia/2 + wmb_base_extra_rad;
+wmbb_r_bottom = wmbb_r_top - (wmb_bottom_th - wmb_bottom_th_min);
+
+smc_pos = [ 0, sm_inner_rad, 0 ];
+
+smc_bolt_nut_dia = sm_bolt_nut_width / cos(30);
+smc_bolt_nut_eff_thick = sm_bolt_nut_thick + sm_bolt_tighten_allow;
+
+smc_bolt_y = sm_bolt_dia/2 + sm_bolt_y_clear;
+smc_max_y = smc_bolt_y + sm_bolt_y_over
+  + max(sm_bolt_head_dia/2, smc_bolt_nut_dia/2);
+smc_cnr_c_x = sm_bolt_shaft/2 - sm_closure_cnr
+  + sm_bolt_head_thick/2 + smc_bolt_nut_eff_thick/2;
+
+catch_cr = catch_knob_dia/2 + catch_stalk_beside_gap;
+catch_strap_thick = sm_main_thick;
+
+echo("R ", sm_inner_rad, bayo_real_rad, bayo_nom_rad);
+
+$fs= coarse ? 2.5 : 0.5;
+$fa= coarse ? 5 : 1;
+
+include <poster-tube-lid-parametric.scad>
+
+// bayonet definition
+
+bayo_a = [ bayo_entry_x, 0 ];
+bayo_p = [ 0, bayo_entry_z ];
+bayo_n = [ 0, bayo_behind-bayo_inramp_z ];
+bayo_m = [ bayo_inramp_x, bayo_behind ];
+bayo_l = bayo_m + bayo_interf * [ 1/bayo_interf_slope,  1 ];
+bayo_k = bayo_l + [ bayo_interf_width, 0 ];
+bayo_j = bayo_k + bayo_interf * [ 1/bayo_interf_slope, -1 ];
+bayo_i = bayo_j + [ rivet_width + inside_rivet_gap, 0 ];
+bayo_h = [ bayo_i[0], bayo_behind + bayo_gap + bayo_interf ];
+bayo_g = [ bayo_m[0] - rivet_width, bayo_h[1] ];
+
+bayo_e = [-bayo_p[0], bayo_p[1]] - [rivet_entry_width,0];
+bayo_d = [-bayo_a[0], bayo_a[1]] - [rivet_entry_width,0];
+bayo_c = bayo_d + [0,-5];
+bayo_b = bayo_a + [0,-5];
+
+bayo_f = [ bayo_e[0], bayo_g[1] + (bayo_e[0] - bayo_g[0]) ];
+
+bayo_polygon = [ bayo_a,
+                bayo_b,
+                bayo_c,
+                bayo_d,
+                bayo_e,
+                bayo_f,
+                bayo_g,
+                bayo_h,
+                bayo_i,
+                bayo_j,
+                bayo_k,
+                bayo_l,
+                bayo_m,
+                bayo_n,
+                bayo_p ];
+
+echo(bayo_polygon);
+
+// CATCH
+
+cppxC = 0.41 * sm_inner_rad * TAU;
+
+// catch pin
+
+cpp_adj = (bayo_n[0] - bayo_f[0]) * (1 - sm_inner_rad / bayo_nom_rad);
+// radius scaling due to nom and actual radius difference in
+// bayo entry construction
+
+cppa = bayo_f + [1,-1] * catch_pin_slop + [1,0] * cpp_adj;
+cppb = bayo_g + [1,-1] * catch_pin_slop + [1,0] * cpp_adj;
+cppd = [ bayo_n[0]
+        - catch_pin_slop - catch_pin_slop_x_extra,
+        -catch_stalk_above_gap ];
+cppi = [ cppa[0], cppd[1] ];
+cppc = [ cppd[0], cppb[1] ];
+cpph = cppd + [0,-1] * catch_stalk_h;
+cppe = cppd + [0,-1] * (catch_knob_above_gap + catch_knob_dia/2);
+cppf = [ cppa[0], cppe[1] ];
+cppg = [ cppa[0], cpph[1] ];
+cppB = 0.5 * (cppf + cppe);
+
+echo("RR", sm_inner_rad / bayo_nom_rad);
+
+// catch assembly depression below pin
+
+cppy6 = cppB[1] - (catch_knob_dia/2
+                  + (cppc[1] - cppd[1])
+                  + catch_stalk_below_gap);
+cpp7 = [ cppB[0], cppy6 + catch_cr ];
+cpp11 = cpp7 + [1,0] * catch_cr;
+cppy9 = cppy6 + catch_strap_width * 1/3;
+cpp9 = [ cpp7[0] + catch_cr * 2, cppy9 ];
+cpp8 = cpp9 + [0,-1] * catch_cr;
+cpp10 = cpp8 + [-1,0] * catch_cr;
+cppC = [ cppxC, cpp9[1] ];
+cppD = cppC + [0,-1] * catch_strap_width;
+
+// catch assembly stalk and so on
+
+catch_cr3 = catch_cr + catch_stalk_h;
+
+cppF = [ cppg[0] - catch_stalk_eff_bend_rad, cppd[1] ];
+cpp4 = [ cppg[0] - catch_stalk_len, cpph[1] ] + [1,-1] * catch_cr;
+cpp5 = [ cpp4[0], cppC[1] + catch_cr ];
+cpp2 = cpp5 + [-1,0] * (catch_cr * 2 + catch_stalk_base_width);
+cpp2r = cpp2 + [1,0] * catch_cr;
+cpp2d = cpp2 + [0,-1] * catch_cr;
+cpp3 = [ cpp2[0] + catch_cr + catch_cr3, cppd[1] - catch_cr3 ];
+cppA = [ -cppxC, cpp9[1] ];
+cppE = [ cppA[0], cppD[1] ];
+
+catch_assembly_dy = -cppy9 + catch_strap_width;
+
+
+module MainProfile(){
+  main_cnr_pos = [ side_thick, top_z ] - [1,1]*main_cnr;
+  difference(){
+    union(){
+      translate(main_cnr_pos){
+       intersection(){
+         difference(){
+           circle(r = main_cnr);
+           circle(r = main_cnr * 0.5);
+         }
+         square([10,10]);
+       }
+      }
+      polygon([[ -top_middle_dr,        middle_bot_z      ],
+              [ -top_middle_dr,        top_z             ],
+              [ main_cnr_pos[0],       top_z             ],
+              [ side_thick,            main_cnr_pos[1]   ],
+              [ side_thick,            -side_height      ],
+              [ side_taper,            -side_height      ],
+              [ 0,                     -rivet_posn       ],
+              [ 0,                     by_oring_z        ],
+              [ -oring_x_rad,          by_oring_z        ],
+              ],
+             convexity=10);
+    }
+    translate([ oring_mid_dia/2 - main_dia/2, 0 ])
+      hull(){
+      translate([ 0, oring_y_rad ])
+       scale([ 1/oring_oblate * (oring_compress_more+1) , oring_oblate ])
+       circle(oring_thick/2);
+      translate([ 0, oring_y_rad*2 - oring_thick/2 ])
+       circle(oring_thick/2);
+    }
+  }
+}
+
+module StrapLoopProfile(){
+  circle(r = strap_loop_thick/2);
+}
+
+module StrapLoop(){ ////toplevel
+  bigrad = strap_loop_inside/2 + strap_loop_thick/2;
+  extralen = strap_loop_thick * 5;
+
+  intersection(){
+    rotate([strap_loop_elevation, 0,0]){
+      for (x= [ -1, +1 ] * bigrad) {
+       translate([x, -extralen, 0])
+         rotate([-90,0,0])
+         linear_extrude(height= extralen + strap_loop_strlen + 0.1,
+                        convexity=10)
+         StrapLoopProfile();
+      }
+      translate([0, strap_loop_strlen, 0]){
+       intersection(){
+         rotate_extrude(convexity=10)
+           translate([bigrad, 0,0])
+           StrapLoopProfile();
+         translate([0,50,0])
+           cube([100,100,100], center=true);
+       }
+      }
+    }
+    translate([0, 50, 0])
+      cube(100, center=true);
+  }
+}
+
+module RotateProjectSlice(offset, slice_size, nom_rad, real_rad){
+  // nom_rad > real_rad
+  rotate([0,0, atan2(offset, nom_rad) ]){
+    intersection(){
+      translate([-offset, -10, 0])
+       rotate([90,0,0])
+       linear_extrude(height= nom_rad*2, convexity=50)
+       children(0);
+      translate([0,0, -25])
+       cylinder(h=50, r= real_rad);
+      translate([0,0, -25])
+       linear_extrude(height= 50, convexity=50)
+       polygon([ [ 0,0 ],
+                 [ -slice_size, -real_rad*2 ],
+                 [ +slice_size, -real_rad*2 ] ]);
+    }
+  }
+}
+
+module RotateProject(x_min, x_max, slice_size, nom_rad, real_rad){
+  offs = [ for (i=[ x_min :
+                   slice_size :
+                   x_max + slice_size ]) i ];
+  echo (offs);
+  for (off=offs)
+    RotateProjectSlice(off, slice_size, nom_rad, real_rad)
+    children(0);
+}
+
+module BayonetCutout(){
+  RotateProject(bayo_c[0], bayo_i[0], bayo_slice_size,
+               bayo_nom_rad, 
+               bayo_real_rad)
+    translate([-0.5 * (bayo_a[0] + bayo_d[0]), 0])
+    polygon(bayo_polygon, convexity=10);
+}
+
+module ProfilesDemo(){ ////toplevel
+  translate([-10,0]) MainProfile();
+  translate([+10, -side_height]) polygon(bayo_polygon, convexity=10);
+}
+
+module LimitForHandling(){ ////toplevel
+  hull() for (r=[0,180])
+    rotate([0,0,r]) {
+      for (rs=[-1,+1]) {
+       for (xd=[0,1]) {
+         rotate([0,0, rs * handling_angle/2]) {
+           translate([rs * xd * main_dia/2 * tan(handling_angle/2),
+                      main_dia/2 + side_thick - main_cnr,
+                      top_z - main_cnr]) {
+             mirror([0,0,1])
+               cylinder(r= main_cnr, h=50);
+             sphere(main_cnr);
+           }
+         }
+       }
+      } 
+    }
+  hull() rotate_extrude(convexity=10){
+    translate([ handling_dia/2 - main_cnr, top_z - main_cnr ]) {
+      circle(r = main_cnr);
+      mirror([0,1]) square([ main_cnr, 50 ]);
+    }
+  }
+  //cylinder(r= handling_dia/2, h=20);
+}
+
+module Cover(){ ////toplevel
+  render() difference(){
+    intersection(){
+      union(){
+       rotate_extrude(convexity=10)
+         translate([main_dia/2, 0])
+         MainProfile();
+       translate([0,0, middle_bot_z])
+         cylinder(h= top_thick_middle, r = main_dia/2 - top_middle_dr + 1);
+      }
+      LimitForHandling();
+    }
+    for (r=[0,180]){
+      rotate([0,0, r])
+       translate([0,0, -side_height])
+       BayonetCutout();
+      rotate([0,0, r + asin((-oring_rm_beside) / (main_dia/2))])
+       translate([0,
+                  oring_mid_dia/2 + oring_thick/4 * oring_rm_scale,
+                  oring_y_rad * 1.5])
+       rotate([-oring_rm_angle, 0, 0])
+       mirror([0,0,1])
+       cylinder(r = oring_thick/4 * oring_rm_scale, h=20);
+    }
+    for (r=[0 : 60 : 179]) {
+      rotate([0,0, r]) {
+       height = top_thick_middle - brace_above_below*2;
+       translate([0,0, middle_bot_z + brace_above_below + height/2 ])
+       cube(center=true, [ oring_bore - brace_end_shorter,
+                           brace_hole_width, height ]);
+      }
+    }
+  }
+  if (enable_head_cups)
+    for (r=[0,180])
+      rotate([0,0,r])
+       translate([-implheadcup_large_dia * .5 - implheadcup_thick/2,
+                  -implheadcup_large_dia * .0,
+                  middle_bot_z + 0.1])
+       ImplHeadCup();
+
+//  translate(strap_loop_thick * [-0.5, 0, +1])
+//    translate([handling_dia/2, 0, -side_height])
+//    rotate([0,180,0]) rotate([0,0,90])
+//    StrapLoop();
+}
+
+module SavingHole(){
+  translate([0,0, -10])
+    cylinder(r= main_dia/2 - jig_rim, h=20);
+}
+
+module Jig(){ ////toplevel
+  difference(){
+    union(){
+      translate([0,0, -side_height]){
+       cylinder(r= main_dia/2 + jig_thick, h= side_height + jig_thick);
+      }
+      translate([-jig_mark_rad, 0, jig_thick - jig_mark])
+       cube([jig_mark_rad*2, jig_mark, jig_mark]);
+    }
+    translate([0,0, -side_height-1])
+      cylinder(r= main_dia/2, h= side_height + 1);
+    SavingHole();
+    translate([0,0, -rivet_posn])
+      rotate([90, 0,0])
+      translate([0,0, -100])
+      cylinder(r= jig_hole_dia/2, h = 200);
+  }
+}
+
+module CoverPrint(){ ////toplevel
+  rotate([0,180,0]) Cover();
+}
+
+module CoverTest2(){ ////toplevel
+  difference(){
+    Cover();
+    SavingHole();
+  }
+}
+
+module CoverTest1(){ ////toplevel
+  difference(){
+    CoverTest2();
+    difference(){
+      for (r= [ 40, 147 ]){
+       rotate([0,0, r]){
+         translate([0,0, -10]) {
+           cube([ main_dia*3, main_dia * .53, 18], center=true);
+         }
+       }
+      }
+//      translate([ 50, 0, 0 ])
+//     cube([ 100,
+//            strap_loop_inside + strap_loop_thick*2 + 1,
+//            100 ],
+//          center=true);
+    }
+  }
+}
+
+module ImplHeadCupTest(){ ////toplevel
+  for (r=[0,180])
+    rotate([0,0,r])
+      translate([-17,0,0])
+      ImplHeadCup();
+}
+
+module SomeStrap(width, cut_width=0){
+  // children(0) is to add, (1) subtract
+  difference(){
+    union(){
+      cylinder(r=sm_outer_rad, h=width);
+      StrapMountProtrusion(smc_cnr_c_x + sm_closure_cnr,
+                          smc_max_y,
+                          sm_closure_cnr,
+                          width);
+      children(0);
+    }
+    translate([0,0,-1])
+      cylinder(r=sm_inner_rad, h=max(width+2, cut_width));
+    translate(smc_pos)
+      StrapMountBolt(5, width);
+    translate(smc_pos)
+      cube([ sm_bolt_tighten_allow, 40,100 ], center=true);
+    children(1);
+  }
+}
+
+module StrapMountBolt(l_delta, strap_width){ ///toplevel
+  // positioned relative to smc_pos
+  translate([(smc_bolt_nut_eff_thick - sm_bolt_head_thick)/2,
+            smc_bolt_y,
+            strap_width/2]){
+    translate([ -sm_bolt_shaft/2-1, 0,0 ]){
+      rotate([0,90,0]) cylinder(r= sm_bolt_dia/2, h= sm_bolt_shaft+2);
+    }
+    translate([ -sm_bolt_shaft/2, 0,0 ])
+      rotate([0,-90,0])
+      cylinder($fn=6, r=smc_bolt_nut_dia/2,
+              h=smc_bolt_nut_eff_thick + l_delta);
+    translate([ sm_bolt_shaft/2, 0,0 ])
+      rotate([0,90,0])
+      cylinder(r=sm_bolt_head_dia/2, h=sm_bolt_head_thick + l_delta);
+  }
+}
+
+module StrapMountProtrusion(half_x, max_y, cnr, width){
+  translate(smc_pos){
+    linear_extrude(height=width, convexity=10){
+      hull(){
+       for (m = [0,1]) mirror([m,0,0]) {
+         translate([-(half_x - cnr), max_y - cnr])
+           circle(r=cnr);
+         translate([-half_x, -sm_inner_rad])
+           square([1,1]);
+       }
+      }
+    }
+  }
+}
+
+module StrapMount(){ ////toplevel
+  SomeStrap(sm_main_width){
+    rotate([0,0,180]){
+      StrapMountProtrusion(strap_loop_inside/2 + strap_loop_thick,
+                          strap_loop_thick,
+                          sm_closure_cnr,
+                          sm_main_width);
+      translate(smc_pos +
+               [0,0, sm_main_width] +
+               strap_loop_thick * [ 0, 0.5, -1.0 ])
+       StrapLoop();
+    }
+    union(){ };
+  }
+}
+
+module WallScrewHoleSlot(){ ////toplevel
+  ds = [-1,+1] * wm_screw_slot/2;
+  linextr_x_yz(-(wm_thick + 1), 1) {
+    hull(){
+      for (d = ds)
+       translate([d, 0])
+         circle(r = wm_screw_dia/2);
+    }
+  }
+  hull(){
+    for (d = ds){
+      translate([0, d, 0]){
+       linextr_x_yz(0, 1)
+         circle(r = wm_screw_head/2);
+       linextr_x_yz(-(wm_screw_head - wm_screw_dia)/2, 0)
+         circle(r = wm_screw_dia/2);
+      }
+    }
+  }
+}
+
+module WallMountMounts(){
+  linextr(0, wm_z_max){
+    translate([ -sm_outer_rad, 0 ])
+      rectfromto([ 0,        wm_lhs_y_min ],
+                [ wm_thick, wm_y_slot1_max ]);
+  }
+}
+module WallMountScrewHoles(){
+  translate([ -sm_outer_rad + wm_thick, 0, wm_z_slotc_screw ]) {
+    translate([ 0, wm_y_slotc_screw, 0 ])
+      WallScrewHoleSlot();
+    translate([ 0, -wm_y_slotc_screw, 0 ])
+      rotate([90,0,0])
+      WallScrewHoleSlot();
+  }
+}
+
+module WallMount(){ ////toplevel
+  SomeStrap(sm_main_width, wm_z_max + 2){
+    WallMountMounts();
+    WallMountScrewHoles();
+  }
+}
+
+module WallMountBaseRingCut(){
+  circle(r = wmb_mount_cut_rad);
+}
+
+module WallMountBaseMounts(){
+  linextr(0, wmb_z_max) {
+    difference(){
+      rectfromto([ -sm_outer_rad, -wmb_y_mount_max ],
+                [ wmb_x_outer,   +wmb_y_mount_max ]);
+      WallMountBaseRingCut();
+    }
+  }
+}
+
+// screws, nuts, slots for nuts to go down into
+module WallMountBaseScrewsEtc(){ ////toplevel
+  for (my=[0,1]) {
+    mirror([0, my, 0]) {
+      translate([wmb_x_screw, 0, wmb_z_screw]) {
+       linextr_y_xz(wmb_y_screw_end,
+                    wmb_y_screw_end + 50)
+         circle(r = wmb_screw_dia/2);
+       linextr_y_xz(wmb_y_screw_end + web_screw_len,
+                    wmb_y_screw_end + 50)
+         circle(r = wmb_screw_head_dia/2);
+       linextr_y_xz(wmb_y_nut_min,
+                    wmb_y_nut_min + wmb_nut_th) {
+         hull(){
+           rotate(30)
+             circle(r = wmb_nut_rad, $fn = 6);
+           translate([0, 50])
+             square(wmb_nut_across, center=true);
+         }
+       }
+      }
+    }
+  }
+}
+
+module WallMountForBase(){ ////toplevel
+  SomeStrap(sm_main_width, wm_z_max + 2){
+    union(){
+      WallMountMounts();
+      WallMountBaseMounts();
+    }
+    union(){
+      WallMountScrewHoles();
+      WallMountBaseScrewsEtc();
+    }
+  }
+}
+
+module WallMountForBaseFixingsTest(){ ////toplevel
+  intersection(){
+    WallMountForBase();
+    linextr(-100,100)
+      rectfromto([ -sm_outer_rad-10, -wm_y_min ],
+                [ wmb_x_outer + 1, -100 ]);
+  }
+}
+
+module WallMountBaseFixingsTest(){ ////toplevel
+  intersection(){
+    WallMountBase();
+    linextr(-2,100)
+      rectfromto([ -sm_outer_rad-10, -wm_y_min ],
+                [ wmbb_x_outer + 1, -100 ]);
+  }
+}
+
+module WallMountBasePillarsPlan(){
+  for (my = [0,1]) {
+    mirror([0, my]) {
+      rectfromto([ -sm_outer_rad, wmbb_y_max - wmb_mount_y_width ],
+                [ wmbb_x_outer, wmbb_y_max ]);
+    }
+  }
+}
+
+// trim parts that are would foul the wall
+module WallMountTrimWallFoulPlan(){
+    translate([ -sm_outer_rad, 0])
+    rectfromto([ -wmbb_r_top, -(wmbb_r_top + 1) ],
+              [ 0,           +(wmbb_r_top + 1) ]);
+}
+
+module WallMountBase(){ ////toplevel
+  difference(){
+    union(){
+      // vertical blocks rising to join to wall mount
+      linextr(wmbb_z_min, wmb_z_max) {
+       difference(){
+         WallMountBasePillarsPlan();
+         WallMountBaseRingCut();
+       }
+      }
+
+      hull(){
+       linextr(wmbb_z_flat_whole_min, wmbb_z_flat_max)
+         circle(r = wmbb_r_top);
+       linextr(wmbb_z_min, wmbb_z_flat_max)
+         circle(r = wmbb_r_bottom);
+      }
+      linextr(wmbb_z_min, wmbb_z_flat_max) {
+       hull(){
+         WallMountBasePillarsPlan();
+         circle(r = wmbb_r_bottom);
+       }
+      }
+    }
+
+    // cutaway for mount part
+    linextr(-wmb_mount_gap_z, wmb_z_max+1) {
+      for (my = [0,1]) {
+       mirror([0, my])
+         rectfromto([ -sm_outer_rad-1, wmb_y_mount_max + wmb_mount_gap_xy ],
+                    [ wmb_x_outer + wmb_mount_gap_xy, 1 ]);
+      }
+    }
+
+    linextr(wmbb_z_min - 1, wmb_z_max + 1)
+      WallMountTrimWallFoulPlan();
+    WallMountBaseScrewsEtc();
+  }
+}
+
+module WallMountBaseCutJigPlan(){ ////toplevel
+  difference(){
+    union(){
+      circle(r = wmbb_r_top);
+    }
+
+    translate([ wmb_jig_around_gap, 0 ])
+      WallMountTrimWallFoulPlan();
+
+    offset(delta = wmb_jig_around_gap)
+      WallMountBasePillarsPlan();
+  }
+}
+
+module WallMountBaseCutJig(){ ////toplevel
+  translate([ 0,0, wmbb_z_flat_max + 0.5 ])
+    linextr(0, wmb_jig_th)
+    WallMountBaseCutJigPlan();
+}
+
+module WallMountForBaseDemo(){ ////toplevel
+  render() WallMountForBase();
+  color("blue") render() WallMountBase();
+  %WallMountBaseScrewsEtc();
+  %WallMountBaseCutJig();
+}
+
+module CatchAssemblyCoreProfile(){
+  difference(){
+    union(){
+      hull(){
+       translate(cpp3) circle(r= catch_cr3);
+       polygon([ cpp3,
+                 cpp2r,
+                 cpp5,
+                 cpph,
+                 cppd
+                 ]);
+      }
+      polygon([cppD,
+              cppC,
+              cpp9,
+              cpp10,
+              cpp11,
+              cpp4,
+              cpp2r,
+              cpp2d,
+              cppA,
+              cppE
+              ]);
+      translate(cpp8) circle(r= catch_cr);
+    }
+    hull(){
+      translate(cpp4) circle(r= catch_cr);
+      translate(cpp5) circle(r= catch_cr);
+      translate(cpp7) circle(r= catch_cr);
+      polygon([cpp4,
+              cppg,
+              cpph,
+              cpp10,
+              cpp11,
+              ]);
+    }
+    translate(cpp2) circle(r= catch_cr);
+  }
+  // if cpp11 is above cpp10, the subtracted hull above
+  // can go down too far.  Ensure we do not cut off below cppy6.
+  polygon([ cppE,
+           cppD,
+           cpp9,
+           [ cpp9[0],            cppy6 ],
+           [ cpp7[0] - catch_cr, cppy6 ],
+           cpp2d
+           ]);
+}
+
+module CatchTipProfile(dy){
+  ddy = [0,dy];
+  intersection(){
+    translate(cppF){
+      difference(){
+//     circle(r = dist2d(cppF, cppd));
+       //circle(r = dist2d(cppF, cppa));
+      }
+    }
+    polygon([ cppa,
+             cppi + ddy,
+             cppd + ddy,
+             cppc,
+             cppb ]);
+  }
+}
+
+module CatchHeadProfile(){
+  polygon([ cppd,
+           cppd,
+           cppi,
+           cppf,
+           cppe,
+           cpph ]);
+}
+
+
+module CatchCore(){ /////toplevel
+  linear_extrude(height=catch_strap_thick, convexity=10)
+    CatchAssemblyCoreProfile();
+
+  hull(){
+    linear_extrude(height=catch_head_th, convexity=10)
+      CatchTipProfile(0);
+    linear_extrude(height=catch_tip_th, convexity=10)
+      CatchTipProfile(catch_tip_th - catch_head_th);
+  }
+
+  linear_extrude(height=catch_head_th, convexity=10)
+    CatchHeadProfile();
+
+  translate(concat(cppB,[0])) hull(){
+    translate([0,0, catch_knob_height + catch_head_th - catch_knob_dia/2])
+      sphere(r = catch_knob_dia/2);
+    cylinder(r = catch_knob_dia/2, h = 0.1);
+  }
+}
+
+module CatchPreDistort(){ /////toplevel
+  scale(100 / sm_inner_rad)
+    rotate([90,0,0])
+    CatchCore();
+}
+
+module CatchAssembly(){ /////toplevel
+  rotate([0,0, -(cppe[0] + cppB[0] + catch_pin_slop) / sm_inner_rad * 360/TAU])
+    translate([0,0, catch_assembly_dy])
+    scale(sm_inner_rad / 100)
+    import(str("poster-tube-lid,CatchPostDistort-fa",
+              (coarse ? 20 : 3),
+              ".stl"),
+          convexity=20);
+
+  SomeStrap(catch_strap_width){
+    union(){ }
+    union(){
+      translate([-200, -200, -200])
+       cube([400, 200, 400]);
+    }
+  }
+}
+
+module CatchDemo(){ /////toplevel
+  color("blue") translate([0,0,
+                          -catch_assembly_dy
+            ])
+    CatchAssembly();
+  translate([0,0,+side_height
+            ])
+    Cover();
+}
+
+module CatchDemoS(){ /////toplevel
+  color("blue") translate([0,0,
+            -catch_assembly_dy
+            ])
+    CatchAssembly();
+  intersection(){
+    translate([0,0,+side_height
+              ])
+      Cover();
+    mirror([0,1,0]) translate([-250,33,0]) cube([500,500,500]);
+  }
+  color("black")
+    translate([0,-33,0])
+    cube([6.15, 2,2], center=true);
+}
+
+module CatchPinProfileDemo(){ /////toplevel
+  translate([0, 0 * -bayo_behind,0]) {
+    echo("G ",
+        bayo_n[0] - bayo_e[0]);
+    color("blue") translate([0,0,
+                            +1,
+              ]) {
+      CatchAssemblyCoreProfile();
+      CatchHeadProfile();
+    }
+    translate([0,0,10])
+      color("red")
+      CatchTipProfile(0);
+
+    polygon(bayo_polygon, convexity=10);
+
+    // adhoc show a position
+    color("purple")
+    translate(concat(
+                    cppa,
+                    10
+                    )) difference(){ circle(2.5); circle(2.0); }
+
+ }
+}
+
+//ProfilesDemo();
+//BayonetCutout();
+//MainProfile();
+//Cover();
+//Jig();
+//CoverTest();
diff --git a/powerbank-anker-10000.eps b/powerbank-anker-10000.eps
new file mode 100644 (file)
index 0000000..01359d8
--- /dev/null
@@ -0,0 +1,89 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: cairo 1.16.0 (https://cairographics.org)
+%%CreationDate: Fri Feb  5 23:12:37 2021
+%%Pages: 1
+%%DocumentData: Clean7Bit
+%%LanguageLevel: 2
+%%BoundingBox: 149 274 598 1452
+%%EndComments
+%%BeginProlog
+50 dict begin
+/q { gsave } bind def
+/Q { grestore } bind def
+/cm { 6 array astore concat } bind def
+/w { setlinewidth } bind def
+/J { setlinecap } bind def
+/j { setlinejoin } bind def
+/M { setmiterlimit } bind def
+/d { setdash } bind def
+/m { moveto } bind def
+/l { lineto } bind def
+/c { curveto } bind def
+/h { closepath } bind def
+/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto
+      0 exch rlineto 0 rlineto closepath } bind def
+/S { stroke } bind def
+/f { fill } bind def
+/f* { eofill } bind def
+/n { newpath } bind def
+/W { clip } bind def
+/W* { eoclip } bind def
+/BT { } bind def
+/ET { } bind def
+/BDC { mark 3 1 roll /BDC pdfmark } bind def
+/EMC { mark /EMC pdfmark } bind def
+/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def
+/Tj { show currentpoint cairo_store_point } bind def
+/TJ {
+  {
+    dup
+    type /stringtype eq
+    { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse
+  } forall
+  currentpoint cairo_store_point
+} bind def
+/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore
+    cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def
+/Tf { pop /cairo_font exch def /cairo_font_matrix where
+      { pop cairo_selectfont } if } bind def
+/Td { matrix translate cairo_font_matrix matrix concatmatrix dup
+      /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point
+      /cairo_font where { pop cairo_selectfont } if } bind def
+/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def
+      cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def
+/g { setgray } bind def
+/rg { setrgbcolor } bind def
+/d1 { setcachedevice } bind def
+/cairo_data_source {
+  CairoDataIndex CairoData length lt
+    { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def }
+    { () } ifelse
+} def
+/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def
+/cairo_image { image cairo_flush_ascii85_file } def
+/cairo_imagemask { imagemask cairo_flush_ascii85_file } def
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+%%PageBoundingBox: 149 274 598 1452
+%%EndPageSetup
+q 149 274 449 1178 rectclip
+1 0 0 -1 0 1612 cm q
+0 g
+360.93 1337.57 m 298.676 1334.211 219.23 1335.672 183.594 1274.547 c 142.047
+ 1187.812 152.582 1088.758 149.211 995.395 c 151.73 795.32 144.426 595.004
+ 156.238 395.148 c 157.656 328.41 155.359 251.004 205.434 199.816 c 263.293
+ 157.641 339.281 161.27 407.469 160.113 c 465.984 160.328 537.961 171.273
+ 565.277 230.957 c 601.477 310.977 590.16 401.633 593.281 486.977 c 592.355
+ 612.16 596.633 737.312 597.473 862.445 c 596.184 971.23 597.672 1080.117
+ 595.984 1188.836 c 591.852 1244.773 555.328 1300.199 498.418 1313.117 c
+ 459.289 1331.371 416.695 1338.148 373.711 1336.93 c 367.316 1337.203 l 
+h
+360.93 1337.57 m f
+Q Q
+showpage
+%%Trailer
+end
+%%EOF
diff --git a/powerbank-anker-10000.svg b/powerbank-anker-10000.svg
new file mode 100644 (file)
index 0000000..5999a71
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   id="svg2"
+   width="1048"
+   height="2149.3333"
+   viewBox="0 0 1048 2149.3333"
+   sodipodi:docname="anker-powerbank.svg"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+  <metadata
+     id="metadata8">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs6" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="2961"
+     inkscape:window-height="2094"
+     id="namedview4"
+     showgrid="false"
+     inkscape:snap-page="false"
+     inkscape:zoom="1.7568239"
+     inkscape:cx="497.30928"
+     inkscape:cy="570.53214"
+     inkscape:window-x="842"
+     inkscape:window-y="0"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="svg2" />
+  <path
+     style="fill:#000000;stroke-width:1.33333337"
+     d="m 481.23878,1783.4274 c -83.00255,-4.4776 -188.92949,-2.5338 -236.44742,-84.0321 -55.39794,-115.6427 -41.34772,-247.7191 -45.84221,-372.2039 3.35787,-266.7652 -6.38184,-533.85214 9.36873,-800.32809 1.88831,-88.98392 -1.17396,-192.18932 65.59317,-260.44389 77.14644,-56.22961 178.46251,-51.39211 269.38093,-52.93653 78.02295,0.28556 173.99094,14.87991 210.40981,94.45887 48.26475,106.69537 33.17812,227.56859 37.33958,341.35772 -1.23437,166.91394 4.46685,333.78584 5.58917,500.62752 -1.71663,145.0466 0.26483,290.2296 -1.98544,435.1878 -5.51041,74.5828 -54.21013,148.4825 -130.0869,165.7076 -52.1723,24.3388 -108.96694,33.3778 -166.27445,31.7502 l -8.52964,0.3672 z"
+     id="path838"
+     inkscape:connector-curvature="0" />
+</svg>
diff --git a/powerbank-bike-clamp.scad b/powerbank-bike-clamp.scad
new file mode 100644 (file)
index 0000000..2a755a8
--- /dev/null
@@ -0,0 +1,431 @@
+// -*- C -*-
+
+// Print one of each:
+//    Main
+//    TubeClampRight
+
+include <utils.scad>
+
+tube_dia = 22.4;
+
+hinge_around = 2.5;
+hinge_pin = 1.8 + 0.75;
+main_th = 3;
+minor_wall_min = 1;
+
+screw = 5.0 + 0.75;
+screw_head = 8.7 + 1.2;
+screw_head_space_above = 10;
+screw_nut_across = 7.9 + 0.75;
+screw_nut_th = 3.9 + 0.75;
+
+knob_behind_across = 12.2 + 0.75;
+behind_knob_th = 5;
+knob_standout_h = 2;
+
+clamp_min_width = 20;
+
+clamp_gap = 2;
+
+corner_rounding_r = 10;
+
+lower_th = 1;
+
+overlap_l = 0.1;
+
+bridge_slop = 1.2;
+
+hinge_lobe_per = 10;
+hinge_gap_z = 0.75;
+hinge_gap_xy = 0.75;
+
+$fs = 0.1;
+$fa = 5;
+
+bank_eps_bbox_x = [149, 598];
+bank_eps_bbox_y = [274, 1452];
+
+bank_y_sz = 102.25 + 0.50 + 3.2;
+bank_x_sz = (26.0 + 0.5);
+bank_recess_y = 5;
+
+strap_th = 3;
+strap_w = 5;
+strap_above = 2.5;
+
+strap_around_over = 1.0;
+strap_around_attach = 2.0;
+
+retainer_walls = [18, 30];
+
+bank_profile_scale_bodge = 1.0;
+
+bank_output_ctr = [ 12.5, 11.5 ]; // from nearest corner
+bank_output_sz  = [ 11.0, 10.5 ];
+
+mounted_pos_y_offset_lim = -100;
+
+liner_th = 0.8;
+
+brace_r = 5;
+brace_len = 50;
+
+straps_y_adj = [ 3.5,
+                0,
+                0 ];
+
+// calculated
+
+straps_y = [ -bank_y_sz * 0.25, // these entries are special and used
+            +bank_y_sz * 0.25, //  for the brace struts
+            0 ];
+
+screw_head_behind = main_th;
+endwall_th = main_th;
+
+bank_recess_dx = minor_wall_min;
+
+pspt_to_mm = 25.4 / 72;
+
+main_r = tube_dia/2 + main_th;
+
+screw_max_y_lhs = -main_r -screw_nut_across/2;
+screw_max_y_rhs = -main_r -knob_behind_across/2;
+
+screw_y = min(screw_max_y_lhs,
+             screw_max_y_rhs);
+
+bot_y = screw_y -max( screw_nut_across/2, knob_behind_across/2 )
+  -minor_wall_min;
+
+holder_x_sz = bank_x_sz + bank_recess_dx*2;
+bank_bot_y = strap_above + strap_th;
+strap_r = strap_th;
+
+brace_total_len = brace_len + main_th;
+brace_ctrs_y_nom = [ straps_y[0] - (brace_r + strap_w/2),
+                    straps_y[1] + (brace_r + strap_w/2) ];
+
+brace_ctrs_y = [ (straps_y + straps_y_adj)[0] + (brace_r + strap_w/2),
+                (straps_y + straps_y_adj)[1] + (brace_r + strap_w/2) ];
+
+clamp_width_actual = max(clamp_min_width, holder_x_sz);
+
+hinge_lobes = floor(clamp_width_actual / hinge_lobe_per);
+hinge_stride = (clamp_width_actual + hinge_gap_z) / hinge_lobes;
+
+hinge_outer_r = hinge_around + hinge_pin/2;
+hinge_y = tube_dia/2 + hinge_outer_r;
+
+top_cnr_r = min(endwall_th, main_th);
+
+mounted_pos_y_offset = max(mounted_pos_y_offset_lim,
+                          bot_y - (-(bank_y_sz/2 + endwall_th)));
+
+
+module TubePlan(){ circle(r = tube_dia/2); }
+module HingePinPlan(){ translate([0, hinge_y]) circle(r= hinge_pin/2); }
+module HingeBodyPlan(){ translate([0, hinge_y]) circle(r= hinge_outer_r); }
+
+module TubeClampLeftPlan(){
+  difference(){
+    union(){
+      polygon([[ 0,                      hinge_y + hinge_outer_r ],
+              [ -(main_r + overlap_l),  hinge_y + hinge_outer_r ],
+              [ -(main_r + overlap_l),  bot_y                   ],
+              [ -clamp_gap/2,           bot_y                   ],
+              [ -clamp_gap/2,           0,                      ],
+              [ 0,                      0,                      ],
+              ]);
+      HingeBodyPlan();
+    }
+    TubePlan();
+    HingePinPlan();
+  }
+}
+
+module TubeClampLeft() { ////toplevel
+  linextr(-clamp_width_actual/2, clamp_width_actual/2)
+    TubeClampLeftPlan();
+}
+
+module TubeClampRightPlan(){ ////toplevel
+  difference(){
+    // It broke at the inside corner, round these a bit
+    offset(r=-corner_rounding_r)
+    offset(r=+corner_rounding_r)
+    difference(){
+      union(){
+       rectfromto([ clamp_gap/2,                   bot_y ],
+                  [ clamp_gap/2 + behind_knob_th,  0     ]);
+       intersection(){
+         circle(r= main_r); // maybe split off from main_r and increase?
+         union(){
+           rectfromto([0,0],
+                      main_r *  [5,5]);
+           rectfromto([ clamp_gap/2, main_r*5 ],
+                      main_r * [2,-5]);
+         }
+       }
+       HingeBodyPlan();
+      }
+      TubePlan();
+    }
+    HingePinPlan();
+  }
+}
+
+module Screws(){
+  linextr_x_yz(-main_r*5, main_r*5)
+    translate([screw_y, 0])
+    circle(r= screw/2);
+
+  translate([0, screw_y, 0]) {
+    linextr_x_yz(-(clamp_gap/2 + screw_nut_th), 0)
+      square([screw_nut_across,
+             screw_nut_across / cos(30) + bridge_slop*2],
+            center=true);
+
+    linextr_x_yz(-(main_r + bank_recess_y + screw_head_space_above),
+                -(clamp_gap/2 + screw_nut_th + screw_head_behind))
+      square([screw_head, screw_head + bridge_slop*2],
+            center=true);
+  }
+}
+
+module SomeClamp(hinge_alt=false){
+  difference(){
+    linextr(-clamp_width_actual/2, clamp_width_actual/2)
+      children(0);
+
+    Screws();
+
+    for (i=[0 : hinge_lobes-1]) {
+      translate([0,
+                hinge_y,
+                -clamp_width_actual/2 + i * hinge_stride
+                + (hinge_alt ? hinge_stride/2 : 0)
+                ])
+       linextr(-hinge_gap_z, hinge_stride/2)
+       square(hinge_outer_r*2 + hinge_gap_xy, center=true);
+    }
+  }
+}
+
+module PowerBankItselfSidePlan(){
+  translate([0, bank_bot_y]){
+    minkowski(){
+      circle($fn=8, r=liner_th);
+      scale( bank_profile_scale_bodge *
+            bank_x_sz / ( (
+                           bank_eps_bbox_x[1] -
+                           bank_eps_bbox_x[0]
+                           ) * pspt_to_mm ))
+       translate(pspt_to_mm *
+                 [-0.5 * (bank_eps_bbox_x[0] +
+                          bank_eps_bbox_x[1]),
+                  -bank_eps_bbox_y[0]])
+       import("powerbank-anker-10000.dxf", convexity=5);
+    }
+  }
+}
+
+module PowerBankItself(){ ////toplevel
+  rotate([0,90,0])
+    linextr_y_xz(-bank_y_sz/2,
+                +bank_y_sz/2)
+    PowerBankItselfSidePlan();
+}
+
+module PowerBankSidePlan(){ ////toplevel
+  render() difference(){
+    rectfromto([ -holder_x_sz/2,  0 ],
+              [ +holder_x_sz/2,  bank_recess_y + bank_bot_y ]);
+
+    PowerBankItselfSidePlan();
+  }
+}
+
+module PowerBankStrapCut(){ ////toplevel
+  difference(){
+    rectfromto([ -holder_x_sz, -0.05 ],
+              [ +holder_x_sz, strap_th + strap_r ]);
+    hull(){
+      for (sx=[-1,+1]) {
+       translate([sx * (holder_x_sz/2 - strap_r + 0.1),
+                  strap_th + strap_r])
+         circle(strap_r);
+      }
+    }
+  }
+}
+
+module PowerBankHolderTest(){ ////toplevel
+  difference(){
+    linextr(-1,5) PowerBankSidePlan();
+    linextr(0, strap_w) PowerBankStrapCut();
+  }
+}
+
+module EndRetainer(depth){ ////toplevel
+  translate([0, -bank_y_sz/2, 0]) {
+    linextr_y_xz(-endwall_th, 0)
+    rectfromto([ 0,         -holder_x_sz/2 ],
+              [ -depth,    +holder_x_sz/2 ]);
+    
+    for (m=[0,1]) {
+      mirror([0,0,m]) {
+       linextr(-holder_x_sz/2, -bank_x_sz/2){
+         hull(){
+           rectfromto([ 0, -endwall_th ],
+                      [ depth, 0 ]);
+           rectfromto([ 0, 0 ],
+                      [ 0.1, depth-0.1 ]);
+         }
+       }
+      }
+    }
+  }
+}
+
+module BraceTubePlan(){
+  intersection(){
+    circle(r= brace_r);
+    rectfromto(brace_r * [-2,  0],
+              brace_r * [+2, +2]);
+  }
+}
+
+module PowerBankHolder(){ ////toplevel
+  difference(){
+    union(){
+      rotate([0,90,0])
+       linextr_y_xz(-(bank_y_sz/2 + 0.1),
+                    +(bank_y_sz/2 + 0.1))
+       PowerBankSidePlan();
+
+      EndRetainer(retainer_walls[0]);
+      mirror([0,1,0]) EndRetainer(retainer_walls[1]);
+
+      translate([0,0, bank_x_sz/2]){
+       for (y = brace_ctrs_y) {
+         translate([0,y,0]) {
+           linextr_x_yz(0, brace_total_len)
+             BraceTubePlan();
+         }
+       }
+       translate([brace_total_len, 0,0])
+         linextr_y_xz(brace_ctrs_y_nom[0] - brace_r,
+                      brace_ctrs_y_nom[1] + brace_r)
+         BraceTubePlan();
+      }
+
+      for (strap_y = straps_y + straps_y_adj) {
+       translate([0, strap_y, 0]) {
+         linextr(-holder_x_sz/2,
+                 +holder_x_sz/2){
+           hull(){
+             for (dy = [-1,+1] *
+                    (strap_w/2 + strap_around_attach - strap_around_over)) {
+               translate([0, dy, 0])
+                 circle(r=strap_around_over);
+             }
+           }
+         }
+       }
+      }
+    }
+
+    for (strap_y = straps_y + straps_y_adj)
+      translate([0, strap_y, 0])
+       rotate([0,0,-90])
+       rotate([0,90,0])
+       linextr(-strap_w/2,
+               +strap_w/2)
+       PowerBankStrapCut();
+
+    translate([ bank_bot_y, -bank_y_sz/2, -bank_x_sz/2 ])
+      linextr_y_xz(-50,50)
+       rotate([0,0,90])
+       translate(bank_output_ctr)
+       square(center=true, bank_output_sz);
+
+    translate([0, -(bank_y_sz/2 + endwall_th), 0] + 0.01 * [-1,-1]) {
+      linextr(-200,200){
+       difference(){
+         square(center=true, top_cnr_r*2);
+         translate(top_cnr_r * [1,1])
+           circle(r= top_cnr_r);
+       }
+      }
+    }
+  }
+}
+
+module TubeClampLeft() { ////toplevel
+  // We want this to print with the recess overhand to the right
+  // where the workpiece cooling fan is
+  rotate([0,0,180]){
+    difference(){
+      SomeClamp(true)
+       TubeClampLeftPlan();
+
+      Screws();
+    }
+  }
+}
+
+module PlacePowerBank(){
+  translate([main_r, -mounted_pos_y_offset, 0])
+    children(0);
+}
+
+module Main(){ ////toplevel
+  TubeClampLeft();
+  difference(){
+    PlacePowerBank()
+      PowerBankHolder();
+    rotate([0,0,180])
+      Screws();
+  }
+}
+
+module TubeClampRight() { ////toplevel
+  rotate([0,0,180]) {
+    rotate([180,0,0]) {
+      difference(){
+       union(){
+         SomeClamp()
+           TubeClampRightPlan();
+
+         translate([clamp_gap/2 + behind_knob_th, screw_y, 0]) {
+           hull(){
+             linextr_x_yz(-0.1, 0)
+               square(center=true,
+                      [knob_behind_across,
+                       knob_behind_across + knob_standout_h*2]);
+             linextr_x_yz(0, knob_standout_h)
+               square(center=true,
+                      knob_behind_across);
+           }
+         }
+       }
+       Screws();
+      }
+    }
+  }
+}
+
+module TubeClampDemo() { ////toplevel
+  TubeClampLeft();
+  rotate([180,0,0])
+    TubeClampRight();
+}
+
+module Demo() { ////toplevel
+  Main();
+  rotate([180,0,0])
+    TubeClampRight();
+  PlacePowerBank()
+    %PowerBankItself();
+}
diff --git a/pronsolerc b/pronsolerc
new file mode 100644 (file)
index 0000000..3073286
--- /dev/null
@@ -0,0 +1,37 @@
+set port /dev/ttyUSB0
+set last_temperature 205.0
+
+macro button_retract
+      M83
+      G1 E-220 F2000
+      M84
+
+macro button_prefeed
+      M83
+      G1 E220 F2000
+      M84
+
+button 3 "FAN ON" /c "#C8C8C8" M106 S200
+button 4 "GET POS" /c "#C8C8C8" M114
+button 5 "RETRACT" button_retract
+button 6 "PREFEED" button_prefeed
+button 7 "BL" G1 X135 Y15 F12000
+button 8 "CENTRE" G1 X70 Y70 F12000
+button 9 "BR" G1 X15 Y15 F12000
+button 10 "FL" G1 X135 Y135 F12000
+button 11 "UP" G1 Z6 F200
+button 12 "FR" G1 X15 Y135 F12000
+set last_bed_temperature 60.0
+set baudrate 115200
+set last_file_path /home/reprap/play
+set xy_feedrate 3000
+set z_feedrate 200
+set e_feedrate 1500
+
+set slicecommand /home/reprap/Slic3r/bin/slic3r $s --load /home/reprap/play/slic3r-config.ini --output $o
+
+set sliceoptscommand /home/reprap/Slic3r/bin/slic3r --load /home/reprap/play/slic3r-config.ini --ignore-nonexistent-config
+
+#set sliceoptscommand python xskeinforge/skeinforge_application/skeinforge.py
+#set slicecommand python xskeinforge/skeinforge_application/skeinforge_utilities/skeinforge_craft.py $s
+set build_dimensions 140x150x80+0+0+0
diff --git a/pull-cord-keeper.scad b/pull-cord-keeper.scad
new file mode 100644 (file)
index 0000000..c8655c9
--- /dev/null
@@ -0,0 +1,189 @@
+// -*- C -*-
+
+hoopthick = 3;
+
+hinnerrad = 15;
+houterrad = hinnerrad + hoopthick;
+hcentredist = 10;
+
+blockdepth = 5;
+blockwidth = hcentredist*2 + 6;
+
+height = 20;
+
+roundedgedia = 7.5;
+
+ziglen = hcentredist/2;
+
+feedxgap = 5;
+feedzgap = 5;
+ribsgap = 1;
+
+ribdepth = 3;
+ribheight = 4;
+
+backxgap = 1;
+
+blockoverlapcnr = 5;
+
+screwholedia = 4 + 0.5;
+
+module Oval(centredist, rad) {
+  hull() {
+    translate([-centredist/2,0,0]) circle(r=rad);
+    translate([+centredist/2,0,0]) circle(r=rad);
+  }
+}  
+
+module VExtrude(){
+  translate([0,0, -height/2])
+    linear_extrude(height=20)
+    children(0);
+}
+
+module OuterOval(){
+  Oval(hcentredist, houterrad);
+}
+
+module Hoop(){
+  difference(){
+    hull(){
+      OuterOval();
+      translate([0, (blockdepth + hoopthick)/2 + hinnerrad])
+       square([blockwidth,
+               blockdepth + hoopthick],
+              center=true);
+    }
+    Oval(hcentredist, hinnerrad);
+  }
+}
+
+module RoundEdges(){
+  intersection(){
+    VExtrude()
+      OuterOval();
+
+    for (xi=[-1,1]) {
+      hull(){
+       for (yi=[-1,1]) {
+         translate([xi * (hcentredist/2 + hinnerrad),
+                    houterrad,
+                    yi * (height/2 - roundedgedia / 4 * sqrt(2))])
+           rotate([90,0,0])
+           cylinder(r=roundedgedia/2, h=houterrad*2, $fn=20);
+       }
+      }
+    }
+  }
+}
+
+module Positive(){
+  difference(){
+    VExtrude()
+      Hoop();
+
+    rotate([90,0,0])
+      translate([0,0,-50])
+      cylinder(r=screwholedia/2, h=100);
+  }
+
+  RoundEdges();
+}
+
+module Ribs(){
+  imax = ceil(height*2 / ribheight);
+  for (i=[-imax:imax]) {
+    hull(){
+      translate([-ribdepth/2,
+                ribheight*i,
+                0])
+       polygon([[0,          0],
+                [ribdepth, -ribheight],
+                [ribdepth, +ribheight]]);
+      translate([50, 0])
+       square([1, height*2], center=true);
+    }
+  }
+}          
+
+module Division(cutmore) {
+  mirror([0,0,1]) {
+    translate([0, 0, -cutmore*feedzgap/2]) {
+      translate([-ziglen + -cutmore*feedxgap/2, -100, 0])
+       cube([100, 100, 50]);
+    }
+  }
+  translate([blockwidth/2 - blockoverlapcnr + -cutmore*backxgap/2,
+            -1,
+            -50])
+    cube([100, 100, 100]);
+
+  translate([ziglen + -cutmore*feedxgap/2,
+            -50,
+            -50])
+    cube([100, 51, 100]);
+
+  translate([50,
+            hinnerrad/2 + houterrad/2 + blockdepth/2 + -cutmore*ribsgap/2,
+            0])
+    rotate([-90,0,90])
+    linear_extrude(height=100)
+    Ribs();
+}
+
+module SDemo(){
+  //difference(){
+  %  Positive();
+  //  Division(0);
+  //}
+  Division(-1);
+}
+
+module A(){
+  difference(){
+    Positive();
+    Division(+1);
+  }
+}
+
+module B(){
+  intersection(){
+    Positive();
+    Division(-1);
+  }
+}
+
+module Demo(){
+  color("red") A();
+  color("blue") B();
+}
+
+module APrint(){ ////toplevel
+  rotate([0,180,0])
+    A();
+}
+
+module BPrint(){ ////toplevel
+  B();
+}
+
+module Kit(){ ////toplevel
+  translate([0, hinnerrad, 0])
+    APrint();
+  rotate([0,0,180])
+    BPrint();
+}
+
+//Ribs();
+//Demo();
+
+//A();
+//B();
+//%Division(+1);
+
+//Hoop();
+
+//Demo();
+//BPrint();
+
+//Kit();
diff --git a/quacks-ingredients-L1.scad b/quacks-ingredients-L1.scad
new file mode 100644 (file)
index 0000000..5a85e58
--- /dev/null
@@ -0,0 +1,5 @@
+//  autogenerated by quacks-ingredients-updates-levels - do not edit
+$phase=1;
+module Token_L(){ Token_L1(); }
+//// toplevels-from:
+include <quacks-ingredients.scad>
diff --git a/quacks-ingredients-L2.scad b/quacks-ingredients-L2.scad
new file mode 100644 (file)
index 0000000..6c7a087
--- /dev/null
@@ -0,0 +1,5 @@
+//  autogenerated by quacks-ingredients-updates-levels - do not edit
+$phase=2;
+module Token_L(){ Token_L2(); }
+//// toplevels-from:
+include <quacks-ingredients.scad>
diff --git a/quacks-ingredients-L3.scad b/quacks-ingredients-L3.scad
new file mode 100644 (file)
index 0000000..b39277d
--- /dev/null
@@ -0,0 +1,5 @@
+//  autogenerated by quacks-ingredients-updates-levels - do not edit
+$phase=3;
+module Token_L(){ Token_L3(); }
+//// toplevels-from:
+include <quacks-ingredients.scad>
diff --git a/quacks-ingredients-L4.scad b/quacks-ingredients-L4.scad
new file mode 100644 (file)
index 0000000..9f86e2a
--- /dev/null
@@ -0,0 +1,5 @@
+//  autogenerated by quacks-ingredients-updates-levels - do not edit
+$phase=4;
+module Token_L(){ Token_L4(); }
+//// toplevels-from:
+include <quacks-ingredients.scad>
diff --git a/quacks-ingredients-L5.scad b/quacks-ingredients-L5.scad
new file mode 100644 (file)
index 0000000..2f35fd3
--- /dev/null
@@ -0,0 +1,5 @@
+//  autogenerated by quacks-ingredients-updates-levels - do not edit
+$phase=5;
+module Token_L(){ Token_L5(); }
+//// toplevels-from:
+include <quacks-ingredients.scad>
diff --git a/quacks-ingredients-counts b/quacks-ingredients-counts
new file mode 100755 (executable)
index 0000000..5377cbc
--- /dev/null
@@ -0,0 +1,111 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+use Data::Dumper;
+use POSIX;
+
+our $which = shift @ARGV;
+
+sub xdata ($) {
+    my ($cb) = @_;
+    return unless $which eq 'Base';
+    foreach my $count (qw(1 2 3)) {
+       foreach my $nspots (qw(0 1 2 3 4)) {
+           $_ = $cb->($count,$nspots)."\t".$_;
+       }
+    }
+}
+
+$_=<DATA>; chomp or die;
+xdata sub {
+    my ($xcount,$xnspots) = @_;
+    "${xcount}x". (qw(Zero One Two Three Four)[$xnspots]);
+};
+our @names = split /\t/, $_;
+
+our %count;
+
+foreach my $nspots (qw(1 2 3 4 0)) {
+    $_=<DATA>; chomp or die;
+    xdata sub {
+       my ($xcount,$xnspots) = @_;
+       $xnspots == $nspots and "$xcount+0";
+    };
+    my @l = split /\t/, $_;
+    foreach my $i (0..$#names) {
+       $_ = $l[$i] || '0+0';
+       $_ ||= 0;
+       m/\+/ or die "$which $nspots ?";
+       
+       $count{$names[$i]}{$nspots} =
+           $which eq 'All'     ? $` + $' :
+           $which eq 'Base'    ? $`      :
+           $which eq 'Witches' ?      $' :
+           die "$which ?";
+    }
+}
+
+$_ = Dumper(\%count);
+s{^}{// }mg;
+#print STDERR;
+
+our $name;
+our $total_count;
+our $total_real_count;
+our $max_nrows=0;
+our $max_rowsz=0;
+
+sub wrtoplevel () {
+    my $cs = $count{$name};
+    my $total = 0; $total += $_ foreach values %$cs;
+    return unless $total;
+    print "module ${which}_$name(){ ////toplevel\n";
+    my $rowsz = ceil(sqrt($total));
+    my $nrows = ceil($total / $rowsz);
+    $total_count += $total;
+    $total_real_count += $total if $name =~ m/^[A-Z][a-z]+$/;
+    $max_nrows = $nrows if $nrows > $max_nrows;
+    $max_rowsz = $rowsz if $rowsz > $max_rowsz;
+    my $ix = 0;
+    printf "// %s  %-10s  total=%2d  rowsz=$rowsz  nrows=$nrows\n",
+       $which, "$name", $total;
+    foreach my $nspots (sort keys %$cs) {
+       my $c = $cs->{$nspots};
+       print <<END;
+  union(){
+    Frame(\$phase, token_pitch * [ $rowsz + 1.00, $nrows + 0.50 ]);
+    \$nspots = $nspots;
+END
+       while ($c--) {
+           my $xy = sprintf "[ %5.1f, %5.1f ]",
+               int($ix / $nrows) - 0.5 * ($rowsz-1),
+               $ix % $nrows - 0.5 * ($nrows-1);
+           print "    translate(token_pitch * $xy) Token_L();\n";
+           $ix++;
+       }
+       print <<END;
+  };
+END
+    }
+    print "}\n";
+}
+
+foreach $name (sort keys %count) {
+    wrtoplevel();
+}
+
+print <<END;
+// $which  total_count=$total_count   total_real_count=$total_real_count
+// $which  max_rowsz=$max_rowsz       max_nrows=$max_nrows
+END
+
+STDOUT->error and die $!;
+
+__DATA__
+White  Green   Blue    Red     Yellow  Purple  Black   Orange  Orange6 Loco    WhiteSpare
+21+6   15+10   14+8    12+6    13+6    15+8    18+8    20+12                   1+0
+9+3    10+5    10+5    8+5     6+5                                             1+0
+5+2                                                                            1+0
+       13+5    10+5    10+5    10+5
+                                                               0+20    0+25
diff --git a/quacks-ingredients-counts.scad b/quacks-ingredients-counts.scad
new file mode 100644 (file)
index 0000000..c2a8d08
--- /dev/null
@@ -0,0 +1,1826 @@
+//  autogenerated - do not edit
+//   update script is quacks-ingredients-updates-levels
+//   source is quacks-ingredients-counts
+module Base_1xFour(){ ////toplevel
+// Base  1xFour      total= 1  rowsz=1  nrows=1
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [   0.0,   0.0 ]) Token_L();
+  };
+}
+module Base_1xOne(){ ////toplevel
+// Base  1xOne       total= 1  rowsz=1  nrows=1
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [   0.0,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_1xThree(){ ////toplevel
+// Base  1xThree     total= 1  rowsz=1  nrows=1
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 3;
+    translate(token_pitch * [   0.0,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_1xTwo(){ ////toplevel
+// Base  1xTwo       total= 1  rowsz=1  nrows=1
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [   0.0,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_1xZero(){ ////toplevel
+// Base  1xZero      total= 1  rowsz=1  nrows=1
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 0;
+    translate(token_pitch * [   0.0,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_2xFour(){ ////toplevel
+// Base  2xFour      total= 2  rowsz=2  nrows=1
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [  -0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.0 ]) Token_L();
+  };
+}
+module Base_2xOne(){ ////toplevel
+// Base  2xOne       total= 2  rowsz=2  nrows=1
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_2xThree(){ ////toplevel
+// Base  2xThree     total= 2  rowsz=2  nrows=1
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 3;
+    translate(token_pitch * [  -0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_2xTwo(){ ////toplevel
+// Base  2xTwo       total= 2  rowsz=2  nrows=1
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [  -0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_2xZero(){ ////toplevel
+// Base  2xZero      total= 2  rowsz=2  nrows=1
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 0;
+    translate(token_pitch * [  -0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_3xFour(){ ////toplevel
+// Base  3xFour      total= 3  rowsz=2  nrows=2
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [  -0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -0.5 ]) Token_L();
+  };
+}
+module Base_3xOne(){ ////toplevel
+// Base  3xOne       total= 3  rowsz=2  nrows=2
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_3xThree(){ ////toplevel
+// Base  3xThree     total= 3  rowsz=2  nrows=2
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 3;
+    translate(token_pitch * [  -0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_3xTwo(){ ////toplevel
+// Base  3xTwo       total= 3  rowsz=2  nrows=2
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [  -0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_3xZero(){ ////toplevel
+// Base  3xZero      total= 3  rowsz=2  nrows=2
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 0;
+    translate(token_pitch * [  -0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_Black(){ ////toplevel
+// Base  Black       total=18  rowsz=5  nrows=4
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -2.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   1.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   0.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   0.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   0.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   1.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   1.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   1.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   1.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_Blue(){ ////toplevel
+// Base  Blue        total=34  rowsz=6  nrows=6
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -2.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,   2.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   2.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -1.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [  -0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   2.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   2.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [   1.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   2.5 ]) Token_L();
+    translate(token_pitch * [   2.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [   2.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   2.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   2.5,   0.5 ]) Token_L();
+  };
+}
+module Base_Green(){ ////toplevel
+// Base  Green       total=38  rowsz=7  nrows=6
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 6 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 6 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -3.0,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -3.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -3.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -3.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -3.0,   1.5 ]) Token_L();
+    translate(token_pitch * [  -3.0,   2.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   1.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   2.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 6 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [  -1.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,   1.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,   2.5 ]) Token_L();
+    translate(token_pitch * [   0.0,  -2.5 ]) Token_L();
+    translate(token_pitch * [   0.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   0.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   0.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   0.0,   2.5 ]) Token_L();
+    translate(token_pitch * [   1.0,  -2.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 6 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 6 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [   1.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   1.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   1.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   1.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   1.0,   2.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -2.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   2.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   2.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   2.0,   2.5 ]) Token_L();
+    translate(token_pitch * [   3.0,  -2.5 ]) Token_L();
+    translate(token_pitch * [   3.0,  -1.5 ]) Token_L();
+  };
+}
+module Base_Orange(){ ////toplevel
+// Base  Orange      total=20  rowsz=5  nrows=4
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -2.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   1.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   0.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   0.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   0.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   1.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   1.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   1.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   1.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   2.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   2.0,   1.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_Purple(){ ////toplevel
+// Base  Purple      total=15  rowsz=4  nrows=4
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -1.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_Red(){ ////toplevel
+// Base  Red         total=30  rowsz=6  nrows=5
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -2.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,   2.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   2.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -1.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [  -0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   2.0 ]) Token_L();
+    translate(token_pitch * [   0.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [   0.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   1.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   2.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [   1.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [   1.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   1.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   2.0 ]) Token_L();
+    translate(token_pitch * [   2.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [   2.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   2.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   2.5,   1.0 ]) Token_L();
+    translate(token_pitch * [   2.5,   2.0 ]) Token_L();
+  };
+}
+module Base_White(){ ////toplevel
+// Base  White       total=35  rowsz=6  nrows=6
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -2.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,   2.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   2.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   2.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [   0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   2.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   2.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 3;
+    translate(token_pitch * [   2.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [   2.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   2.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   2.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   2.5,   1.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_WhiteSpare(){ ////toplevel
+// Base  WhiteSpare  total= 3  rowsz=2  nrows=2
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -0.5,  -0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [  -0.5,   0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 3;
+    translate(token_pitch * [   0.5,  -0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Base_Yellow(){ ////toplevel
+// Base  Yellow      total=29  rowsz=6  nrows=5
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -2.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,   2.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   2.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [  -0.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   2.0 ]) Token_L();
+    translate(token_pitch * [   0.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [   0.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   1.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [   0.5,   2.0 ]) Token_L();
+    translate(token_pitch * [   1.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [   1.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   1.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   2.0 ]) Token_L();
+    translate(token_pitch * [   2.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [   2.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   2.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   2.5,   1.0 ]) Token_L();
+  };
+}
+// Base  total_count=252   total_real_count=219
+// Base  max_rowsz=7       max_nrows=6
+module All_Black(){ ////toplevel
+// All  Black       total=26  rowsz=6  nrows=5
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -2.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,   2.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   2.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   2.0 ]) Token_L();
+    translate(token_pitch * [   0.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [   0.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   1.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   2.0 ]) Token_L();
+    translate(token_pitch * [   1.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [   1.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   1.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   2.0 ]) Token_L();
+    translate(token_pitch * [   2.5,  -2.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module All_Blue(){ ////toplevel
+// All  Blue        total=52  rowsz=8  nrows=7
+  union(){
+    Frame($phase, token_pitch * [ 8 + 1.00, 7 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 8 + 1.00, 7 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -3.5,  -3.0 ]) Token_L();
+    translate(token_pitch * [  -3.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -3.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -3.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -3.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -3.5,   2.0 ]) Token_L();
+    translate(token_pitch * [  -3.5,   3.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -3.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,   2.0 ]) Token_L();
+    translate(token_pitch * [  -2.5,   3.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -3.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   2.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   3.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -3.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 8 + 1.00, 7 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [  -0.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   2.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   3.0 ]) Token_L();
+    translate(token_pitch * [   0.5,  -3.0 ]) Token_L();
+    translate(token_pitch * [   0.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [   0.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   1.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   2.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   3.0 ]) Token_L();
+    translate(token_pitch * [   1.5,  -3.0 ]) Token_L();
+    translate(token_pitch * [   1.5,  -2.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 8 + 1.00, 7 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 8 + 1.00, 7 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [   1.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   1.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   2.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   3.0 ]) Token_L();
+    translate(token_pitch * [   2.5,  -3.0 ]) Token_L();
+    translate(token_pitch * [   2.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [   2.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   2.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   2.5,   1.0 ]) Token_L();
+    translate(token_pitch * [   2.5,   2.0 ]) Token_L();
+    translate(token_pitch * [   2.5,   3.0 ]) Token_L();
+    translate(token_pitch * [   3.5,  -3.0 ]) Token_L();
+    translate(token_pitch * [   3.5,  -2.0 ]) Token_L();
+    translate(token_pitch * [   3.5,  -1.0 ]) Token_L();
+  };
+}
+module All_Green(){ ////toplevel
+// All  Green       total=58  rowsz=8  nrows=8
+  union(){
+    Frame($phase, token_pitch * [ 8 + 1.00, 8 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 8 + 1.00, 8 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -3.5,  -3.5 ]) Token_L();
+    translate(token_pitch * [  -3.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -3.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -3.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -3.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -3.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -3.5,   2.5 ]) Token_L();
+    translate(token_pitch * [  -3.5,   3.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -3.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,   2.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,   3.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -3.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   2.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   3.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -3.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 8 + 1.00, 8 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [  -0.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   2.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   3.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -3.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   2.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   3.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 8 + 1.00, 8 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 8 + 1.00, 8 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [   1.5,  -3.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   2.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   3.5 ]) Token_L();
+    translate(token_pitch * [   2.5,  -3.5 ]) Token_L();
+    translate(token_pitch * [   2.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [   2.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   2.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   2.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   2.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   2.5,   2.5 ]) Token_L();
+    translate(token_pitch * [   2.5,   3.5 ]) Token_L();
+    translate(token_pitch * [   3.5,  -3.5 ]) Token_L();
+    translate(token_pitch * [   3.5,  -2.5 ]) Token_L();
+  };
+}
+module All_Loco(){ ////toplevel
+// All  Loco        total=25  rowsz=5  nrows=5
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 0;
+    translate(token_pitch * [  -2.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   1.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   2.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   1.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   2.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module All_Orange(){ ////toplevel
+// All  Orange      total=32  rowsz=6  nrows=6
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -2.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -2.5,   2.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   2.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   2.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   2.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   2.5 ]) Token_L();
+    translate(token_pitch * [   2.5,  -2.5 ]) Token_L();
+    translate(token_pitch * [   2.5,  -1.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module All_Orange6(){ ////toplevel
+// All  Orange6     total=20  rowsz=5  nrows=4
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 0;
+    translate(token_pitch * [  -2.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   1.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   0.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   0.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   0.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   1.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   1.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   1.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   1.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   2.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   2.0,   1.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module All_Purple(){ ////toplevel
+// All  Purple      total=23  rowsz=5  nrows=5
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -2.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   1.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   2.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   1.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module All_Red(){ ////toplevel
+// All  Red         total=46  rowsz=7  nrows=7
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -3.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,   1.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,   2.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,   3.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   1.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   2.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   3.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [  -1.0,   1.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   2.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   3.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   3.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -1.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [   1.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   3.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   3.0 ]) Token_L();
+    translate(token_pitch * [   3.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [   3.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   3.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   3.0,   0.0 ]) Token_L();
+  };
+}
+module All_White(){ ////toplevel
+// All  White       total=46  rowsz=7  nrows=7
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -3.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,   1.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,   2.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,   3.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   1.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   2.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   3.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   1.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   2.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   3.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   2.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [   0.0,   3.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   3.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 3;
+    translate(token_pitch * [   2.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   3.0 ]) Token_L();
+    translate(token_pitch * [   3.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [   3.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   3.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   3.0,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module All_WhiteSpare(){ ////toplevel
+// All  WhiteSpare  total= 3  rowsz=2  nrows=2
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -0.5,  -0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [  -0.5,   0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 3;
+    translate(token_pitch * [   0.5,  -0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module All_Yellow(){ ////toplevel
+// All  Yellow      total=45  rowsz=7  nrows=7
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -3.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,   1.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,   2.0 ]) Token_L();
+    translate(token_pitch * [  -3.0,   3.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   1.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   2.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   3.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   1.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [  -1.0,   2.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   3.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   3.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -2.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [   1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   3.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   3.0 ]) Token_L();
+    translate(token_pitch * [   3.0,  -3.0 ]) Token_L();
+    translate(token_pitch * [   3.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   3.0,  -1.0 ]) Token_L();
+  };
+}
+// All  total_count=376   total_real_count=353
+// All  max_rowsz=8       max_nrows=8
+module Witches_Black(){ ////toplevel
+// Witches  Black       total= 8  rowsz=3  nrows=3
+  union(){
+    Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Witches_Blue(){ ////toplevel
+// Witches  Blue        total=18  rowsz=5  nrows=4
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -2.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   1.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,   1.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [   0.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   0.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   0.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   1.0,  -1.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [   1.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   1.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   1.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -0.5 ]) Token_L();
+  };
+}
+module Witches_Green(){ ////toplevel
+// Witches  Green       total=20  rowsz=5  nrows=4
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -2.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   1.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   0.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   0.0,  -0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [   0.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   1.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   1.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   1.0,   0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [   1.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   2.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   2.0,   1.5 ]) Token_L();
+  };
+}
+module Witches_Loco(){ ////toplevel
+// Witches  Loco        total=25  rowsz=5  nrows=5
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 0;
+    translate(token_pitch * [  -2.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   1.0 ]) Token_L();
+    translate(token_pitch * [  -2.0,   2.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   1.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   2.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -2.0 ]) Token_L();
+    translate(token_pitch * [   2.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   2.0,   2.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Witches_Orange(){ ////toplevel
+// Witches  Orange      total=12  rowsz=4  nrows=3
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -1.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   1.0 ]) Token_L();
+    translate(token_pitch * [   0.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   1.0 ]) Token_L();
+    translate(token_pitch * [   1.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   1.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Witches_Orange6(){ ////toplevel
+// Witches  Orange6     total=20  rowsz=5  nrows=4
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 0;
+    translate(token_pitch * [  -2.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -2.0,   1.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,   0.5 ]) Token_L();
+    translate(token_pitch * [  -1.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   0.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   0.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   0.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   1.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   1.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   1.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   1.0,   1.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -1.5 ]) Token_L();
+    translate(token_pitch * [   2.0,  -0.5 ]) Token_L();
+    translate(token_pitch * [   2.0,   0.5 ]) Token_L();
+    translate(token_pitch * [   2.0,   1.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 1;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Witches_Purple(){ ////toplevel
+// Witches  Purple      total= 8  rowsz=3  nrows=3
+  union(){
+    Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   0.0 ]) Token_L();
+    translate(token_pitch * [  -1.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.0,   1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,  -1.0 ]) Token_L();
+    translate(token_pitch * [   1.0,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]);
+    $nspots = 2;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Witches_Red(){ ////toplevel
+// Witches  Red         total=16  rowsz=4  nrows=4
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -1.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [  -0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [   0.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   1.5 ]) Token_L();
+  };
+}
+module Witches_White(){ ////toplevel
+// Witches  White       total=11  rowsz=4  nrows=3
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -1.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -1.5,   1.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [  -0.5,   1.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [   0.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.0 ]) Token_L();
+    translate(token_pitch * [   0.5,   1.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]);
+    $nspots = 3;
+    translate(token_pitch * [   1.5,  -1.0 ]) Token_L();
+    translate(token_pitch * [   1.5,   0.0 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]);
+    $nspots = 4;
+  };
+}
+module Witches_Yellow(){ ////toplevel
+// Witches  Yellow      total=16  rowsz=4  nrows=4
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 0;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 1;
+    translate(token_pitch * [  -1.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -1.5,   1.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,  -0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 2;
+    translate(token_pitch * [  -0.5,   0.5 ]) Token_L();
+    translate(token_pitch * [  -0.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   0.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   0.5,   0.5 ]) Token_L();
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 3;
+  };
+  union(){
+    Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]);
+    $nspots = 4;
+    translate(token_pitch * [   0.5,   1.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -1.5 ]) Token_L();
+    translate(token_pitch * [   1.5,  -0.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   0.5 ]) Token_L();
+    translate(token_pitch * [   1.5,   1.5 ]) Token_L();
+  };
+}
+// Witches  total_count=154   total_real_count=134
+// Witches  max_rowsz=5       max_nrows=5
diff --git a/quacks-ingredients-demos.scad b/quacks-ingredients-demos.scad
new file mode 100644 (file)
index 0000000..1f02c60
--- /dev/null
@@ -0,0 +1,39 @@
+// -*- C -*-
+
+include <quacks-ingredients.scad>
+
+sandingframe_gap = 0.3; // to be added to radius
+sandingframe_nw = 5;
+sandingframe_nl = 6;
+sandingframe_th = thick*2/3;
+
+module Demo(){ ////toplevel
+  $nspots = 3;
+  color("red") { Token_L3(); }
+  color("white") { Token_L1(); Token_L5(); }
+  color("black") { Token_L2(); Token_L4(); }
+}
+
+module SandingFrame(){ ////toplevel
+  stridel = token_dia + 5;
+  nl = sandingframe_nl;
+  nw = sandingframe_nw;
+  stridew = stridel * cos(30);
+  stride = [stridel,stridew];
+
+  linear_extrude(height = sandingframe_th) {
+    difference(){
+      translate((token_dia/2 + stridel) * 0.5 * [-1,-1])
+       square([ stridel * (nl + 0.5),
+                stridew * nw + stridel * 0.5 ]);
+      for (j = [0 : nw-1]) {
+       eo = j % 2;
+       for (i = [0 : nl-1-0.5*eo]) {
+         translate([stridel * (i + 0.5 * eo),
+                    stridew * j])
+           circle(r = token_dia/2 + sandingframe_gap);
+       }
+      }
+    }
+  }
+}
diff --git a/quacks-ingredients-make-copy-gcodes b/quacks-ingredients-make-copy-gcodes
new file mode 100755 (executable)
index 0000000..fd34367
--- /dev/null
@@ -0,0 +1,135 @@
+#!/bin/bash
+#
+# usage:
+#    ./quacks-ingredients-make-copy-gcodes Tests_L
+# Uses
+#    quacks-ingredients-L$l,Tests_L.auto.stl
+# to make
+#    quacks-ingredients-L$l,Tests_L.auto.gcode
+# and then edits them and copies them to the SD card as
+#    PREPARED/Q$l.G
+
+set -e
+
+f=$1
+shift
+
+lhs=quacks-ingredients-L
+
+gh () {
+       g=$lhs$l.auto.gcode
+       h=/media/sd/PREPARED/Q$l.G
+}
+
+for l in 1 2 3 4 5; do
+       gh
+       qi=quacks-L$l.auto.ini
+
+       cp quacks.ini $qi
+
+       case $l in
+       2|4|5)
+               perl -i~ -pe '
+                       s/^(retraction_hop *= *.*)/retraction_hop = 0.6/m
+               ' $qi
+               ;;
+       esac
+
+       cura -i $qi -s $lhs$l,$f.auto.stl -o $g
+       case $l in
+       1|2|3|4)
+               perl -i~ -pe 's/^/;/ if m/^M140 S0\b.*\n/' $g
+               ;;
+       esac
+
+       perl -i~ -ne '
+               $l =~ s/^/;/ if m/^M400/;
+               $l .= "G91\nG1 Z5\nG90\n" if m/^M84/;
+               print $l or die $!;
+               $l = $_;
+               END { print $l or die $!; }
+       ' $g
+done
+
+for l in 2 4 5; do
+       gh
+       : perl -i~ -0777 -ne '
+               @l = split m{^(?=;LAYER:\d+\n)}m, $_;
+               foreach my $i (0..$#l) {
+                       $l[$i] =~
+                               s{
+                               (       ^G1 \ Z\S+ \s*\n        
+                                 |     ^\;LAYER:\d+ \s*\n
+                                   (?: ^M10[67].* \s*\n )?
+                               )
+                                       ^G0 \ F(\d+) \ ( X\S+ \ Y\S+ )
+                                                        \ Z(\S+) \s*\n
+                               }{
+                                       die "$& $2" unless $2 > 9000;
+                                       $1 .
+                                       "G1 Z$4\n".
+                                       "G0 F$2 $3\n".
+                                       "G1 Z$4\n"
+                               }mxe    or $i==0
+                                       or die "$l[$i] $i";
+                       $l[$i] =~
+                               s{
+                                       ^G1 \ Z([0-9.]+) \s*\n
+                               (       ^G0 \ F(\d+) \ X\S+ \ Y\S+ \s*\n
+                                       (?: ; .* \s*\n )?
+                                       ^G1 \ Z([0-9.]+) \s*\n  )
+                               }{
+                                       die "$& $3" unless $1 >= $4;
+                                       die "$& $3" unless $3 > 9000;
+                                       my $z = $i == $#l ? $1 : $4 + 0.5;
+                                       "G0 F$3\n".
+                                       "G1 Z$z\n" .
+                                       $2
+                               }gmxe or $i==0 or die "$l[$i] $i";
+               }
+               print or die $! foreach @l;
+       ' $g
+done
+
+exec 3>${lhs}234.auto.gcode
+for l in 2 3; do
+       gh
+       perl -pe 's/^/;/ if m/^M104 S0\b/ || (m/^M84/..0)' $g >&3
+done
+for l in 4; do
+       gh
+       cat $g >&3
+done
+
+copies="3:5 2:234 1:1"
+copyls=""
+for copy in $copies; do
+       l=-P-${copy%:*}
+       ci=$lhs${copy#*:}.auto.gcode
+       copyls+=" $l"
+       gh
+       rm -f $g
+       ln -vs $ci $g
+done
+
+umount /media/sd >/dev/null 2>&1 ||:
+mount /media/sd
+
+for l in 1 2 3 4 5 234 $copyls; do
+       gh
+       cp $g $h
+done
+
+sleep 0.5
+
+umount /media/sd
+mount /media/sd
+
+for l in 1 2 3 4 5 234 $copyls; do
+       gh
+       cmp $g $h
+       ls -l $h
+done
+sleep 0.5
+
+umount /media/sd
diff --git a/quacks-ingredients-update-levels b/quacks-ingredients-update-levels
new file mode 100755 (executable)
index 0000000..395770d
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+set -e
+
+for l in 1 2 3 4 5; do
+       f=quacks-ingredients-L$l.scad
+       cat >$f.tmp <<END
+//  autogenerated by quacks-ingredients-updates-levels - do not edit
+\$phase=$l;
+module Token_L(){ Token_L$l(); }
+//// toplevels-from:
+include <quacks-ingredients.scad>
+END
+       mv -f $f.tmp $f
+done
+
+f=quacks-ingredients-counts.scad
+cat >$f.tmp <<END
+//  autogenerated - do not edit
+//   update script is quacks-ingredients-updates-levels
+//   source is quacks-ingredients-counts
+END
+
+for which in Base All Witches; do
+       ./quacks-ingredients-counts $which >>$f.tmp
+done
+mv -f $f.tmp $f
+
+egrep '^// [A-Z][a-z]*  *[A-Za-z]' $f | sort
diff --git a/quacks-ingredients.scad b/quacks-ingredients.scad
new file mode 100644 (file)
index 0000000..6925a1b
--- /dev/null
@@ -0,0 +1,145 @@
+// -*- C -*-
+
+//
+// git clean -xdff
+// ./quacks-ingredients-update-levels
+// make autoincs
+//
+// make -j8 quacks-stls 2>&1 | tee log
+// OR EG
+// make -j8 quacks-ingredients-L{1,2,3,4,5},Base_Yellow.auto.stl
+//
+// ./quacks-ingredients-make-copy-gcodes Base_Yellow   etc.
+//
+//   Print Q-P-1 in spots, Q-P-2 in main colour, Q-P-3 in spots
+//
+// For colours which only have zero-spot counters, print only Q-P-2
+
+token_dia = 20;
+spot_dia = 4.3;
+spot_gap = spot_dia / 3.0;
+
+thick = 3.0;
+
+multicolour_gap = 0.075; // each side
+initial_layer_thick = 0.400;
+initial_layer_width = 0.750;
+final_layer_thick = 0.500;
+multicolour_post = 4;
+
+$fs=0.1;
+$fa=1;
+
+// calculated
+
+token_pitch = token_dia + 3;
+
+// autoadjusted
+
+$spots_absent = false;
+$spots_plusgap = false;
+
+module Spots_Extrude_Lower(){
+  d = $spots_plusgap ? 1 : 0;
+  translate([0,0,-d])
+    linear_extrude(height= initial_layer_thick + d)
+    children(0);
+}
+
+module Spots_Extrude_Upper(){
+  d = $spots_plusgap ? 1 : 0;
+  translate([0,0, thick + d])
+    mirror([0,0, 1])
+    linear_extrude(height= final_layer_thick + d)
+    children(0);
+}
+
+module SpotAt(condition, xy) {
+  if (condition == !$spots_absent) {
+    translate(xy * (spot_gap + spot_dia) * sqrt(0.5))
+      circle(r= spot_dia/2 +
+            ($spots_plusgap ? multicolour_gap : 0));
+  }
+}
+
+module Token_Spots(){
+  SpotAt(($nspots % 2) > 0,  [0,0]);
+  SpotAt($nspots >= 2, [ 1, 1]);
+  SpotAt($nspots >= 2, [-1,-1]);
+  SpotAt($nspots >= 4, [ 1,-1]);
+  SpotAt($nspots >= 4, [-1, 1]);
+}
+
+module Token_Spots_All(){
+  $nspots = 5;
+  Token_Spots();
+}
+
+module Token_L1(){
+  Spots_Extrude_Lower()
+    Token_Spots();
+}
+
+module Token_L2(){
+  $spots_absent = true;
+  Spots_Extrude_Lower()
+    Token_Spots();
+}
+
+module Token_L3(){
+  $spots_plusgap = true;
+  difference(){
+    linear_extrude(height=thick)
+      circle(r=token_dia/2);
+    Spots_Extrude_Lower() Token_Spots_All();
+    Spots_Extrude_Upper() Token_Spots_All();
+  }
+}
+
+module Token_L4(){
+  $spots_absent = true;
+  Spots_Extrude_Upper()
+    Token_Spots();
+}
+
+module Token_L5(){
+  Spots_Extrude_Upper()
+    Token_Spots();
+}
+
+module Frame(phase, base_sz) {
+  zs = [ initial_layer_thick,
+        initial_layer_thick,
+        thick,
+        thick,
+        thick ];
+
+  sz = base_sz + phase * initial_layer_width * 2 * [1,1];
+  linear_extrude(height= initial_layer_thick) {
+    difference(){
+      square(center=true, sz + initial_layer_width * 2 * [1,1]);
+      square(center=true, sz);
+    }
+  }
+  // Priming tower
+  translate([-base_sz[0]/2, (2.8-phase)*(multicolour_post*1.7)])
+    linear_extrude(height= zs[phase-1])
+    square(multicolour_post);
+}
+
+module Tests(){
+  for ($nspots = [1,2,3,4]) {
+    translate(($nspots - 2) * token_pitch * [1,0])
+      children();
+  }
+}
+
+module Tests_L() { ////toplevel
+  Frame($phase, token_dia * [ 6, 1.5 ]);
+  Tests() Token_L();
+}
+
+//// toplevels-from:
+include <quacks-ingredients-counts.scad>
+
+//Demo();
diff --git a/quacks.ini b/quacks.ini
new file mode 100644 (file)
index 0000000..8a4f7cc
--- /dev/null
@@ -0,0 +1,266 @@
+[profile]
+layer_height = 0.14
+wall_thickness = 1
+retraction_enable = True
+solid_layer_thickness = 1
+fill_density = 20
+perimeter_before_infill = True
+nozzle_size = 0.5
+print_speed = 50
+print_temperature = 0
+print_temperature2 = 0
+print_temperature3 = 0
+print_temperature4 = 0
+print_temperature5 = 0
+print_bed_temperature = 0
+support = None
+platform_adhesion = None
+support_dual_extrusion = Both
+wipe_tower = False
+wipe_tower_volume = 15
+ooze_shield = False
+filament_diameter = 2.85
+filament_diameter2 = 0
+filament_diameter3 = 0
+filament_diameter4 = 0
+filament_diameter5 = 0
+filament_flow = 100.0
+retraction_speed = 10
+retraction_amount = 1.5
+retraction_dual_amount = 16.5
+retraction_min_travel = 1.5
+retraction_combing = All
+retraction_minimal_extrusion = 0.005
+retraction_hop = 0.1
+bottom_thickness = 0.425
+layer0_width_factor = 125
+object_sink = 0.0
+overlap_dual = 0.15
+travel_speed = 175
+bottom_layer_speed = 15
+infill_speed = 40
+solidarea_speed = 30
+inset0_speed = 30
+insetx_speed = 35
+fill_angle = 45
+cool_min_layer_time = 20
+fan_enabled = True
+skirt_line_count = 1
+skirt_gap = 3.0
+skirt_minimal_length = 250
+fan_full_height = 0.5
+fan_speed = 75
+fan_speed_max = 100
+cool_min_feedrate = 10
+cool_head_lift = False
+solid_top = True
+solid_bottom = True
+fill_overlap = 15
+support_type = Lines
+support_angle = 60
+support_fill_rate = 30
+support_xy_distance = 1.5
+support_z_distance = 0.1
+spiralize = False
+simple_mode = False
+brim_line_count = 10
+raft_margin = 5.0
+raft_line_spacing = 3.0
+raft_base_thickness = 0.3
+raft_base_linewidth = 1.0
+raft_interface_thickness = 0.27
+raft_interface_linewidth = 0.4
+raft_airgap_all = 0.0
+raft_airgap = 0.5
+raft_surface_layers = 2
+raft_surface_thickness = 0.27
+raft_surface_linewidth = 0.4
+fix_horrible_union_all_type_a = True
+fix_horrible_union_all_type_b = False
+fix_horrible_use_open_bits = False
+fix_horrible_extensive_stitching = False
+plugin_config = 
+object_center_x = -1
+object_center_y = -1
+simplemodesettings = 
+simplemodeprofile = Standard
+simplemodematerial = PLA
+simplemodematerialtype = Beginner
+
+[alterations]
+start.gcode = ;Sliced at: {day} {date} {time}
+       ;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {fill_density}
+       ;Print time: {print_time}
+       ;Filament used: {filament_amount}m {filament_weight}g
+       ;Filament cost: {filament_cost}
+       ;M190 R{print_bed_temperature} ;Uncomment to add your own bed temperature line
+       ;M109 R{print_temperature} ;Uncomment to add your own temperature line
+       G21                     ;metric values
+       G90                     ;absolute positioning
+       M82                     ;set extruder to absolute mode
+       M107                    ;start with the fan off
+       G28 X0 Y0               ;move X/Y to min endstops
+       G28 Z0                  ;move Z to min endstops
+       G1 Z15.0 F{travel_speed};move the platform down 15mm
+       G92 E0                  ; zero the extruded length
+       G1 F200 E0              ; extrude 3mm of feed stock
+       G92 E0                  ; zero the extruded length again
+       G1 F{travel_speed}      ; set travel speed
+       M203 X192 Y208 Z3       ; speed limits
+       M117 Printing...        ; send message to LCD
+end.gcode = M400
+       M104 S0                        ; hotend off
+       M140 S0                        ; heated bed heater off (if you have it)
+       M107                           ; fans off
+       G91                            ; relative positioning
+       G1 E-1 F300                    ; retract the filament a bit before lifting the nozzle, to release some of the pressure
+       G1 Z+0.5 E-5 X-20 Y-20 F3000   ; move Z up a bit and retract filament even more
+       G90                            ; absolute positioning
+       G1 X0 Y250                     ; move to cooling position
+       M84                            ; steppers off
+       G90                            ; absolute positioning
+       M117 TAZ Ready.
+       ;{profile_string}
+start2.gcode = ;Sliced at: {day} {date} {time}
+       ;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {fill_density}
+       ;Print time: {print_time}
+       ;Filament used: {filament_amount}m {filament_weight}g
+       ;Filament cost: {filament_cost}
+       ;M190 R{print_bed_temperature} ;Uncomment to add your own bed temperature line
+       ;M104 S{print_temperature} ;Uncomment to add your own temperature line
+       ;M109 T1 S{print_temperature2} ;Uncomment to add your own temperature line
+       ;M109 T0 S{print_temperature} ;Uncomment to add your own temperature line
+       G21        ;metric values
+       G90        ;absolute positioning
+       M107       ;start with the fan off
+       G28 X0 Y0  ;move X/Y to min endstops
+       G28 Z0     ;move Z to min endstops
+       G1 Z15.0 F{travel_speed} ;move the platform down 15mm
+       T1                      ;Switch to the 2nd extruder
+       G92 E0                  ;zero the extruded length
+       G1 F200 E10             ;extrude 10mm of feed stock
+       G92 E0                  ;zero the extruded length again
+       G1 F200 E-{retraction_dual_amount}
+       T0                      ;Switch to the first extruder
+       G92 E0                  ;zero the extruded length
+       G1 F200 E10             ;extrude 10mm of feed stock
+       G92 E0                  ;zero the extruded length again
+       G1 F{travel_speed}
+       ;Put printing message on LCD screen
+       M117 Printing...
+end2.gcode = ;End GCode
+       M104 T0 S0                     ;extruder heater off
+       M104 T1 S0                     ;extruder heater off
+       M140 S0                     ;heated bed heater off (if you have it)
+       G91                                    ;relative positioning
+       G1 E-1 F300                            ;retract the filament a bit before lifting the nozzle, to release some of the pressure
+       G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more
+       G28 X0 Y0                              ;move X/Y to min endstops, so the head is out of the way
+       M84                         ;steppers off
+       G90                         ;absolute positioning
+       ;{profile_string}
+start3.gcode = ;Sliced at: {day} {date} {time}
+       ;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {fill_density}
+       ;Print time: {print_time}
+       ;Filament used: {filament_amount}m {filament_weight}g
+       ;Filament cost: {filament_cost}
+       ;M190 R{print_bed_temperature} ;Uncomment to add your own bed temperature line
+       ;M104 S{print_temperature} ;Uncomment to add your own temperature line
+       ;M109 T1 S{print_temperature2} ;Uncomment to add your own temperature line
+       ;M109 T0 S{print_temperature} ;Uncomment to add your own temperature line
+       G21        ;metric values
+       G90        ;absolute positioning
+       M107       ;start with the fan off
+       G28 X0 Y0  ;move X/Y to min endstops
+       G28 Z0     ;move Z to min endstops
+       G1 Z15.0 F{travel_speed} ;move the platform down 15mm
+       T2                      ;Switch to the 2nd extruder
+       G92 E0                  ;zero the extruded length
+       G1 F200 E10             ;extrude 10mm of feed stock
+       G92 E0                  ;zero the extruded length again
+       G1 F200 E-{retraction_dual_amount}
+       T1                      ;Switch to the 2nd extruder
+       G92 E0                  ;zero the extruded length
+       G1 F200 E10             ;extrude 10mm of feed stock
+       G92 E0                  ;zero the extruded length again
+       G1 F200 E-{retraction_dual_amount}
+       T0                      ;Switch to the first extruder
+       G92 E0                  ;zero the extruded length
+       G1 F200 E10             ;extrude 10mm of feed stock
+       G92 E0                  ;zero the extruded length again
+       G1 F{travel_speed}
+       ;Put printing message on LCD screen
+       M117 Printing...
+end3.gcode = ;End GCode
+       M104 T0 S0                     ;extruder heater off
+       M104 T1 S0                     ;extruder heater off
+       M104 T2 S0                     ;extruder heater off
+       M140 S0                     ;heated bed heater off (if you have it)
+       G91                                    ;relative positioning
+       G1 E-1 F300                            ;retract the filament a bit before lifting the nozzle, to release some of the pressure
+       G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more
+       G28 X0 Y0                              ;move X/Y to min endstops, so the head is out of the way
+       M84                         ;steppers off
+       G90                         ;absolute positioning
+       ;{profile_string}
+start4.gcode = ;Sliced at: {day} {date} {time}
+       ;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {fill_density}
+       ;Print time: {print_time}
+       ;Filament used: {filament_amount}m {filament_weight}g
+       ;Filament cost: {filament_cost}
+       ;M190 R{print_bed_temperature} ;Uncomment to add your own bed temperature line
+       ;M104 S{print_temperature} ;Uncomment to add your own temperature line
+       ;M109 T2 S{print_temperature2} ;Uncomment to add your own temperature line
+       ;M109 T1 S{print_temperature2} ;Uncomment to add your own temperature line
+       ;M109 T0 S{print_temperature} ;Uncomment to add your own temperature line
+       G21        ;metric values
+       G90        ;absolute positioning
+       M107       ;start with the fan off
+       G28 X0 Y0  ;move X/Y to min endstops
+       G28 Z0     ;move Z to min endstops
+       G1 Z15.0 F{travel_speed} ;move the platform down 15mm
+       T3                      ;Switch to the 4th extruder
+       G92 E0                  ;zero the extruded length
+       G1 F200 E10             ;extrude 10mm of feed stock
+       G92 E0                  ;zero the extruded length again
+       G1 F200 E-{retraction_dual_amount}
+       T2                      ;Switch to the 3th extruder
+       G92 E0                  ;zero the extruded length
+       G1 F200 E10             ;extrude 10mm of feed stock
+       G92 E0                  ;zero the extruded length again
+       G1 F200 E-{retraction_dual_amount}
+       T1                      ;Switch to the 2nd extruder
+       G92 E0                  ;zero the extruded length
+       G1 F200 E10             ;extrude 10mm of feed stock
+       G92 E0                  ;zero the extruded length again
+       G1 F200 E-{retraction_dual_amount}
+       T0                      ;Switch to the first extruder
+       G92 E0                  ;zero the extruded length
+       G1 F200 E10             ;extrude 10mm of feed stock
+       G92 E0                  ;zero the extruded length again
+       G1 F{travel_speed}
+       ;Put printing message on LCD screen
+       M117 Printing...
+end4.gcode = ;End GCode
+       M104 T0 S0                     ;extruder heater off
+       M104 T1 S0                     ;extruder heater off
+       M104 T2 S0                     ;extruder heater off
+       M104 T3 S0                     ;extruder heater off
+       M140 S0                     ;heated bed heater off (if you have it)
+       G91                                    ;relative positioning
+       G1 E-1 F300                            ;retract the filament a bit before lifting the nozzle, to release some of the pressure
+       G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more
+       G28 X0 Y0                              ;move X/Y to min endstops, so the head is out of the way
+       M84                         ;steppers off
+       G90                         ;absolute positioning
+       ;{profile_string}
+support_start.gcode = 
+support_end.gcode = 
+cool_start.gcode = 
+cool_end.gcode = 
+replace.csv = 
+preswitchextruder.gcode = ;Switch between the current extruder and the next extruder, when printing with multiple extruders.
+       ;This code is added before the T(n)
+postswitchextruder.gcode = ;Switch between the current extruder and the next extruder, when printing with multiple extruders.
+       ;This code is added after the T(n)
+
diff --git a/question-question.fig b/question-question.fig
new file mode 100644 (file)
index 0000000..6d4e5b9
--- /dev/null
@@ -0,0 +1,11 @@
+#FIG 3.2  Produced by xfig version 3.2.6a
+Landscape
+Center
+Metric
+A4
+100.00
+Single
+-2
+1200 2
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3780 3870 675 675 3780 3870 4455 3870
+4 0 0 34 -1 14 64 0.0000 4 645 645 3465 4185 ?\001
diff --git a/question-token.scad b/question-token.scad
new file mode 100644 (file)
index 0000000..2bd7bc6
--- /dev/null
@@ -0,0 +1,25 @@
+// -*- C -*-
+
+tokenrad=13;
+tokenthick=1.9;
+
+joinwidth=1.0;
+
+circlerad=15;
+
+module Letter() {
+  translate([-circlerad,-circlerad])
+    import("question-question.dxf", convexity=100);
+}
+
+module Token() { ////toplevel
+  rotate([0,180,0])
+  linear_extrude(height=tokenthick) union(){
+    difference(){
+      %circle(tokenrad);
+      Letter();
+    }
+  }
+}
+
+Token();
diff --git a/read-firmware b/read-firmware
new file mode 100755 (executable)
index 0000000..153e803
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -ex
+
+if [ $# = 0 ]; then
+   port=/dev/ttyUSB0
+else
+   port="$1"; shift
+fi
+
+ad () {
+   avrdude -b 38400 -v -P $port -p atmega644P -c arduino "$@"
+}
+
+args=''
+for f in flash eeprom hfuse lfuse efuse; do
+    args+=" -U $f:r:firmware-$f.hex:i"
+done
+
+ad "$@"
+ad "$@" $args
diff --git a/reprap-objects.make b/reprap-objects.make
new file mode 100644 (file)
index 0000000..33e92d6
--- /dev/null
@@ -0,0 +1,86 @@
+# reprap-objects Makefile, reuseable parts
+#
+# Build scripts for various 3D designs
+# Copyright 2012-2016 Ian Jackson
+#
+# This work is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This work 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 work.  If not, see <http://www.gnu.org/licenses/>.
+
+
+HRR=/home/reprap
+SLIC3R=$(HRR)/Slic3r/bin/slic3r
+M4=m4
+SKEINFORGE=python $(HRR)/reprappro-software.git/skeinforge/skeinforge_application/skeinforge_utilities/skeinforge_craft.py
+
+CWD := $(shell pwd)
+PLAY ?= $(CWD)
+
+AUTO_TOPLEVELS := $(foreach m,$(USING_AUTOS),$(shell $(PLAY)/toplevel-find $m))
+
+AUTO_INCS += funcs.scad
+
+default:       autoincs scads
+
+$(shell set -xe; $(PLAY)/commitid.scad.pl >commitid.scad.tmp; cmp commitid.scad.tmp commitid.scad || mv -f commitid.scad.tmp commitid.scad )
+
+autoincs:      $(AUTO_INCS) $(AUTO_STLS_INCS)
+scads:         $(addsuffix .auto.scad, $(AUTO_TOPLEVELS))
+stls:          $(addsuffix .auto.stl, $(AUTO_TOPLEVELS))
+
+%.auto.scads: %.scad
+       $(MAKE) $(addsuffix .auto.scad, $(shell $(PLAY)/toplevel-find $*))
+%.auto.stls:
+       $(MAKE) $(addsuffix .auto.stl, $(shell $(PLAY)/toplevel-find $*))
+
+-include .*.d
+
+%.stl:         %.scad $(AUTO_INCS)
+               openscad -d .$@.d.tmp -o $*.tmp.stl $<
+               @rm -f $@
+               @sed -e 's/\.tmp\.stl:/.stl:/' <.$@.d.tmp >.$@.d
+               @rm .$@.d.tmp
+               mv -f $*.tmp.stl $@
+
+AUTOBASE=$(shell echo $(1) | perl -pe 's/,\w+\.auto$$//')
+
+%:             %.cpp
+               cpp -nostdinc -P <$< >$@.tmp && mv -f $@.tmp $@
+
+funcs.scad:
+
+#%.gcode:      %.stl
+#              $(SKEINFORGE) $<
+
+%.gcode:       manual-gcode-generator %.m-g
+               $(PLAY)/$^ >$@.tmp && mv -f $@.tmp $@
+
+%.dxf:         %.eps
+               pstoedit -dt -f "dxf: -polyaslines -mm" $< $@
+
+%:             %.pl
+               ./$< >$@.tmp && mv -f $@.tmp $@
+
+%:             %.m4
+               $(M4) -P >$@.tmp $< && mv -f $@.tmp $@
+
+.PRECIOUS: %.auto.scad
+%.auto.scad: $(PLAY)/toplevel-make Makefile $(PLAY)/toplevel-find
+               @echo ' write $@'
+               $< $@ >$@.tmp
+               @mv -f $@.tmp $@
+
+.PRECIOUS:     %.stl %.gcode %.eps %.dxf
+
+clean:
+               rm -f *~ *.stl *.auto.scad *.gcode .*.d $(AUTO_INCS)
+
diff --git a/ring-tests.scad b/ring-tests.scad
new file mode 100644 (file)
index 0000000..c3bd34f
--- /dev/null
@@ -0,0 +1,43 @@
+// -*- C -*-
+
+thick = 3.5;
+width = 5;
+
+centre_dia = 21;
+
+// calculated
+
+ellipse_width = width / 0.90;
+
+module Profile(){
+  intersection(){
+    scale([thick, ellipse_width]) circle(r=0.5, $fn=50);
+    square([thick*2, width], center=true);
+  }
+}
+
+module Ring(dia){
+  rotate_extrude($fn=100){
+    translate([dia/2 + thick/2, 0]){
+      Profile();
+    }
+  }
+}
+
+//Profile();
+//Ring(centre_dia);
+
+module Tests(){
+  min_dia = 19;
+  max_dia = 22;
+  dia_step = .25;
+  n_dias = (max_dia - min_dia)/dia_step;
+  for (i= [ 0 : n_dias ]) {
+    echo(i);
+    rotate([0,0, i*360 / (n_dias+2)])
+      translate([80,0,0])
+      Ring(min_dia + (max_dia-min_dia)*(i/n_dias));
+  }
+}
+
+Tests();
diff --git a/rope-adjuster.scad b/rope-adjuster.scad
new file mode 100644 (file)
index 0000000..fa4f591
--- /dev/null
@@ -0,0 +1,77 @@
+// -*- C -*-
+
+include <utils.scad>
+
+hole_dia = 10;
+around_hole = 4;
+thick = 3;
+lever_len = 50;
+
+teeth_n = 4;
+teeth_bite = 4;
+teeth_pitch = 4;
+teeth_gap = 3;
+teeth_back = 1;
+teeth_height = 12;
+teeth_chamfer = 3;
+
+// calculated
+
+teeth_x_mid = lever_len/2 - hole_dia/2 - teeth_bite - teeth_gap*1.5;
+teeth_height_total = teeth_height + teeth_chamfer;
+
+module Circles(r) {
+  for (x = [-1,+1] * 0.5 * lever_len) {
+    translate([x, 0])
+      circle(r);
+  }
+}
+
+module Plan() {
+  difference(){
+    hull(){
+      Circles(hole_dia/2 + around_hole);
+    }
+    Circles(hole_dia/2);
+    translate([ teeth_x_mid - teeth_bite - teeth_back - teeth_gap - hole_dia/2,
+               0 ])
+      circle(hole_dia/2);
+  }
+}
+
+module TeethPlan(){
+  translate([
+            teeth_x_mid,
+            -0.5 * teeth_n * teeth_pitch,
+            ]) {
+    for (m=[0,1]) {
+      mirror([m,0]) {
+       translate([ teeth_bite + teeth_gap/2, 0 ])
+         rectfromto([ -0.1, 0 ],
+                    [ teeth_back, teeth_n * teeth_pitch ]);
+       for (i= [ 0: teeth_n-1 ]) {
+         translate([teeth_gap/2, teeth_pitch*i])
+           polygon([[ 0,0 ],
+                    [ teeth_bite, 0 ],
+                    [ teeth_bite, teeth_pitch ]]);
+       }
+      }
+    }
+  }
+}
+
+module Adjuster(){
+  linextr(0,thick)
+    Plan();
+  difference(){
+    linextr(thick - 0.1, thick + teeth_height_total)
+      TeethPlan();
+    translate([ teeth_x_mid, 0, thick + teeth_height_total + 1])
+      linextr_y_xz(-teeth_pitch * teeth_n,
+                +teeth_pitch * teeth_n)
+      rotate(45)
+      square(sqrt(2) * (teeth_gap/2 + teeth_chamfer + 1), center=true);
+  }
+}
+
+Adjuster();
diff --git a/rpi-mount.scad b/rpi-mount.scad
new file mode 100644 (file)
index 0000000..b1b9c12
--- /dev/null
@@ -0,0 +1,46 @@
+// -*- C -*-
+
+include <utils.scad>
+
+pi_board_gap = 0.5;
+pi_board_support_z = 2;
+pi_board_support_wall = 2;
+
+pi_sz   = [ 66.0, 30.5 ] + pi_board_gap * [1,1];
+
+pi_nom_sz = [ 65, 30 ];
+pi_nom_centres_in = 3.5;
+pi_solder_side_gap = 1.5 * 2;
+pi_screw_hole_dia  = 2.3;
+pi_screw_hole_wall = 2.3;
+
+pi_sz_z_incl_ribbon = 18.0;
+
+pi_mount_z_min = 1.75;
+pi_mount_z_def = 2.50;
+
+// calculated, output
+
+function pi_ribbon_top_z(pi_mount_z= pi_mount_z_def)
+  = pi_mount_z + pi_sz_z_incl_ribbon;
+
+module PiMount(pi_mount_z= pi_mount_z_def){
+  sxy = pi_nom_sz/2 - [1,1] * pi_nom_centres_in;
+  for (mx=[0,1]) mirror([mx,0,0]) for (my=[0,1]) mirror([0,my,0]) {
+    difference(){
+      union(){
+       linextr(-0.1, pi_mount_z + pi_board_support_z, convexity=1)
+         rectfromto( pi_nom_sz/2 - 2 * [1,1] * pi_nom_centres_in,
+                     pi_sz/2 + [1,1] * pi_board_support_wall);
+       linextr(-0.1, pi_mount_z - pi_solder_side_gap, convexity=1)
+         translate(sxy)
+         square(center=true, pi_screw_hole_dia + pi_screw_hole_wall*2);
+      }
+      linextr(pi_mount_z, pi_mount_z + 5, convexity=1)
+       rectfromto(-[10,10], pi_sz/2);
+      translate( sxy )
+       linextr(-1, pi_mount_z + 10, convexity=1)
+       circle(r= pi_screw_hole_dia/2);
+    }
+  }
+}
diff --git a/salter-scale-hook.scad b/salter-scale-hook.scad
new file mode 100644 (file)
index 0000000..3a8cfa3
--- /dev/null
@@ -0,0 +1,108 @@
+// -*- C -*-
+
+include <utils.scad>
+
+rod_dia = 8+2;
+thick = 8;
+screw_dia = 3.5 + 0.75;
+screw_head_dia = 8.2 + 1.0;
+rod_offset = 14 + 2;
+mainheight = 25;
+width = 40;
+rearthick = 4;
+screw_head_depth = 2;
+
+// calculated
+
+d = rod_dia/2 + thick/2;
+yminc = -d;
+ymin = yminc-thick/2;
+ymaxc = mainheight;
+ymax = mainheight+thick/2;
+
+cutdepth = rod_offset - rod_dia/2 - rearthick;
+
+cut_z0 = screw_head_dia/2;
+cut_z1 = width/2 - rearthick;
+
+cutslopez = cutdepth * 0.5;
+
+module C() {
+  circle(r = thick/2, $fn=30);
+}
+
+module Profile() {
+  e = rod_offset;
+  hull(){
+    translate([-d, 0]) C();
+    translate([-d,-d]) C();
+  }
+  difference(){
+    rectfromto([-d,ymin], [e,0]);
+    circle(r= rod_dia/2, $fn=50);
+  }
+  hull(){
+    for (y= [-d, +mainheight]) {
+      translate([d, y]) C();
+      rectfromto([d, y-thick/2], [e, y+thick/2]);
+    }
+  }
+}
+
+module CutProfile(){
+  hull(){
+    for (x = [rod_dia/2 + thick/2, 30]) {
+      for (y= [yminc,ymaxc] ) {
+       translate([x,y]) circle(r = (thick-rearthick)/2, $fn=20);
+      }
+    }
+  }
+}
+
+module ProfileDemo(){
+  Profile();
+  color("red") translate([0,0,1]) CutProfile();
+}
+
+module Cut(less){
+  translate([0,0, cut_z0 + less])
+    linear_extrude(height = cut_z1 - cut_z0 - less*2)
+    CutProfile();
+}
+
+module ScrewHole(){
+  xd =  (screw_head_dia-screw_dia)/2;
+  translate([0,0,-50])
+    cylinder(h=100, r= screw_dia/2, $fn=20);
+  hull(){
+    translate([0,0,-xd])
+      cylinder(h=1, r= screw_dia/2, $fn=50);
+    cylinder(h=20, r= screw_head_dia/2, $fn=50);
+  }
+}
+
+module Hook(){
+  difference(){
+    translate([0,0, -width/2])
+      linear_extrude(height=width) Profile();
+
+    for (m=[0,1]) {
+      mirror([0,0,m]) {
+       hull(){
+         Cut(cutslopez);
+         translate([cutdepth,0,0]) Cut(0);
+       }
+      }
+    }
+
+    translate([rod_dia/2 + screw_head_depth,
+              ymaxc - screw_head_dia,
+              0]) {
+      rotate([0,-90,0])
+       ScrewHole();
+    }
+  }
+}
+
+//ProfileDemo();
+Hook();
diff --git a/scaffold-clamp-cleat.scad b/scaffold-clamp-cleat.scad
new file mode 100644 (file)
index 0000000..69e8538
--- /dev/null
@@ -0,0 +1,13 @@
+// -*- C -*-
+
+// Per cleat print
+//     VCleatA        OR    VCleatA
+//     GeneralB             VCleatA
+//     Pin                  Pin
+//
+// These bits are compatible with scaffold-clamp-tensioner
+
+//// toplevels-from:
+include <scaffold-clamp-common.scad>
+
+module DemoA(){ VCleatARaw(); }
diff --git a/scaffold-clamp-common.scad b/scaffold-clamp-common.scad
new file mode 100644 (file)
index 0000000..79ed1da
--- /dev/null
@@ -0,0 +1,489 @@
+// -*- C -*-
+
+include <utils.scad>
+
+tube_dia = 48.3;
+
+th = 7;
+
+pin_gap = 1.5; // around
+
+smooth_r = 15;
+
+bolt_dia = 5;
+bolt_flat = 10 + 1;
+
+nbolts = 2;
+
+open_gap = 10;
+
+hinge_unit = 10;
+hinge_z_gap = 1;
+
+hinge_units = 4;
+
+bolt_gap = 1.0; // total, on both sides
+
+// ---------- vhook ----------
+
+vhook_th = 14;
+
+// ---------- cleat ----------
+
+cleat_frames = 10;
+cleat_curve_r = 200;
+cleat_horn_l = 40;
+cleat_horn_d_min = [10, 12];
+cleat_horn_d_max = [12, 14];
+cleat_height = 25;
+cleat_stem_l = 20;
+
+cleat_overlap = (1-cos(60));
+
+// ---------- hhook ----------
+
+hhook_inside = 40;
+hhook_th = 4;
+hhook_l = 40;
+
+// ---------- linear bracket ----------
+
+linear_bracket_h = 50;
+linear_bracket_l = 100;
+linear_bracket_t = 15;
+linear_bracket_hole_offset = 20;
+linear_bracket_hole_dia = 5 + 1.00;
+
+// ========== defaults ==========
+
+pin_head_th = th/2;
+pin_dia = th;
+pin_hole_dia = pin_dia/2;
+pin_tail = pin_hole_dia + pin_head_th + hinge_z_gap*3;
+
+// ========== calculated ==========
+
+TAU = PI*2;
+
+hole_dia = th + pin_gap;
+
+bolt_hole_r = (bolt_dia + bolt_gap)/2;
+
+main_r = tube_dia/2 + th;
+
+hinge_gap = pin_gap;
+
+hinge_o_r = 0.5 * hole_dia + th;
+
+hinge_x = -0.5 * tube_dia - hinge_o_r;
+bolt_x = 0.5 * tube_dia + th + bolt_flat * 0.5;
+max_x = bolt_x + max(bolt_hole_r + th, 0.5 * bolt_flat/2);
+
+flats_y = open_gap/2 + th;
+
+stride_z = hinge_unit*2 + hinge_z_gap*2;
+total_z = hinge_units * stride_z - hinge_z_gap;
+
+min_z = -total_z/2;
+max_z = +total_z/2;
+
+pin_flatten = pin_dia/2 * (1 - cos(45));
+
+bolt_stride = total_z / nbolts;
+
+// calculated - vhook
+
+vhook_inside = 15;
+
+vhook_theta = atan2( smooth_r, main_r );
+
+vhook_y0 = -max(main_r, (tube_dia/2 + vhook_th));
+vhook_ctr = vhook_y0 - vhook_inside/2;
+vhook_outer_dia = vhook_inside + vhook_th*2;
+
+// calculated - cleat
+
+cleat_horn_tl = cleat_horn_l + cleat_stem_l/2;
+
+vcleat_dz = max(0,
+               cleat_horn_tl
+               + cleat_horn_d_min[0]/2
+               - cleat_horn_d_min[0]/2 * cleat_overlap
+               - total_z/2
+               );
+
+// calculated - hhook
+
+hhook_outer_dia = hhook_inside + hhook_th*2;
+
+hhook_ctr = -max(main_r + hhook_inside/2,
+                tube_dia/2 + hhook_outer_dia/2);
+
+$fa = 3;
+$fs = 0.1;
+
+module SmoothPlan(){
+  offset(r=-smooth_r) offset(delta=smooth_r) children(0);
+}
+
+module TubePlan(){ circle(r = tube_dia/2); }
+module MainCirclePlan(){ circle(r = main_r); }
+
+module PlanWeldMainCircle(){
+  intersection(){
+    difference(){
+      SmoothPlan(){
+       union(){
+         MainCirclePlan();
+         children(0);
+       }
+      }
+      TubePlan();
+    }
+    rotate(-135) square(100);
+  }
+}
+
+module MainPlan(flatten=false) {
+  difference(){
+    SmoothPlan()
+      union(){
+      translate([hinge_x, 0]) circle(r= hinge_o_r);
+      MainCirclePlan();
+      rectfromto([0,           -flats_y],
+                [max_x,       +flats_y]);
+    }
+    TubePlan();
+    rectfromto([0,       -open_gap/2],
+              [max_x+1, +open_gap/2]);
+    translate([hinge_x, 0]) {
+      intersection(){
+       circle(r= hole_dia/2);
+       if (flatten)
+         translate([ pin_flatten, 0 ])
+         square(center=true, [hole_dia, hole_dia + 1]);
+      }
+    }
+  }
+}
+
+module Portion(d=0) {
+  translate([hinge_x, 0]) circle(r= hinge_o_r + d);
+  rectfromto([hinge_x*2, 0],
+            [max_x+10, -(tube_dia/2+th+10)]);
+}
+
+module MainPlanA(flatten){
+  intersection(){
+    MainPlan(flatten);
+    Portion(0);
+  }
+}
+
+module MainPlanB(flatten){
+  difference(){
+    MainPlan(flatten);
+    Portion(hinge_gap);
+  }
+}
+
+module HalfClampXPositive(flatten=false){
+  translate([0,0, min_z]) {
+    linextr(0, total_z) mirror([0,1]) MainPlanB();
+    for (i=[0 : hinge_units-1]) {
+      translate([0,0, stride_z*i])
+       linextr(0, hinge_unit) MainPlanA(flatten);
+    }
+  }
+}
+
+module HalfClampXNegative(){
+  for (j=[0:nbolts-1]) {
+    translate([ bolt_x, 0, min_z + (j + 0.5) * bolt_stride ]) {
+      translate([0, -tube_dia/2, 0])
+       rotate([-90,0,0])
+       cylinder(r= bolt_hole_r, h= tube_dia);
+      translate([0, -flats_y, 0])
+       rotate([90,0,0])
+       cylinder(r= bolt_flat/2, h= tube_dia/2);
+    }
+  }
+}
+
+module HalfClampX(flatten=false){
+  difference(){
+    HalfClampXPositive(flatten);
+    HalfClampXNegative();
+  }
+}
+
+// ---------- vhook ----------
+
+module VHookProfile() {
+  translate([0, -vhook_inside/2 - vhook_th/2])
+    circle(r = vhook_th/2);
+}
+
+module VHookHookMain(outer=false){ ////toplevel
+  rotate([0,90,0])
+    rotate_extrude(convexity=10)
+    rotate([0,0,90])
+    hull(){
+      VHookProfile();
+      if (outer) {
+       translate([0,-vhook_outer_dia]) square(center=true, vhook_th);
+      }
+    }
+}
+
+module VHookA(){ ////toplevel
+  DummyA();
+
+  translate([0, vhook_ctr, 0]){
+    for (m=[0,1]) {
+      mirror([0, m, 0]) {
+       linextr(-0.1, vhook_outer_dia/2)
+         VHookProfile();
+       translate([0, -vhook_inside/2 -vhook_th/2, vhook_outer_dia/2])
+         sphere(r= vhook_th/2);
+      }
+    }
+
+    intersection(){
+      VHookHookMain(outer=true);
+      linextr_y_xz(0, vhook_outer_dia/2) hull(){
+       VHookProfile();
+       translate([0,-0.1]) square(center=true, [vhook_th, 0.2]);
+      }
+    }
+
+    intersection(){
+      VHookHookMain();
+      translate([0,0, -vhook_outer_dia])
+       cube(center=true, vhook_outer_dia*2);
+    }
+  }
+
+  //translate([0, vhook_y0, 50]) rotate([0,0,-90]) color("black") cube(10);
+  // translate([0,0,-150]) rotate([0,0,180 + theta]) color("blue") cube(100);
+}
+
+module VHookPlanDemo(){
+  MainPlanA();
+  translate([0, vhook_ctr, 5])
+    for (m=[0,1]) {
+      mirror([0,m])
+       color("blue") VHookProfile();
+    }
+}
+
+// ---------- cleat ----------
+
+function cleat_frame_theta(s) = s * cleat_horn_tl / cleat_curve_r * 360/TAU;
+function cleat_frame_z(s) = cleat_curve_r * (1 - cos(cleat_frame_theta(s)));
+function cleat_frame_x(s) = cleat_curve_r * sin(cleat_frame_theta(s));
+function cleat_frame_r(s) = ( cleat_horn_d_min * s +
+                             cleat_horn_d_max * (1-s) ) * 0.5;
+
+module CleatFrameSphere(r) {
+  scale([1, r[1]/r[0], 1])
+    sphere(r= r[0]);
+}
+
+module CleatFrame(s) {
+  r = cleat_frame_r(s);
+  translate([cleat_frame_x(s), 0, cleat_frame_z(s)])
+    rotate([0, 90, 0])
+    CleatFrameSphere(r);
+}
+
+
+module CleatHorn(){
+  for (si=[0 : cleat_frames-2]) {
+    s0 = si / (cleat_frames-1);
+    s1 = (si+1) / (cleat_frames-1);
+    hull(){
+      CleatFrame(s0);
+      CleatFrame(s1);
+    }
+  }
+}
+
+module CleatBase(){
+  frames = cleat_frames/2;
+  se = cleat_stem_l/2 / cleat_horn_tl;
+  r = cleat_frame_r(se);
+
+  hull(){
+    for (s = [-se, se]) {
+      for (z= [-(cleat_height + tube_dia/2),
+              cleat_frame_z(s)]) {
+       translate([cleat_frame_x(s), 0, z])
+         linear_extrude(height=0.1)
+         scale([1, r[1]/r[0]])
+         circle(r=r[0]);
+      }
+    }
+  }
+}
+
+module VCleat(){
+  intersection(){
+    translate([0,0, vcleat_dz]){
+      difference(){
+       translate([0, -(main_r + cleat_height), 0]) {
+         rotate([0, -90, 90]) {
+           CleatBase();
+           for (m=[0,1]) {
+             mirror([m,0,0]) {
+               CleatHorn();
+             }
+           }
+         }
+       }
+       linextr(-cleat_stem_l, +cleat_stem_l)
+         circle(r = tube_dia/2 + 0.1);
+      }
+    }
+      translate([0,0, total_z * 0.5])
+       cube(center=true,
+            (main_r + cleat_stem_l)*4 * [1,1,0] +
+            total_z * [0,0,2]);
+  }
+}
+
+module VCleatA(){ ////toplevel
+  DummyA();
+  VCleat();
+}
+
+// ---------- hhook ----------
+
+module HHookHookPlan(){
+  translate([0, hhook_ctr]){
+    difference(){
+      circle(r = hhook_outer_dia/2);
+      circle(r = hhook_inside/2);
+      rectfromto([+hhook_outer_dia, -hhook_outer_dia],
+                [0,                +hhook_outer_dia]);
+    }
+    translate([0, -(hhook_inside/2 + hhook_th/2)]){
+      hull(){
+       for (x=[-0.1, hhook_l]) {
+         translate([x,0]) square(center=true, hhook_th);
+       }
+      }
+    }
+  }
+}
+
+module HHookA(){ ////toplevel
+  DummyA();
+  linextr(min_z, max_z) {
+    HHookHookPlan();
+  }
+}
+
+module HHookPlanDemo(){
+  MainPlanA();
+  HHookHookPlan();
+}
+
+// ---------- linear bracket ----------
+
+module LinearBracketA(){ ////toplevel
+  difference(){
+    union(){
+      HalfClampXPositive();
+      mirror([1,0,0])
+      linextr_y_xz(-open_gap/2 - linear_bracket_t, -open_gap/2)
+       rectfromto([0, min_z],
+                  [max_x + linear_bracket_l, min_z + linear_bracket_h]);
+    }
+    HalfClampXNegative();
+    linextr(-1000,1000)
+      TubePlan();
+    mirror([1,0,0])
+      linextr_y_xz(-100,100) {
+      for (t = [
+               [1,1] * linear_bracket_hole_offset,
+               -[1,1] * linear_bracket_hole_offset +
+               [linear_bracket_l, linear_bracket_h]
+               ]) {
+       translate([ max_x, min_z ] + t)
+         circle(r= linear_bracket_hole_dia/2);
+      }
+    }
+  }
+}
+
+// ---------- misc ----------
+
+module PinSitu(){ ////toplevel
+  difference(){
+    union(){
+      translate([0,0, -pin_head_th])
+       cylinder(r= pin_dia/2, h = total_z + pin_head_th + pin_tail);
+      mirror([0,0,1])
+       cylinder(r= hinge_o_r - pin_gap, h = pin_head_th);
+    }
+    translate([0,0, total_z + pin_tail/2])
+      rotate([0,90,0])
+      translate([0,0, -pin_dia])
+      cylinder(r= pin_hole_dia/2, h=pin_dia*2);
+    translate([pin_dia/2 * cos(45), -50, -pin_head_th*2])
+      cube([50,100, total_z*2]);
+  }
+}
+
+module Pin(){ ////toplevel
+  rotate([0,90,0]) {
+    PinSitu();
+  }
+}
+
+module GeneralB(){ ////toplevel
+  HalfClampX(true);
+}
+
+module DummyA(){ ////toplevel
+  HalfClampX();
+}
+
+module PlanDemo(){ ////toplevel
+  MainPlan();
+  translate([0,0,-4]) color("red") Portion(1);
+  translate([0,0,-2]) color("grey") Portion(0);
+
+  translate([0, tube_dia*1.5]) {
+    MainPlanB();
+    MainPlanA();
+  }
+
+  translate([0, -tube_dia*1.5]) {
+    VHookPlanDemo();
+  }
+  translate([tube_dia*4, 0]) {
+    HHookPlanDemo();
+  }
+//  translate([max_x - hinge_x + 20, 0]) color("blue") MainPlanA();
+}
+
+module DemoA(){ DummyA(); }
+
+module Demo(){ ////toplevel
+  color("red") rotate([180,0,0]) GeneralB();
+  color("blue") DemoA();
+  color("orange") translate([hinge_x, 0, min_z - hinge_z_gap])
+    rotate([0,0,180]) PinSitu();
+}
+
+module DemoPair(){ ////toplevel
+  color("red") rotate([180,0,0]) DemoA();
+  color("blue") DemoA();
+  color("orange") translate([hinge_x, 0, min_z - hinge_z_gap])
+    rotate([0,0,180]) PinSitu();
+}
+
+//PlanDemo();
+//HalfClamp();
diff --git a/scaffold-clamp-linear-bracket.scad b/scaffold-clamp-linear-bracket.scad
new file mode 100644 (file)
index 0000000..a5fbe24
--- /dev/null
@@ -0,0 +1,11 @@
+// -*- C -*-
+
+// Per linear bracket print
+//     LinearBracketA
+//     GeneralB
+//     Pin
+
+//// toplevels-from:
+include <scaffold-clamp-common.scad>
+
+module DemoA(){ LinearBracketA(); }
diff --git a/scaffold-clamp-straphook.scad b/scaffold-clamp-straphook.scad
new file mode 100644 (file)
index 0000000..1804be9
--- /dev/null
@@ -0,0 +1,22 @@
+// -*- C -*-
+
+// Per gym ring strap retainer print
+//     HHookA
+//     GeneralB
+//     Pin
+
+//// toplevels-from:
+include <scaffold-clamp-common.scad>
+
+th = 3;
+hhook_th = 3;
+hinge_units = 2;
+nbolts = 1;
+hinge_unit = 5;
+bolt_dia = 3;
+bolt_flat = 7 + 1;
+
+hhook_inside = 35;
+hhook_l = 50;
+
+module DemoA(){ HHookA(); }
diff --git a/scaffold-clamp-tensioner.scad b/scaffold-clamp-tensioner.scad
new file mode 100644 (file)
index 0000000..6207dfb
--- /dev/null
@@ -0,0 +1,11 @@
+// -*- C -*-
+
+// Per tensioner print
+//     VHookA
+//     GeneralB
+//     Pin
+
+//// toplevels-from:
+include <scaffold-clamp-common.scad>
+
+module DemoA(){ VHookA(); }
diff --git a/screw-recess-test-number.fig.pl b/screw-recess-test-number.fig.pl
new file mode 100755 (executable)
index 0000000..023b315
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/perl
+
+my $number = shift @ARGV;
+die unless $number =~ m/^\d+$/;
+
+my $fontsz = $number * 6 + 12;
+
+print <<END or die $!;
+#FIG 3.2  Produced by xfig version 3.2.5b
+Landscape
+Center
+Metric
+A4      
+100.00
+Single
+-2
+1200 2
+4 0 0 50 -1 18 $fontsz 0.0000 4 285 225 3600 4500 $number\001
+END
diff --git a/screw-recess-test.scad b/screw-recess-test.scad
new file mode 100644 (file)
index 0000000..08fdf20
--- /dev/null
@@ -0,0 +1,125 @@
+// -*- C -*-
+
+module RecessScrewCutout_RecessCylinder(recessdia,zbelow, h){
+  translate([0,0,-zbelow]) cylinder(r=recessdia/2, h=h+1, $fn=40);
+}
+
+RecessedScrewCutout_defaultrecessdepth_flat = -0.30;
+RecessedScrewCutout_defaultrecessdepth_hex = -0.60;
+
+function RecessedScrewCutout_recessdepth(recessdia,
+        recessdepth_arg=RecessedScrewCutout_defaultrecessdepth_flat) =
+  recessdepth_arg >= 0 ? recessdepth_arg : -recessdepth_arg * recessdia;
+
+function RecessedScrewCutout_totaldepth(recessdia,
+        recessdepth_arg=RecessedScrewCutout_defaultrecessdepth_flat) =
+  RecessedScrewCutout_recessdepth(recessdia, recessdepth_arg) +
+  + 0.5*recessdia + 0.1;
+
+module RecessedScrewCutout(shaftdia, recessdia, shaftlen,
+        zbelow=1,
+        recessdepth_arg=RecessedScrewCutout_defaultrecessdepth_flat) {
+  // pass recessdepth_arg=-1 for the default for flat heads
+  // pass recessdepth_arg=-1 for the default for flat heads
+  recessdepth = RecessedScrewCutout_recessdepth(recessdia, recessdepth_arg);
+  recesstopz = RecessedScrewCutout_totaldepth(recessdia, recessdepth_arg);
+
+  translate([0,0,-zbelow]) cylinder(r=shaftdia/2, h=shaftlen+zbelow, $fn=20);
+  RecessScrewCutout_RecessCylinder(recessdia,zbelow, recessdepth);
+  intersection(){
+    cube([recessdia + 1, shaftdia + 0.1, recesstopz*2 + 1], center=true);
+    translate([0, -recessdia, recesstopz])
+      rotate([0,135,0]) cube([recessdia, recessdia*2, 10]);
+    RecessScrewCutout_RecessCylinder(recessdia,zbelow, recesstopz+1);
+  }
+}
+
+//              nom.   shaft
+//              shaft   slop
+screw_info_M2   = [2,   1.2];
+screw_info_M3   = [3,   1.2];
+screw_info_M4   = [4,   1.1];
+screw_info_M5   = [5,   1.0];
+screw_info_M6   = [6,   1.2];
+
+function screw_shaft_dia_nom(info)      = info[0];
+function screw_shaft_dia_use(info)      = info[0] + info[1];
+function screw_recess_dia_use(info)     = info[0] * 2.50 + 1.0;
+function screw_recess_depth(info)       = info[0] * 1.00 + 0.50;
+function screw_recess_depth_allen(info) = info[0] * 1.55 + 0.50;
+
+function RecessedScrewCutoutStandard_totaldepth(info) =
+  RecessedScrewCutout_totaldepth(screw_recess_dia_use(info),
+                                screw_recess_depth(info));
+
+function RecessedScrewCutoutStandardAllen_totaldepth(info) =
+  RecessedScrewCutout_totaldepth(screw_recess_dia_use(info),
+                                screw_recess_depth_allen(info));
+
+module RecessedScrewCutoutStandard(info, shaftlen, zbelow=1) {
+  RecessedScrewCutout(screw_shaft_dia_use(info),
+                     screw_recess_dia_use(info),
+                     shaftlen, zbelow,
+                     screw_recess_depth(info));
+}                               
+
+module RecessedScrewCutoutStandardAllen(info, shaftlen, zbelow=1) {
+  RecessedScrewCutout(screw_shaft_dia_use(info),
+                     screw_recess_dia_use(info),
+                     shaftlen, zbelow,
+                     screw_recess_depth_allen(info));
+}                               
+
+tests = [
+        screw_info_M2,
+        screw_info_M3,
+        screw_info_M4,
+2       screw_info_M5,
+        screw_info_M6
+        ];
+
+function Test_blocksz(t) = screw_recess_dia_use(t) + 7;
+
+module OneTestCore(t, h, ymul, labelnumber=false){
+  blocksz = Test_blocksz(t);
+  translate([0, ymul * (blocksz*0.5 - 1.5), 0]) {
+    difference(){
+      translate([-blocksz/2, -blocksz/2, 0])
+       cube([blocksz, blocksz, h]);
+      child();
+    }
+    if (labelnumber) {
+      rotate([90,0,0])
+       translate([-blocksz/4,blocksz/5, blocksz/2-1])
+       linear_extrude(height=0.3+1)
+      import(file=str("screw-recess-test-number-s",t[0],".dxf"), convexity=100);
+    }
+  }
+}
+
+module OneTest(t){
+  h = RecessedScrewCutoutStandard_totaldepth(t) + 3;
+  ha = RecessedScrewCutoutStandardAllen_totaldepth(t) + 3;
+  OneTestCore(t, h, 1){
+    RecessedScrewCutoutStandard(t, h+1);
+  }
+  OneTestCore(t, ha, -1, true){
+    RecessedScrewCutoutStandardAllen(t, ha+1);
+  }
+}
+
+function Test_x(i) = i<=0 ? 0 :
+  Test_x(i-1) + Test_blocksz(tests[i-1])/2 + Test_blocksz(tests[i])/2 - 3;
+
+module Tests(){
+  for (i = [0:len(tests)-1]) {
+    echo(i, Test_x(i));
+    translate([Test_x(i), 0, 0])
+      OneTest(tests[i]);
+  }
+}
+
+//OneTest(tests[1]);
+Tests();
+//Hole();
+//Holes();
diff --git a/sealing-box.scad.m4 b/sealing-box.scad.m4
new file mode 100644 (file)
index 0000000..dbfb220
--- /dev/null
@@ -0,0 +1,180 @@
+// -*- C -*-
+
+// This file can be used in two ways:
+//
+// A. Rectangular boxes
+//      1. include <sealing-box.scad>
+//      2. assign() values to (xxx these should be $ variables)
+//           $sealingbox_wallth
+//           $sealingbox_sz[0] (outer dimension)
+//           $sealingbox_sz[1] (outer dimension)
+//           $sealingbox_sz[2] (inner dimension)
+//           $sealingbox_ceilth
+//           $sealingbox_floorth
+//           $sealingbox_wallth
+//      3. use the modules
+//           SealingBox_RectBox
+//           SealingBox_RectLid
+//           (origin is notional outside corner, but at level of
+//            inside of base; box extends to positive x,y,z)
+//
+// B. Complicated shapes, but harder work
+//      1. Be a .m4 file and m4_include sealing-box.scad.m4
+//      2. Define your own BoxDoShapeSomething like BoxDoShapeRect
+//      3. Invoke BoxUseShape
+//      4. Use the Box and Lid modules generated
+//
+// Other settings
+//  $sealingbox_cnrrad
+
+$sealingbox_cnrrad = 10;
+$sealingbox_crude = false;
+$sealingbox_inner_slop = 0.2;
+
+m4_define(`BoxLocals',`
+  xbox = $sealingbox_sz[0];
+  ybox = $sealingbox_sz[1];
+  zbox = $sealingbox_sz[2];
+  wall = $sealingbox_wallth;
+  floorth = $sealingbox_floorth;
+  ceilth = $sealingbox_ceilth;
+  cnrrad = $sealingbox_cnrrad;
+
+  xbox_lin = xbox - cnrrad*2;
+  ybox_lin = ybox - cnrrad*2;
+')
+
+m4_define(`innertube', `(1.0 + 0.2)')
+m4_define(`lidoverlap', `1.5')
+m4_define(`lidoverhang', `6')
+m4_define(`tubesealrad', `2.0')
+
+m4_define(`BoxFn',`$fn= $sealingbox_crude ? ($2) : ($1)')
+
+m4_dnl Box_Part($1=transl_x,$2=transl_y, $3=rot_z,$4=mirror_xy)
+m4_dnl          $5=kind, $6=kindargs, $7=profile(profileargsargs))
+m4_define(`Box_Part',`
+  translate([($1),($2)])
+    rotate([0,0,($3)])
+    mirror([($4),0,0])
+    BoxPart_Extrude_$5($6, $7)') m4_dnl
+
+boxpart_d = 0.01;
+
+m4_dnl BoxPart_Extrude_Linear(dist, `profile(...);');
+m4_define(`BoxPart_Extrude_Linear',`
+  rotate([90,0,0])
+    translate([0,0, -($1)])
+    linear_extrude(height= boxpart_d + ($1)) {
+      $2
+    }
+')
+
+m4_dnl BoxPart_Extrude_Arc(x0_radius, swept_angle, `profile(...);')
+m4_dnl  arc starting at transl_x, transl_y, moving towards positive
+m4_dnl  y at first and then bending towards negative x, until
+m4_dnl  use negative x0_radius to inciate bending towards positive x
+m4_dnl  swept_angle is reached
+m4_dnl  x0_radius is the radius of the extruded part at x=0, not of the box
+m4_define(`BoxPart_Extrude_Arc',`
+  translate([-($1),0,0])
+    intersection(){
+      translate([0,0,-500])
+        linear_extrude(height=1000)
+        scale(500)
+        mirror([($1)<0, 0,0])
+        FArcSegment_mask($2);
+      rotate_extrude(convexity=10, $fs=1, BoxFn(36,8))
+        mirror([($1)<0, 0,0])
+        translate([+($1),0,0]){
+          $3
+        }
+    }
+')
+
+m4_dnl BoxDoShapeRect(`profile(profileargs)');
+m4_define(`BoxDoShapeRect',`
+  Box_Part(0,           cnrrad,         0,0, Linear,`ybox_lin', `$1' )
+  Box_Part(0,           ybox-cnrrad,    0,0, Arc,`-cnrrad,90' , `$1' )
+  Box_Part(cnrrad,      ybox,         -90,0, Linear,`xbox_lin', `$1' )
+  Box_Part(xbox-cnrrad, ybox,         -90,0, Arc,`-cnrrad,90' , `$1' )
+  Box_Part(xbox,        ybox-cnrrad, -180,0, Linear,`ybox_lin', `$1' )
+  Box_Part(xbox,        cnrrad,      -180,0, Arc,`-cnrrad,90' , `$1' )
+  Box_Part(xbox-cnrrad, 0,           -270,0, Linear,`xbox_lin', `$1' )
+  Box_Part(cnrrad,      0,           -270,0, Arc,`-cnrrad,90' , `$1' )
+')
+
+m4_dnl '
+
+module SealingBox_WallProfile(){
+  BoxLocals
+  z = zbox - innertube - tubesealrad;
+  translate([0, -0.1]) square([wall, z]);
+  translate([tubesealrad, z]) circle(r=tubesealrad, BoxFn(20,6));
+}
+
+module SealingBox_FloorProfile(){
+  BoxLocals
+  mirror([0,1]) square([wall, floorth]);
+}
+
+function SealingBox_lidbigger() = lidoverlap + innertube;
+
+module SealingBox_LidProfile(){
+  BoxLocals
+  rad = tubesealrad + innertube;
+  morex = wall;
+  inner_buttress_h = tubesealrad*1.5 + innertube + ceilth;
+
+  difference(){
+    translate([0, zbox + ceilth]) mirror([0,1]) {
+      translate([-SealingBox_lidbigger(),
+                0])
+      square([lidoverlap + innertube + tubesealrad,
+             lidoverhang + innertube + ceilth]);
+      square([tubesealrad*2 + innertube + lidoverlap,
+             inner_buttress_h]);
+    }
+    hull(){
+      translate([tubesealrad,
+                zbox - innertube - tubesealrad])
+       for (t=[ [0,0],
+                [0, -zbox]
+                ]) {
+         translate(t)
+           circle(r= tubesealrad + innertube, BoxFn(20,6));
+       }
+    }
+  }
+  translate([tubesealrad*2 + $sealingbox_inner_slop,
+            zbox + ceilth]) {
+    mirror([0,1]) {
+      square([lidoverlap + innertube,
+             inner_buttress_h]);
+    }
+  }
+}
+
+module SealingBox_CeilProfile(){
+  BoxLocals
+  translate([0, zbox])
+    square([wall*2, ceilth]);
+}
+
+// BoxDoShape(Basename,BoxDoShapeSomething)
+// generates modules BasenameBox and BasenameLid
+m4_define(`BoxUseShape',`
+  module $1Box(){
+    BoxLocals
+    $2(SealingBox_WallProfile(););
+    hull(){ $2(SealingBox_FloorProfile();); }
+  }
+
+  module $1Lid(){
+    BoxLocals
+    $2(SealingBox_LidProfile(););
+    hull(){ $2(SealingBox_CeilProfile();); }
+  }
+')
+
+BoxUseShape(`SealingBox_Rect',`BoxDoShapeRect')
diff --git a/secateurs-clip.scad b/secateurs-clip.scad
new file mode 100644 (file)
index 0000000..bdb008d
--- /dev/null
@@ -0,0 +1,22 @@
+// -*- C -*-
+
+l = 47;
+d = 18;
+
+w = 15;
+t = 10;
+te = 7;
+
+linear_extrude(height=15, convexity=4) {
+  for (m=[0,1]) {
+    mirror([m,0]) {
+      polygon([[ -1,      0 ],
+              [ -1,     -t ],
+              [ l/2+te, -t ],
+              [ l/2+te,  d ],
+              [ l/2,     d ],
+              [ l/2,     0 ],
+              ]);
+    }
+  }
+}
diff --git a/sewing-table-end-profile-photo.jpg b/sewing-table-end-profile-photo.jpg
new file mode 100644 (file)
index 0000000..73f3a20
Binary files /dev/null and b/sewing-table-end-profile-photo.jpg differ
diff --git a/sewing-table-end-profile.fig b/sewing-table-end-profile.fig
new file mode 100644 (file)
index 0000000..2250e94
--- /dev/null
@@ -0,0 +1,29 @@
+#FIG 3.2  Produced by xfig version 3.2.6a
+Landscape
+Center
+Metric
+A4
+100.00
+Single
+-2
+1200 2
+2 5 0 1 0 -1 60 -1 -1 0.000 0 0 -1 0 0 5
+       0 sewing-table-end-profile-photo.jpg
+        2655 6615 23743 6615 23743 12787 2655 12787 2655 6615
+2 1 0 1 1 7 50 -1 -1 0.000 0 0 -1 0 0 2
+        3780 10170 22635 9990
+2 1 0 2 9 7 50 -1 -1 0.000 0 0 -1 0 0 2
+        22671 9881 22587 6522
+2 1 0 3 14 7 50 -1 -1 0.000 0 0 -1 0 0 2
+        3780 8137 22635 7957
+2 1 0 2 9 7 50 -1 -1 0.000 0 0 -1 0 0 2
+        4090 9278 4034 6610
+2 2 0 2 28 7 50 -1 -1 0.000 0 0 -1 0 0 5
+        1818 8008 5248 8008 5248 14648 1818 14648 1818 8008
+3 1 0 1 6 7 40 -1 -1 0.000 0 0 0 17
+        8544 8080 12341 8071 16290 7965 19845 7830 22214 7727 22560 7582
+        22605 7370 22539 6869 22548 6611 12555 4725 4252 5873 4101 6534
+        4052 7107 4047 7674 4058 7812 4323 7950 4864 8005
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000
diff --git a/sewing-table-front-profile-photo.jpg b/sewing-table-front-profile-photo.jpg
new file mode 100644 (file)
index 0000000..d0dd7f1
Binary files /dev/null and b/sewing-table-front-profile-photo.jpg differ
diff --git a/sewing-table-front-profile.fig b/sewing-table-front-profile.fig
new file mode 100644 (file)
index 0000000..d318174
--- /dev/null
@@ -0,0 +1,26 @@
+#FIG 3.2  Produced by xfig version 3.2.6a
+Landscape
+Center
+Metric
+A4
+100.00
+Single
+-2
+1200 2
+2 5 0 1 0 -1 60 -1 -1 0.000 0 0 -1 0 0 5
+       0 sewing-table-front-profile-photo.jpg
+        2250 2115 14794 2115 14794 8172 2250 8172 2250 2115
+2 2 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 5
+        5311 3627 1892 3627 1892 8634 5311 8634 5311 3627
+2 1 0 1 1 7 50 -1 -1 0.000 0 0 -1 0 0 2
+        3455 7525 12541 7946
+3 1 0 1 28 7 40 -1 -1 0.000 0 0 0 25
+        5678 3664 4889 3652 4306 3643 3820 3620 3256 3600 2800 3591
+        2416 3574 3055 1515 11023 1659 11041 2606 10905 2752 10663 2946
+        10504 3060 10304 3176 10113 3305 9859 3397 9581 3493 9306 3565
+        8734 3613 8367 3618 7996 3636 7520 3639 7119 3651 6713 3663
+        6299 3670
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000
diff --git a/sewing-table-jig.scad b/sewing-table-jig.scad
new file mode 100644 (file)
index 0000000..9302119
--- /dev/null
@@ -0,0 +1,21 @@
+//// toplevels-from:sewing-table.scad
+include <sewing-table.scad>
+
+JIG = true;
+
+tile_th=0.8;
+interlock_dia=5;
+
+jig_pencil_rad = 1;
+jig_pencil_slotlen = 10;
+jig_min_th = 0.3;
+jig_post_hole_slop = 0.5;
+
+test_tile_th = -0.1;
+
+test_edge = interlock_dia * 0.5 + interlock_fine + 2;
+round_edge_rad = tile_th/2;
+frontcurve_z_slop = 15;
+rearcurve_z_slop = 20;
+
+interlock_fine = tile_th/8;
diff --git a/sewing-table-rear-profile-photo.jpg b/sewing-table-rear-profile-photo.jpg
new file mode 100644 (file)
index 0000000..34bf64d
Binary files /dev/null and b/sewing-table-rear-profile-photo.jpg differ
diff --git a/sewing-table-rear-profile.fig b/sewing-table-rear-profile.fig
new file mode 100644 (file)
index 0000000..3a6af71
--- /dev/null
@@ -0,0 +1,28 @@
+#FIG 3.2  Produced by xfig version 3.2.6a
+Landscape
+Center
+Metric
+A4
+100.00
+Single
+-2
+1200 2
+2 2 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 5
+        3255 -6357 -7303 -6357 -7303 18013 3255 18013 3255 -6357
+2 1 0 1 1 7 50 -1 -1 0.000 0 0 -1 0 0 2
+        -2808 6102 -1101 12715
+2 5 0 1 0 -1 60 -1 -1 0.000 0 0 -1 0 0 5
+       0 sewing-table-rear-profile-photo.jpg
+        11014 -9675 -5445 -9675 -5445 16370 11014 16370 11014 -9675
+2 1 0 1 14 7 50 -1 -1 0.000 0 0 -1 0 0 2
+        -536 -9216 -5067 1826
+3 1 0 1 28 7 40 -1 -1 0.000 0 0 0 26
+        3330 -6390 1755 -5895 495 -5310 -630 -4590 -1440 -3825 -2115 -2790
+        -2745 -1710 -3240 -450 -3555 990 -3555 2430 -3420 3780 -2970 5625
+        -2610 6930 -2205 8415 -1890 9630 -1485 11160 -1125 12690 -855 14085
+        -90 16020 14715 9630 9855 -6975 9270 -8550 8685 -8010 7200 -7560
+        5985 -7200 4365 -6660
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000
diff --git a/sewing-table-test.scad b/sewing-table-test.scad
new file mode 100644 (file)
index 0000000..14b83b9
--- /dev/null
@@ -0,0 +1,15 @@
+//// toplevels-from:sewing-table.scad
+include <sewing-table.scad>
+TEST = true;
+test_tile_th = 0.67;
+test_edge = interlock_dia * 0.5 + interlock_fine + 2;
+
+leg_n_fins = 2;
+
+leg_top_thick = 3;
+leg_bot_thick = 4;
+leg_top_flat_z = 0.5;
+leg_fin_top_w = 2;
+leg_fin_bot_w = 3;
+leg_fin_bot_rad = 15;
+leg_bot_mid_dia = 9;
diff --git a/sewing-table.scad.m4 b/sewing-table.scad.m4
new file mode 100644 (file)
index 0000000..b71e041
--- /dev/null
@@ -0,0 +1,1193 @@
+// -*- C -*-
+
+include <funcs.scad>
+include <commitid.scad>
+
+ply_th = 18;
+ply_hole_dia = 15;
+ply_edge_min = 10;
+
+ply_hole_dia_real = 12;
+
+tile_th = 3;
+post_dia = 8;
+
+post_shorter = 1;
+
+$screw_dia = 3.2;
+screw_big_dia = 3.6;
+screw_big_len = 4.0;
+
+round_edge_rad = 2.0;
+
+round_cnr_rad = 10;
+
+interlock_dia = 10;
+interlock_fine = 0.66;
+
+interlock_fine_slope = 1.0;
+interlock_fine_lenslop = 1.0;
+
+demo_slop = 0.1;
+
+leg_height = 53.75 - 0.95;
+
+leg_hole_dia = 5 + 0.75;
+leg_big_dia = 37;
+leg_bot_dia = 15;
+leg_top_flat_z = 2;
+leg_top_thick = 8;
+
+leg_midspc_dia = 20;
+leg_bot_thick = 8;
+leg_bot_mid_dia = 12;
+
+leg_fin_top_w = 3;
+leg_fin_bot_w = 5;
+leg_fin_bot_rad = 30;
+leg_fin_bot_flat_z = 5;
+
+leg_n_fins = 4;
+leg_n_tubules = 4;
+leg_tubule_dia = 4;
+
+// spacer
+
+spacer_ext_slop = 0.25;
+spacer_int_slop = 0.25;
+spacer_height = 10;
+
+// cutout
+
+machine_rear_to_front = 84 + 0.25 - 1.4;
+
+cutout_l_end_y_front_slop = 0.5;
+cutout_l_end_y_rear_slop = 0.5;
+
+cutout_l_end_x = 22;
+cutout_l_end_y = machine_rear_to_front;
+cutout_l_end_new_x_slop = 1.4 - 1.95;
+cutout_l_end_y_total = cutout_l_end_y
+  + cutout_l_end_y_front_slop + cutout_l_end_y_rear_slop;
+
+tile02_tr = [-250, 0];
+tile01_tr = [  0, 0];
+
+cutout_tile01_y = 170 - 147 + cutout_l_end_y_front_slop;
+cutout_tile11_y = cutout_l_end_y_total - cutout_tile01_y;
+
+// front and rear curves
+
+rearedge_len = 170 + 0.70;
+frontedge_len = 170;
+
+rearcurve_strt_len = 52;
+
+rearcurve_z_slop = -0.50;
+
+rearcurve_avoid_y = 35;
+
+rearcurve_double_inrad = 26.10 + 8.04;
+
+reartablet_z = 2.54;
+reartablet_x = 5 + 1;
+reartablet_y = 8;
+
+frontcurve_side_skew = 3.5 / 72;
+frontcurve_avoid_y = 70;
+frontcurve_z_slop = 0.75;
+
+frontcurve_strt_len = 50;
+frontcurve_dualcurve_angle = 30;
+
+teststrapslots_at = [ [ 110, 70 ], [ 110, -35 ],
+                     [ 180, 90 ],
+                     [ 190, -80 ], // do not change index of this one
+                     [   0, 70 ],  [  0, -35 ],
+                     ];
+
+teststrap = [ 3, 5 ];
+teststrap_peg = [7.5, 3.5];
+
+ply_edge_hole_dist_real = 14;
+
+// calculated
+
+TEST = false;
+JIG = false;
+
+ply_edge_hole_dist = ply_edge_min + ply_hole_dia/2;
+
+echo(str("HOLES IN PLY ctr dist from PLY edge = ", ply_edge_hole_dist));
+
+hole_slop = (ply_hole_dia - post_dia)/2;
+tile_hard_edge_hole_dist = ply_edge_hole_dist + hole_slop;
+
+echo(str("HOLES IN PLY ctr dist from TILE HARD edge = ",
+        tile_hard_edge_hole_dist));
+
+echo(str("HOLES IN PLY ctr dist from TILE ROUND edge = ",
+        tile_hard_edge_hole_dist + round_edge_rad));
+
+thehd = [ tile_hard_edge_hole_dist, tile_hard_edge_hole_dist ];
+thehd_tr = thehd;
+thehd_tl = [ -thehd_tr[0], thehd_tr[1] ];
+thehd_bl = -thehd_tr;
+thehd_br = -thehd_tl;
+
+tablet_z_slop = 1.00;
+
+interlock_rad = interlock_dia/2;
+interlock_negative_rad = interlock_rad + 0.125;
+
+interlock_sq_adj = 0.2; // arbitrary
+
+leg_fin_top_rad = sqrt( pow(leg_big_dia/2,2) -
+                       pow(leg_fin_top_w/2,2) );
+
+leg_tubule_pos_rad = leg_big_dia/2 * 0.6;
+
+m4_define(`POST_TCROSSSZ',
+          `2*( tile_hard_edge_hole_dist - test_edge + 1 )')
+
+module Post(){
+  mirror([0,0,1]) {
+    if (!JIG) {
+      difference(){
+       cylinder(r= post_dia/2, h= tile_th + ply_th - post_shorter);
+       translate([0,0, tile_th]) {
+         cylinder(r= screw_big_dia/2, h= screw_big_len);
+         cylinder(r= $screw_dia/2, h= ply_th, $fn=20);
+       }
+      }
+    }
+    if (TEST) {
+      translate([0,0, tile_th/2]) {
+       cube([post_dia,      POST_TCROSSSZ, tile_th], center=true);
+       cube([POST_TCROSSSZ, post_dia,      tile_th], center=true);
+      }
+    }
+    if (JIG) {
+      translate([0,0, tile_th/2]) {
+       cube([POST_TCROSSSZ, POST_TCROSSSZ, tile_th], center=true);
+      }
+    }
+  }
+}
+
+module PostHole(){
+  if (JIG) {
+    translate([0,0,-5])
+      cylinder(r= post_dia/2 + jig_post_hole_slop, h=10);
+    translate([0,0, -jig_min_th])
+      cylinder(r= ply_hole_dia_real/2, h = 5);
+    for (rot=[0:90:270]) rotate(rot) {
+       translate([ ply_edge_hole_dist_real, 0, 0 ])
+         cube([ jig_pencil_rad*2, jig_pencil_slotlen, 20 ], center=true);
+      }
+  }
+}
+
+module Posts(posts) {
+  for (p= posts) {
+    translate(concat(p, [0]))
+      Post();
+  }
+}
+module PostHoles(posts) {
+  for (p= posts)  {
+    translate(concat(p, [0]))
+      PostHole();
+  }
+}
+
+module TileBase(botleft, topright){
+  size = topright - botleft;
+  botleft_post = botleft + thehd_tr;
+  topright_post = topright + thehd_bl;
+  difference(){
+    mirror([0,0,1])
+      translate(concat(botleft, [0]))
+      cube(concat(size, [tile_th]));
+    if (!(TEST || JIG)) {
+      cidsz = topright_post-botleft_post
+       + [-post_dia,-post_dia]
+       + [0, thehd[1]];
+      cidszr = [ min(cidsz[0],50), min(cidsz[1],50) ];
+      echo("CID",cidsz,cidszr);
+      translate( concat(botleft_post, [ -tile_th ])
+                + 0.5 * [ post_dia, post_dia, 0 ]
+                + 0.5 * concat( cidsz - cidszr, [ 0 ]) )
+       Commitid_BestCount_M(cidszr);
+    }
+    if ((TEST || JIG)) {
+      crossoff = tile_hard_edge_hole_dist + POST_TCROSSSZ/2;
+      cidsz = [ thehd[0], size[1] - 2*crossoff ];
+      cidszr = [ cidsz[0], min(cidsz[1], 50) ];
+      if (TEST)
+       translate( concat(botleft + [0, crossoff] + (cidsz-cidszr)/2, [0]) )
+         Commitid_BestCount(cidszr);
+      difference(){
+       mirror([0,0,1]) {
+         translate(concat(botleft + [test_edge,test_edge], [test_tile_th]))
+           cube(concat(size - [test_edge,test_edge]*2, [tile_th*2]));
+         translate(concat(botleft_post, [-1]))
+           cube(concat(topright_post-botleft_post, [tile_th+2]));
+       }
+       shufflesz = max(test_edge, tile_hard_edge_hole_dist)*2;
+       minkowski(){
+         MachineEnvelope();
+         cube(shufflesz, center=true);
+       }
+       if (JIG) {
+         translate([0,0,-20]) linear_extrude(height=20) {
+           for (diag=[[ +1, botleft                   ],
+                      [ -1, [topright[0], botleft[1]] ]]) {
+             translate(diag[1])
+               rotate(atan2(size[1], diag[0] * size[0]))
+               translate([0, -test_edge/2])
+               square([vectorlen2d(size), test_edge]);
+           }
+         }
+       }
+      }
+    }
+  }
+}
+
+m4_dnl   R_EDGE(c,ix)
+m4_dnl        c is from Rectangle_corners and
+m4_dnl        ix is a corner number
+m4_dnl    expands to two comma-separated corners:
+m4_dnl    that denoted by ix, and the next one anticlockwise
+m4_define(`R_EDGE',`$1[$2], $1[(($2)+1)%4]')
+
+m4_dnl   R_CNR(c,ix)
+m4_dnl        c is from Rectangle_corners and
+m4_dnl        ix is a corner number
+m4_dnl    expands to an array of corners as for RoundCorner
+m4_define(`R_CNR',`[ $1[$2], $1[(($2)+1)%4], $1[(($2)+3)%4] ]')
+
+m4_dnl  INREFFRAME(left_cnr, right_cnr, morevars) { body; }
+m4_define(`INREFFRAME',`
+  length_vec = ($2) - ($1);
+  length = dist2d([0,0], length_vec);
+  length_uvec = length_vec / length;
+  ortho_uvec = [ -length_uvec[1], length_uvec[0] ];
+  m = [ [ length_uvec[0],  ortho_uvec[0], 0, ($1)[0], ],
+       [ length_uvec[1],  ortho_uvec[1], 0, ($1)[1], ],
+       [ 0,              0,              1,            0, ],
+       [ 0,              0,              0,            1, ] ];
+  $3
+  multmatrix(m)
+')
+
+m4_dnl  INREFFRAME(left_cnr, right_cnr, morevars)
+m4_dnl    INREFFRAME_EDGE { body; }
+m4_define(`INREFFRAME_EDGE',`
+  translate([0,0, -round_edge_rad])
+')
+
+module RoundEdge(left_cnr, right_cnr) {
+  INREFFRAME(left_cnr, right_cnr)
+    INREFFRAME_EDGE {
+    difference(){
+      rotate([0,90,0])
+       cylinder(r= round_edge_rad, h= length, $fn=50);
+      translate([-1, 0, -20])
+       cube([length+2, 20, 20]);
+    }
+  }
+}
+
+m4_define(`ROUNDCORNER_VARS',`
+  this_cnr = ci[0];
+  right_cnr = ci[1];
+  left_cnr = ci[2];
+  bigr= round_cnr_rad - round_edge_rad;
+  l_uvec = unitvector2d(left_cnr - this_cnr);
+  r_uvec = unitvector2d(right_cnr - this_cnr);
+  lp1 = left_cnr  + clockwise2d(l_uvec) * bigr;
+  lp2 = this_cnr  + clockwise2d(l_uvec) * bigr;
+  lp3 = this_cnr  - clockwise2d(r_uvec) * bigr;
+  lp4 = right_cnr - clockwise2d(r_uvec) * bigr;
+  ctr = line_intersection_2d(lp1,lp2,lp3,lp4);
+  ctr3 = concat(ctr,[0])
+')
+
+module RoundCorner_selector(ci, adj) {
+  ROUNDCORNER_VARS;
+  intersection(){
+    union(){
+      INREFFRAME(ctr3,concat(lp1,[4])){
+       translate([0,0,-bigr]) linear_extrude(height=bigr*2) {
+         translate([-bigr*2 + adj, -bigr])
+           square([bigr*2, bigr*3]);
+       }
+      }
+    }
+    union(){
+      INREFFRAME(ctr3,concat(lp4,[0])){
+       translate([0,0,-bigr]) linear_extrude(height=bigr*2) {
+         translate([-bigr*2, -bigr*2])
+           square([bigr*2 + adj, bigr*3]);
+       }
+      }
+    }
+  }
+}
+
+module RoundCornerCut(ci) {
+  // ci should be [this_cnr, right_cnr, left_cnr]
+  // where right_cnr is to the right (ie, anticlockwise)
+  ROUNDCORNER_VARS;
+  difference(){
+    RoundCorner_selector(ci, -0.1);
+    translate(ctr3)
+      cylinder(center=true, h=20, r= bigr);
+  }
+}
+
+module RoundCornerAdd(ci) {
+  ROUNDCORNER_VARS;
+  intersection(){
+    RoundCorner_selector(ci, +0.1);
+    INREFFRAME_EDGE {
+      translate(ctr3){
+       rotate_extrude(convexity=10, $fn=50)
+         translate([bigr, 0])
+         difference(){
+         circle(r= round_edge_rad, $fn=50);
+         mirror([1,1])
+           square([20,20]);
+       }
+      }
+    }
+  }
+}
+
+module InterlockLobePlan(negative) {
+  r = negative ? interlock_negative_rad : interlock_rad;
+  ymir = negative ? 0 : 1;
+
+  dx = sqrt(3) * r;
+  $fn= 80;
+  translate([thehd[0], 0]){
+    mirror([0,ymir]){
+      circle(r=r);
+      difference(){
+       translate([-dx, -0.1])
+         square([ dx*2, r/2 + 0.1 ]);
+       for (xi = [-1, 1]) {
+         translate([ xi*dx, r ])
+           circle(r=r);
+       }
+      }
+    }
+  }
+}
+
+module InterlockEdgePlan(negative, nlobes, length, dosquare=true) {
+  for (lobei = [ 0 : nlobes-1 ]) {
+    lobex = (length - thehd[0]*2) * (lobei ? lobei / (nlobes-1) : 0);
+    translate([lobex, 0, 0]) {
+      InterlockLobePlan(negative);
+    }
+  }
+
+  if (dosquare) {
+    iadj = 0;
+    slotshorter = negative ? -0.1 : interlock_fine_lenslop;
+    mirror([0, negative])
+      translate([slotshorter, iadj])
+      square([length - slotshorter*2, interlock_fine + iadj*2]);
+  }
+}
+
+module InterlockEdge(left_cnr, right_cnr, negative=0, nlobes=2) {
+  plusth = negative * 1.0;
+  protr = interlock_fine + interlock_sq_adj;
+
+  z2 = -tile_th/2;
+  z1 = -tile_th/2 - protr / interlock_fine_slope;
+  z3 = -tile_th/2 + protr / interlock_fine_slope;
+
+  negsign = negative ? -1 : +1;
+  yprotr = negsign * protr;
+
+  INREFFRAME(left_cnr, right_cnr) {
+    for (vsect = [ // zs0            zs1      ys0,            ys1
+                 [ -tile_th-plusth, plusth,  0,              0],
+                 [ z1,              z2,      0, yprotr],
+                 [ z2,              z3,      yprotr, 0],
+                 ]) {
+      zs0 = vsect[0];
+      zs1 = vsect[1];
+      zsd = zs1-zs0;
+      ys0 = vsect[2];
+      ys1 = vsect[3];
+      ysd = ys1-ys0;
+      sl = ysd/zsd;
+      m = [ [ 1,0,   0,    0 ],
+           [ 0,1, -sl, -ys0 + negsign*interlock_sq_adj ],
+           [ 0,0,   1,  zs0 ],
+           [ 0,0,   0,    1 ] ];
+      multmatrix(m)
+       linear_extrude(height=zsd, convexity=10)
+       InterlockEdgePlan(negative, nlobes, length, !!ysd);
+    }
+  }
+}
+
+function TestPiece_holes2corners(holes) =
+  [ holes[0] + thehd_bl,
+    holes[1] + thehd_br,
+    holes[1] + thehd_tr,
+    holes[0] + thehd_tl ];
+
+module TestPiece1(){ ////toplevel
+  holes = [ [-100, 0],
+           [   0, 0]
+           ];
+  corners = TestPiece_holes2corners(holes);
+  rcs = R_CNR(corners,0);
+  difference(){
+    union(){
+      TileBase(corners[0], corners[2]);
+      Posts(holes);
+      RoundEdge(corners[0], corners[1]);
+      RoundEdge(corners[3], corners[0]);
+    }
+    InterlockEdge(corners[1], corners[2], 1, nlobes=1);
+    RoundCornerCut(rcs);
+    PostHoles(holes);
+  }
+  RoundCornerAdd(rcs);
+}
+
+module TestPiece2(){ ////toplevel
+  holes = [ [   0, 0],
+           [  50, 0]
+           ];
+  corners = TestPiece_holes2corners(holes);
+  difference(){
+    union(){
+      TileBase(corners[0], corners[2]);
+      Posts(holes);
+      RoundEdge(corners[0], corners[1]);
+      InterlockEdge(corners[3], corners[0], 0, nlobes=1);
+    }
+    PostHoles(holes);
+  }
+}
+
+module TestDemo(){ ////toplevel
+  translate([ -thehd[0], 0 ])
+    color("blue")
+    TestPiece1();
+  translate([ +thehd[0] + demo_slop, 0 ])
+    TestPiece2();
+}
+
+module PostTestPiece(){ ////toplevel
+  hole_sizes = [2.8, 3.0, 3.1, 3.134, 3.168, 3.2, 3.3, 3.5];
+  nholes = len(hole_sizes)*2;
+  nrows = 4;
+  stride = post_dia*1.5;
+  rect_sz = stride * [ nrows,
+                      ceil(nholes/nrows) ];
+  corners = Rectangle_corners(-stride * 0.5 * [1,1], rect_sz);
+  difference(){
+    union(){
+      TileBase(corners[0], corners[2]);
+      RoundEdge(corners[0], corners[1]);
+      for (i= [ 0: nholes-1 ]) {
+       $screw_dia = hole_sizes[ floor(i/2) ];
+       translate(stride * [ (nrows-1) - (i % nrows),
+                            floor(i / nrows),
+                            0
+                            ]) {
+         Posts([[0,0]]);
+         color("blue")
+           mirror([0,0,1])
+           translate([post_dia/2, -post_dia/2, 1])
+           cube([1, post_dia * (i / nholes), tile_th]);
+       }
+      }
+    }
+  }
+}
+
+module Machine_NewRearProfile(){
+  // figures copied out of xfig edit boxes
+  // best not to edit the posbox size if poss - just move it
+  posbox = 10 * ([7.2333,-14.1267] - [-16.2289,40.0289]); // box, Green
+  sideline = -10 * ([-6.2400,13.5600] - [-2.4467,28.2556]); // line, Blue
+  scaleline = 10 * dist2d([-1.1911,-20.4800], [-11.2600,4.0578]); // Green2
+  scaleline_mm = 12+5+10+5+3;
+  sh = -[abs(posbox[0]), abs(posbox[1])];
+  rot = atan2(-sideline[0], sideline[1]);
+  sc = scaleline_mm / scaleline;
+  //echo("SH",sh,rot,sc);
+  scale(sc) rotate(rot) translate(sh){
+    import("sewing-table-rear-profile.dxf", convexity=10); // spline, Pink3
+  }
+}
+
+module Machine_NewFrontProfile(){
+  // figures copied out of xfig edit boxes
+  // best not to edit the posbox size if poss - just move it
+  posbox = 10 * ([11.8022,8.0600] - [4.2044,19.1867]); // box, Green
+  refline = 10 * ([7.6778,16.7222] - [27.8689,17.6578]); // line, Blue
+  refline_mm = (11-1)*10;
+  sh = -[abs(posbox[0]), abs(posbox[1])];
+  rot = atan2(-refline[0], refline[1]);
+  sc = refline_mm / vectorlen2d(refline);
+  //echo("SH",sh,rot,sc);
+  mirror([1,0]) scale(sc) rotate(rot+90) translate(sh){
+    import("sewing-table-front-profile.dxf", convexity=10); // spline, Pink3
+  }
+}
+
+module Machine_NewEndProfile(){
+  // figures copied out of xfig edit boxes
+  // NB that y coords are reversed - xfig origin is at bottom left
+  posboxs = 10 * [[4.0400,17.7956], [11.6622,32.5511]]; // box, Pink3
+  refline = 10 * ([8.4000,22.6000] - [50.3000,22.2000]); // line, Blue
+  refline_mm = 10 * (11 - 2.5);
+  sidelines = 10 * [[[9.0889,20.6178], [8.9644,14.6889]],
+                   [[50.3800,21.9578], [50.1933,14.4933]]]; // lines, Blue3
+  baseline = 10 * [[8.4000,18.0822], [50.3000,17.6822]]; // line, Green2
+
+  rot_adj = -0.38;
+
+  posbox = [min(posboxs[0][0],posboxs[1][0]),
+           max(posboxs[0][1],posboxs[1][1])];
+
+  m4_define(`MNEP_ELP',
+     `line_intersection_2d(baseline[0],baseline[1],
+                           sidelines[$1][0],sidelines[$1][1])')
+  endline = [MNEP_ELP(0),MNEP_ELP(1)];
+
+  rot = atan2(-refline[1], -refline[0]);
+  sc = refline_mm / vectorlen2d(refline);
+  sh = (0.5 * (endline[0] + endline[1])) - posbox;
+
+  ellen = sc * dist2d(endline[0],endline[1]);
+  scy = cutout_l_end_y_total / ellen;
+
+  scale([scy,1]) scale(sc) rotate(rot + rot_adj) translate(-[sh[0],-sh[1]]){
+
+    mirror([0,1]){
+  //%translate(1 * (posboxs[0] - posbox)) square(50);
+  //%translate(1 * (posboxs[1] - posbox)) square(50);
+//  %translate(1 * (baseline[0] - posbox)) square([50,10]);
+
+//  %translate(1 * (endline[0] - posbox)) square([50,10]);
+//  %translate(1 * (endline[1] - posbox)) square([50,10]);
+
+//  %translate(1 * (sidelines[0][0] - posbox)) square([10,50]);
+//  %translate(1 * (sidelines[0][1] - posbox)) square([10,50]);
+//  %translate(1 * (sidelines[1][0] - posbox)) square([10,50]);
+//  %translate(1 * (sidelines[1][1] - posbox)) square([10,50]);
+    }
+
+    import("sewing-table-end-profile.dxf", convexity=10); // spline, Pink3
+  }
+}
+
+module Machine_NewEndProfileDemo(){ ////toplevel
+    translate([0,5,0])                             Machine_NewEndProfile();
+    translate([0,5,1]) color("blue") mirror([1,0]) Machine_NewEndProfile();
+  mirror([0,1,0]){
+    translate([0,5, 0])                             Machine_NewEndProfile();
+    translate([0,5,-1]) color("blue") mirror([1,0]) Machine_NewEndProfile();
+  }
+}
+
+module Machine_NewArm(){
+  translate([0,0,-30]) linear_extrude(height=60) {
+    translate(tile01_tr + [ -cutout_l_end_x - cutout_l_end_new_x_slop,
+                           (-cutout_tile01_y + cutout_tile11_y)/2 ]){
+      rotate(-90){
+       hull(){
+         for (d=[0,400]) 
+           translate([0,d]) Machine_NewEndProfile();
+       }
+      }
+    }
+  }
+}
+
+module Machine_NewRearCurve(){
+  slant = atan2(4,210-10);
+  //echo("SL",slant);
+  translate([0,0, rearcurve_double_inrad]) rotate([slant,0,0]){
+    translate([ rearcurve_double_inrad,
+               0,
+               -rearcurve_double_inrad + 10 ]){
+      rotate([180,0,0]) rotate([0,0,90]) linear_extrude(height=30){
+       hull(){
+         Machine_NewRearProfile();
+         translate([0,-100]) Machine_NewRearProfile();
+       }
+      }
+    }
+    rotate([0,90,0]) rotate([90,0,0]) {
+      intersection(){
+       rotate_extrude(convexity=10, $fn=64)
+         rotate(90)
+         translate([ 0, -rearcurve_double_inrad ])
+         Machine_NewRearProfile();
+       translate([0,0,-500])
+         cube([500,500,1000]);
+      }
+    }
+    translate([1,0,-rearcurve_double_inrad])
+      rotate([0,-90,0]) rotate([0,0,-90])
+      linear_extrude(height= rearcurve_strt_len + 1)
+      Machine_NewRearProfile();
+  }
+}
+
+module Machine_Curves(){ ////toplevel
+  translate([ tile01_tr[0] - cutout_l_end_x + rearedge_len,
+             cutout_tile11_y,
+             0 ]){
+    //%cube([20,20,20]);
+    translate([ -reartablet_x,
+               -1,
+               -reartablet_z + tablet_z_slop])
+      mirror([0,0,1])
+      cube([ reartablet_x+1,
+            reartablet_y+1,
+            20 ]);
+  }
+  translate([ tile01_tr[0] - cutout_l_end_x + frontedge_len,
+             cutout_tile11_y,
+             frontcurve_z_slop ]){
+    translate([0, -machine_rear_to_front, 0])
+      multmatrix([[1, -frontcurve_side_skew, 0, 0],
+                 [0,  1,   0, 0],
+                 [0,  0,   1, 0],
+                 [0,  0,   0, 1]])
+      mirror([1,0,0]) rotate([0,-90,0])rotate([0,0,-90])
+      linear_extrude(height= 200)
+      Machine_NewFrontProfile();
+  }
+  translate([ tile01_tr[0] - cutout_l_end_x + rearedge_len,
+             cutout_tile11_y,
+             frontcurve_z_slop ]){
+    translate([ rearcurve_strt_len,
+               0,
+               rearcurve_z_slop ]){
+      Machine_NewRearCurve();
+    }
+  }
+}
+
+module TestStrapSlots(){
+  pegwidth = teststrap_peg[0];
+  for (pos = teststrapslots_at) {
+    echo("TSS",pos);
+    translate(concat(pos,[0]))
+      for (mx = [0,1]) mirror([mx,0,0]) {
+         translate([ pegwidth/2, -teststrap[1]/2, -20 ])
+           cube(concat(teststrap,[40]));
+       }
+  }
+}
+
+module TestStrapPeg_any(l){ cube(concat([l],teststrap_peg)); }
+
+module TestStrapPeg_Short(){ ////toplevel
+  TestStrapPeg_any(35);
+}
+
+module TestStrapPeg_Long(){ ////toplevel
+  TestStrapPeg_any(60);
+}
+
+module PostSpacer(){ ////toplevel
+  $fn = 50;
+  difference(){
+    cylinder(r= ply_hole_dia_real/2 - spacer_ext_slop,
+            h= spacer_height);
+    translate([0,0,-1])
+      cylinder(r= post_dia/2 + spacer_int_slop,
+              h= ply_th + 2);
+  }
+}
+
+module Machine(){ ////toplevel
+  Machine_NewArm();
+  Machine_Curves();
+  if (TEST)
+    TestStrapSlots();
+}
+
+module MachineEnvelope(){
+  // used for testing
+  p_arm_bl = [-cutout_l_end_x, -cutout_tile01_y];
+  y_arm_t  = cutout_tile11_y;
+  p_crv_fl = p_arm_bl + [frontedge_len, -frontcurve_avoid_y];
+  y_crv_b  = y_arm_t + rearcurve_avoid_y;
+
+  translate([0,0,-50]) linear_extrude(height= 100){
+    translate(p_arm_bl) square([400, y_arm_t] - p_arm_bl);
+    translate(p_crv_fl) square([400, y_crv_b] - p_crv_fl);
+  }
+}
+
+module Leg(){ ////toplevel
+  difference(){
+    union(){
+      hull(){
+       mirror([0,0,1])
+         cylinder(r= leg_big_dia/2, h=leg_top_flat_z, $fn=100);
+       translate([0,0, -leg_top_thick])
+         cylinder(r= leg_bot_dia/2, height=1, $fn=100);
+      }
+      if (!TEST)
+       translate([0,0,-leg_height])
+         cylinder(r= leg_bot_mid_dia/2, h=leg_bot_thick);
+      for (rot=[0: 360/leg_n_fins : 359]) rotate(rot) {
+         hull(){
+           mirror([0,0,1]) translate([0, -leg_fin_top_w/2, 0])
+             cube([ leg_fin_top_rad - 0.1,
+                    leg_fin_top_w,
+                    1 ])
+             ;
+           translate([0, -leg_fin_bot_w/2, -leg_height])
+             cube([ leg_fin_bot_rad,
+                    leg_fin_bot_w,
+                    leg_fin_bot_flat_z ]);
+         }
+       }
+    }
+    mirror([0,0,1]) translate([0,0,-1])
+      cylinder(r= leg_hole_dia/2,
+              h= (!TEST ? leg_height+2 : leg_height/2),
+              $fn=30);
+    mirror([0,0,1]) translate([0,0,leg_top_thick - 0.1])
+      hull(){
+        cylinder(r= (!TEST ? leg_midspc_dia/2 : 0.1),
+                h= leg_height - leg_top_thick - leg_bot_thick + 0.2,
+                $fn=30);
+       if (TEST)
+         cylinder(r= leg_midspc_dia/2,
+                  h= leg_height - leg_top_thick - leg_bot_thick
+                     + (!TEST ? 0.2 : -leg_midspc_dia/2),
+                  $fn=30);
+      }
+    cid_shear = (leg_fin_bot_w - leg_fin_top_w)/2 /
+                 (leg_height -leg_fin_bot_flat_z);
+    multmatrix([[ 1, 0, 0, leg_midspc_dia/2 ],
+                 [ 0, cid_shear,
+                         1, -leg_fin_bot_w/2 ],
+                 [ 0, 1, 0, -leg_height + leg_fin_bot_flat_z ],
+                 [ 0, 0, 0, 1 ]])
+      Commitid_BestCount([ leg_big_dia/2 - leg_midspc_dia/2,
+                            leg_height - leg_fin_bot_flat_z
+                             - leg_top_thick ]);
+    if (!TEST)
+      for (rot=[45: 360/leg_n_tubules : 359]) rotate(rot) {
+         mirror([0,0,1]) translate([ leg_tubule_pos_rad, 0, -1])
+           cylinder(r= leg_tubule_dia/2, h=leg_height+2, $fn=20);
+       }
+  }
+}
+
+function Rectangle_corners(c0, sz) =
+  // returns the corners of a rectangle from c0 to c0+sz
+  // if sz is positive, the corners are anticlockwise starting with c0
+  [ c0 + [ 0,     0     ],
+    c0 + [ sz[0], 0     ],
+    c0 + [ sz[0], sz[1] ],
+    c0 + [ 0,     sz[1] ] ];
+
+function Rectangle_corners2posts(c) =
+  [ c[0] + thehd_tr,
+    c[1] + thehd_tl,
+    c[2] + thehd_bl,
+    c[3] + thehd_br ];
+
+module Rectangle_TileBase(c) { TileBase(c[0], c[2]); }
+
+function Posts_interpolate_one(c0,c1) = [c0, (c0+c1)/2, c1];
+
+module Tile02(){ ////toplevel
+  sz = [100,170];
+  c0 = tile02_tr + -sz;
+  c = Rectangle_corners(c0, sz);
+  posts = Rectangle_corners2posts(c);
+  rcs = R_CNR(c,0);
+  difference(){
+    union(){
+      Rectangle_TileBase(c);
+      Posts(posts);
+      RoundEdge(R_EDGE(c,0));
+      RoundEdge(R_EDGE(c,3));
+      InterlockEdge(R_EDGE(c,2), 0);
+    }
+    InterlockEdge(R_EDGE(c,1), 1);
+    RoundCornerCut(rcs);
+    PostHoles(posts);
+  }
+  RoundCornerAdd(rcs);
+}
+
+module Tile12(){ ////toplevel
+  sz = [100,250];
+  c0 = tile02_tr + [-sz[0], 0];
+  c = Rectangle_corners(c0, sz);
+  posts = Rectangle_corners2posts(c);
+  rcs = R_CNR(c,3);
+  difference(){
+    union(){
+      Rectangle_TileBase(c);
+      Posts(posts);
+      RoundEdge(R_EDGE(c,2));
+      RoundEdge(R_EDGE(c,3));
+    }
+    InterlockEdge(R_EDGE(c,0), 1);
+    InterlockEdge(R_EDGE(c,1), 1);
+    RoundCornerCut(rcs);
+    PostHoles(posts);
+  }
+  RoundCornerAdd(rcs);
+}
+
+tile_01_11_cnr = tile01_tr + [-cutout_l_end_x, 0];
+tile_11_10_cnr = tile01_tr + [0, cutout_tile11_y];
+tile_01_00_cnr = tile01_tr - [0, cutout_tile01_y];
+
+module Tile11(){ ////toplevel
+  sz = [250,250];
+  c0 = tile01_tr + [-sz[0],0];
+  c = Rectangle_corners(c0, sz);
+  cnr_posts = Rectangle_corners2posts(c);
+  posts = concat(
+                Posts_interpolate_one(cnr_posts[0],
+                                      cnr_posts[1] - [cutout_l_end_x, 0]),
+                [ cnr_posts[1] + [0, cutout_tile11_y],
+                  cnr_posts[2],
+                  cnr_posts[3]
+                  ]);
+  difference(){
+    union(){
+      Rectangle_TileBase(c);
+      Posts(posts);
+      RoundEdge(R_EDGE(c,2));
+      InterlockEdge(R_EDGE(c,3));
+    }
+    InterlockEdge(c[0], tile_01_11_cnr, 1);
+    InterlockEdge(tile_11_10_cnr, c[2], 1);
+    PostHoles(posts);
+    Machine();
+  }
+}    
+
+module Tile01(){ ////toplevel
+  sz = [250,170];
+  c0 = tile01_tr + -sz;
+  c = Rectangle_corners(c0, sz);
+  cnr_posts = Rectangle_corners2posts(c);
+  posts = concat(
+                Posts_interpolate_one(R_EDGE(cnr_posts,0)),
+                [ cnr_posts[2] + [0, -cutout_tile01_y] ],
+                Posts_interpolate_one(cnr_posts[2] - [cutout_l_end_x, 0],
+                                      cnr_posts[3])
+                );
+  difference(){
+    union(){
+      Rectangle_TileBase(c);
+      Posts(posts);
+      RoundEdge(R_EDGE(c,0));
+      InterlockEdge(tile_01_11_cnr, c[3]);
+      InterlockEdge(R_EDGE(c,3));
+    }
+    PostHoles(posts);
+    InterlockEdge(c[1], tile_01_00_cnr, 1);
+    Machine();
+  }
+}    
+
+module Tile10(){ ////toplevel
+  sz = [250,250];
+  c0 = tile01_tr + [0,0];
+  c = Rectangle_corners(c0, sz);
+  cnr_posts = Rectangle_corners2posts(c);
+  cty = cutout_tile11_y;
+  rcy = cty + rearcurve_avoid_y;
+  posts = [ cnr_posts[0] + [ 0,                             cty ],
+           cnr_posts[1] + [ -sz[0] + rearedge_len - cutout_l_end_x, cty ],
+           cnr_posts[1] + [ 0,                             rcy ],
+           cnr_posts[2],
+           cnr_posts[3] ];
+  rcs = R_CNR(c,2);
+  difference(){
+    union(){
+      Rectangle_TileBase(c);
+      Posts(posts);
+      RoundEdge(R_EDGE(c,1));
+      RoundEdge(R_EDGE(c,2));
+      InterlockEdge(c[3], tile_11_10_cnr);
+    }
+    PostHoles(posts);
+    RoundCornerCut(rcs);
+    Machine();
+  }
+  RoundCornerAdd(rcs);
+}
+
+module Tile00(){ ////toplevel
+  sz = [250,170];
+  c0 = tile01_tr + [0,-sz[1]];
+  c = Rectangle_corners(c0, sz);
+
+  // the edge c[1]..c[2] needs a diagonal chunk, from c1bis to c2bis
+  c2bis = [ -cutout_l_end_x + frontedge_len + frontcurve_strt_len, c[2][1] ];
+  c1bis = [ c[1][0],
+           c[2][1] -
+           (c[2][0] - c2bis[0]) * tan(90 - frontcurve_dualcurve_angle) ];
+
+  cnr_posts = Rectangle_corners2posts(c);
+  cty = cutout_tile01_y;
+  rcy = cty + frontcurve_avoid_y;
+  posts = [ cnr_posts[0],
+           cnr_posts[1],
+           0.5 * (cnr_posts[0] + cnr_posts[1]),
+           cnr_posts[2] + [ 0,                             -rcy ],
+           cnr_posts[2] + [ -sz[0] + frontedge_len - cutout_l_end_x, -cty ],
+           cnr_posts[3] + [ 0,                             -cty ]
+           ];
+  rcs = R_CNR(c,1);
+  rc2 = [c1bis,c2bis,c[1]];
+  difference(){
+    union(){
+      difference(){
+       union(){
+         Rectangle_TileBase(c);
+         Posts(posts);
+         RoundEdge(R_EDGE(c,0));
+         RoundEdge(c[1], c1bis);
+         InterlockEdge(tile_01_00_cnr, c[0]);
+       }
+       RoundCornerCut(rcs);
+       translate([0,0,-20]) linear_extrude(height=40) {
+         polygon([ c1bis, c1bis + [50,0], c2bis + [50,0], c2bis ]);
+       }
+      }
+      RoundEdge(c1bis, c2bis);
+    }
+    Machine();
+    PostHoles(posts);
+    RoundCornerCut(rc2);
+  }
+  RoundCornerAdd(rcs);
+  RoundCornerAdd(rc2);
+}
+
+module FitTest_general(c0,sz, dobrace=false, bracexx=0){
+  c = Rectangle_corners(c0, sz);
+  brace = [7,7,9];
+  bsz = sz + [bracexx,0,0];
+  difference(){
+    union(){
+      Rectangle_TileBase(c);
+      if (dobrace) {
+       translate(concat(c0, [-brace[2] + 0.1])){
+         difference(){
+           cube(concat(bsz, [brace[2]]) - [5,0,0]);
+           translate(brace + [0,0, -25])
+             cube(concat(bsz, [50]) - brace*2 + [10,0,0]);
+         }
+       }
+      }
+      RoundEdge(R_EDGE(c,1));
+    }
+    Machine();
+  }
+}
+
+module FitTest_PairLink(cut=false){ ////toplevel
+  cy0=-55; cy1=85; cx=132;
+  bar = [10,10];
+  legrad = 12;
+  footrad_min = 1; footrad_max = 4; footrad_depth = 5;
+  strap = [3,5];
+  adj_neg_slop = 1.0;
+  bar_z_slop = 1.75;
+
+  // calculated
+  straphole_x_max = legrad/sqrt(2) + footrad_max;
+  dz = cut ? adj_neg_slop : 0;
+
+  translate([cx - bar[0]/2, cy0, dz + bar_z_slop])
+    cube([bar[0], cy1-cy0, bar[1] - bar_z_slop]);
+
+  for (endy=[cy0,cy1]) {
+    $fn=32;
+    translate([cx,endy,dz]){
+      // feet
+      for (rot=[45:90:315]) rotate(rot) {
+       translate([legrad,0,0]){
+         hull(){
+           cylinder(r= footrad_max, h=1);
+           translate([0,0,-footrad_depth])
+             cylinder(r= footrad_min, h=1);
+         }
+         if (cut)
+           translate([0,0,-10])
+           cylinder(r= footrad_min +
+                    adj_neg_slop * (footrad_max-footrad_min)/footrad_depth,
+                    h=20);
+       }
+      }
+      // legs
+      for (rot=[45,135]) rotate(rot) {
+       hull(){
+         for (s=[-1,+1]){
+           translate([s*legrad,0,0])
+             cylinder(r= footrad_max, h=bar[1]);
+         }
+       }
+      }
+      // strap holes
+      if (cut) {
+       for (rot=[0,180]) rotate(rot) {
+           translate([ straphole_x_max - strap[0]/2, 0,0 ])
+             cube(concat(strap,[20]), center=true);
+         }
+      }
+    }
+  }
+}
+
+module FitTest_RearCurve(){ ////toplevel
+  difference(){
+    FitTest_general([100,0], [180,100]);
+    FitTest_PairLink(true);
+    TestStrapSlots();
+  }
+}
+
+module FitTest_FrontCurve(){ ////toplevel
+  p0 = [100,-80];
+  sz = [180,80];
+  difference(){
+    intersection() {
+      Tile00();
+      translate([0,0,-8]) linear_extrude(height=18) {
+       translate(p0) square(sz);
+       translate(teststrapslots_at[3])
+         scale(2* [ teststrap_peg[0], teststrap[1] ])
+         circle(r=1, $fn=20);
+      }
+    }
+    FitTest_PairLink(true);
+    TestStrapSlots();
+  }
+}
+
+module FitTest_Entire(){ ////toplevel
+  p0 = [-33,-80];
+  szrear = [263,180];
+  szfront = [243,szrear[1]];
+  difference(){
+    FitTest_general(p0, szrear, dobrace=true, bracexx=0);
+    FitTest_PairLink(true);
+    translate(concat(p0,[0]) + [szfront[0],-10,-40])
+      cube([100, -p0[1], 80]);
+    TestStrapSlots();
+  }
+  intersection(){
+    FitTest_RearCurve();
+    translate(concat(p0,[-20])) cube(concat(szrear,[40]));
+  }
+  FitTest_FrontCurve();
+}
+
+module FitTest_EntireDemo(){ ////toplevel
+  FitTest_Entire();
+  //%Tile00();
+}
+
+module FitTest_EndEnd(){ ////toplevel
+  p0 = [-30,-32];
+  sz = [156,81] - p0;
+  sz2 = [136,68] - p0;
+  difference(){
+    FitTest_general(p0, sz);
+    translate([ p0[0] -1, p0[1]+sz2[1], -10])
+      cube([ sz2[0] +1, 50, 20 ]);
+  }
+}
+
+module FitTest_PairDemo(){ ////toplevel
+  sh=[-90,-15,0];
+  translate(sh){
+    FitTest_PairLink();
+    %FitTest_FrontCurve();
+    %FitTest_RearCurve();
+  }
+  rotate([0,0,180]){
+    translate(sh){
+      difference(){
+       union(){
+         FitTest_FrontCurve();
+         FitTest_RearCurve();
+       }
+       #FitTest_PairLink(true);
+      }
+    }
+  }
+}
+
+module RoundCornerDemo_plat(cnr){
+  mirror([0,0,1]) linear_extrude(height=1) polygon(cnr);
+}
+
+module RoundCornerDemo(){ ////toplevel
+  cnr = [ [-2,-3], [13,-3], [-12,9] ];
+  translate([0,25,0]) RoundCornerDemo_plat(cnr);
+  translate([25,0,0]) RoundCornerAdd(cnr);
+  translate([-25,0,0]) RoundCornerCut(cnr);
+  translate([0,-25,0]) RoundCorner_selector(cnr, 0);
+  difference(){
+    RoundCornerDemo_plat(cnr);
+    RoundCornerCut(cnr);
+  }
+  RoundCornerAdd(cnr);
+}
+
+module Demo(){ ////toplevel
+  translate(demo_slop*[-2,1]) color("blue") Tile12();
+  translate(demo_slop*[-2,0]) color("red")  Tile02();
+  translate(demo_slop*[-2,1]) color("orange") Tile11();
+  translate(demo_slop*[-2,0]) color("purple") Tile01();
+  translate(demo_slop*[-3,1]) color("blue")   Tile10();
+  translate(demo_slop*[-3,0]) color("red")    Tile00();
+  %Machine();
+  // Can also do this, to print reference sheet:
+  //  load this into openscad
+  //  select Ctrl-4 view, view all, scale appropriately
+  //  import sewing-table,Demo-flat.png
+  //  pngtopnm <sewing-table,Demo-flat.png | ppmbrighten -s -50 -v +100 >t.pnm
+  //  lpr t.pnm
+}
+  
+//TestPiece1();
+//TestPiece2();
+//Demo();
+
+//Machine_NewRearProfile();
+//Machine_NewRearCurve();
+//Machine_NewFrontProfile();
+//Machine_NewEndProfile();
+//Machine_NewEndProfileDemo();
+//Machine_Curves();
+//Machine();
+//FitTest();
+//MachineEnvelope();
diff --git a/shelf-label-holder.scad b/shelf-label-holder.scad
new file mode 100644 (file)
index 0000000..8353d7e
--- /dev/null
@@ -0,0 +1,65 @@
+// -*- C -*-
+
+prong_nomdepth = 15;
+prong_curverad = 30;
+prong_thick = 0.7;
+prong_maxdepth = 18;
+
+front_thick = 2.5;
+
+//nom_shelf = 14.54 + 0.5;
+nom_shelf = 20.315 + 0.5;
+
+interference = 0.75;
+
+length = 60;
+
+// calculated
+
+interference_angle = atan2(interference, prong_nomdepth);
+
+module ProngElevationUnrotated(){
+  intersection(){
+    union(){
+      translate([ prong_nomdepth, prong_curverad ])
+       circle( prong_curverad , $fa=0.5 );
+      translate([ -10, 0 ])
+       square([ prong_nomdepth + 10, 10 ]);
+    }
+    translate([-5, -5])
+      square([ prong_maxdepth + 5, prong_thick + 5]);
+  }
+}
+
+module Elevation(){
+  difference(){
+    union(){
+      rotate(-interference_angle)
+       ProngElevationUnrotated();
+      translate([0, -nom_shelf])
+       mirror([0,1])
+       rotate(-interference_angle)
+       ProngElevationUnrotated();
+      translate([-10, -nom_shelf - prong_thick/2])
+       square([10, nom_shelf + prong_thick]);
+    }
+    mirror([1,0])
+      translate([ front_thick, -100 ])
+      square([ 50, 200 ]);
+  }
+}
+
+module Main(){
+  linear_extrude(height=length)
+    Elevation();
+}
+
+module Print(){
+  rotate([0,-90,0])
+    Main();
+}
+
+//ProngElevationUnrotated();
+//Elevation();
+Main();
+//Print();
diff --git a/simplephone-case-test.scad b/simplephone-case-test.scad
new file mode 100644 (file)
index 0000000..eedb932
--- /dev/null
@@ -0,0 +1,3 @@
+//// toplevels-from:sewing-table.scad
+include <simplephone-case.scad>
+$test = true;
diff --git a/simplephone-case.scad b/simplephone-case.scad
new file mode 100644 (file)
index 0000000..2cbc5ff
--- /dev/null
@@ -0,0 +1,167 @@
+// -*- C -*-
+
+psz = [
+       120,
+       56 + 5 - 3.75,
+       15 + 3,
+       ];
+
+thick = [
+        2,
+        2,
+        1.5,
+        ];
+
+btn_x = 59.6;
+btn_dia = 13;
+btn_y = 14.03;
+
+abtn_x = 46.85;
+abtn_sz = [ 11, 13 ];
+
+screen_xbot = 79;
+screen_sz = [ 35, 46 ];
+
+thumb_xbot = 90;
+thumb_dia = 25;
+
+vol_xbot = 86.5;
+vol_xtop = 107.5;
+vol_depth = 1.0;
+vol_zsz = 9;
+vol_zoff = 0;
+
+rail_ysz = 2.5;
+rail_zsz = 2.5;
+
+stay_height = 1.49;
+
+case_x_less = 0; //case_x_less = 10;
+
+inner_cnr_rad = 4.0;
+
+// calculated
+
+btn_yprop = btn_y / psz[1];
+echo(btn_yprop);
+
+ym = psz[1]/2;
+outer_cnr_rad = inner_cnr_rad + thick[2];
+
+x_sliced = outer_cnr_rad * (1-sin(45));
+
+$screen = true;
+
+module RoundedProfile(sz, cnr_rad){
+  hull(){
+    for (x=[ cnr_rad, sz[0]-cnr_rad ])
+      for (y=[ cnr_rad, sz[1]-cnr_rad ])
+       translate([x,y])
+         circle(r= cnr_rad, $fn=20);
+  }
+}
+
+module RoundedCube(sz, cnr_rad){
+  if ($test)
+    cube(sz);
+  else hull(){
+    for (x=[ cnr_rad, sz[0]-cnr_rad ])
+      for (y=[ cnr_rad, sz[1]-cnr_rad ])
+       for (z=[ cnr_rad, sz[2]-cnr_rad ])
+         translate([x,y,z])
+           sphere(r= cnr_rad, $fn=40);
+  }
+}
+
+module Stay(xbot, xtop, width, midgap_width) {
+  translate([ (xbot+xtop)/2, psz[1]/2, psz[2] ]){
+    difference(){
+      cube([ xtop-xbot, width, stay_height*2 ], center=true);
+      if (midgap_width > 0)
+       cube([ 200, midgap_width, 10 ], center=true);
+    }
+  }
+}
+
+module Stays(){
+  Stay(  76, 82, 10, 0);
+  Stay(-0.1, 55, 10, 0);
+  Stay( 113,125, 70, 15);
+}
+
+module Case(){
+  difference(){
+    mirror([1,0,0])
+      translate(-thick +
+               - [1,0,0] * x_sliced)
+      RoundedCube(psz
+                 + 2*thick
+                 - [1,0,0] * (thick[0])
+                 + [1,0,0] * (x_sliced)
+                 - [case_x_less, 0, 0],
+                 outer_cnr_rad);
+
+    for (yp= [ btn_yprop, 1-btn_yprop ])
+      translate([ -btn_x,
+                 yp * psz[1],
+                 0.5 * psz[2] ])
+       cylinder(r= btn_dia/2, h=20);
+
+    translate([ -abtn_x,
+               btn_yprop * psz[1],
+               psz[2] ])
+      cube(concat(abtn_sz, [ thick[2]*3 ]), center=true);
+
+    if ($screen)
+      mirror([1,0,0])
+      translate([ screen_xbot,
+                 (psz[1] - screen_sz[1])/2,
+                 psz[2]-3 ])
+      cube(concat(screen_sz, [ thick[2]+6 ]));
+
+    hull(){
+      for (x=[ thumb_xbot+thumb_dia/2, psz[0]+10 ])
+       translate([ -x,
+                   ym,
+                   -thick[2]-1 ])
+         cylinder(r= thumb_dia/2,
+                  h= thick[2] + 2,
+                  $fn= 20);
+    }
+
+    mirror([1,0,0])
+      translate([ (vol_xbot+vol_xtop)/2, 0, psz[2]/2 + vol_zoff ])
+      cube([ vol_xtop-vol_xbot, vol_depth*2, vol_zsz ], center=true);
+
+    translate([ thick[0], -10, -10 ])
+      cube([ 10, psz[1]+20, psz[2]+20 ]);
+
+    //translate([-50,-50,10]) cube([100,100,100]);
+
+    mirror([1,0,0])
+      difference(){
+       RoundedCube(psz + [1,0,0],
+                   inner_cnr_rad);
+
+        Stays();
+
+       if (0) for (m=[0,1]) {
+         translate([0,ym,0]) mirror([0,m,0]) translate([0,-ym,0])
+           translate([-1,-1, psz[2]-rail_zsz])
+           cube([psz[0]+1, rail_ysz+1, rail_zsz+1]);
+       }
+      }
+  }
+}
+
+module TestLoop(){
+  intersection(){
+    Case($screen=false);
+    translate([ -vol_xbot, 0,0 ])
+      cube([ 4, 200,200 ], center=true);
+  }
+}
+
+Case();
+//TestLoop();
+//RoundedCube(psz, inner_cnr_rad);
diff --git a/size-tests.m-g b/size-tests.m-g
new file mode 100644 (file)
index 0000000..fbb666b
--- /dev/null
@@ -0,0 +1,213 @@
+; -*- fundamental -*-
+
+; slic3r originally produced this, with these comments, and then we edited:
+; layer_height = 0.4
+; perimeters = 3
+; solid_layers = 3
+; fill_density = 0.4
+; perimeter_speed = 30
+; infill_speed = 60
+; travel_speed = 130
+; scale = 1
+; nozzle_diameter = 0.59
+; filament_diameter = 1.77
+; extrusion_multiplier = 1
+; single wall width = 0.71mm
+; first layer single wall width = 0.60mm
+
+M190 S65 ; wait for bed temperature to be reached
+M104 S215 ; set temperature
+G28 ; home all axes
+M109 S215 ; wait for temperature to be reached
+G90 ; use absolute coordinates
+G21 ; set units to millimeters
+G92 E0
+M82 ; use absolute distances for extrusion
+
+!zprint=0.3
+
+; "skirt" - prep extruder
+!draw 5,5 100,5 101,5.5 101,6.0 100,6.5 5,6.5
+
+; edge ticks for global motions sizing
+
+!edge_ticks(){
+!draw 20,10 10,10 10,20
+!draw 10,65 10,75
+!draw 10,120 10,130 20,130
+!draw 65,130 75,130
+!draw 120,130 130,130 130,120
+!draw 130,75 130,65
+!draw 130,20 130,10 120,10
+!draw 75,10 65,10
+!}
+
+;!edge_ticks()
+
+!outer_square(){
+ !draw @0.000,@0.000 @5.000,@0.000 @5.000,@5.000 @0.000,@5.000 @0.000,@0.000
+!}
+!inner_square(){
+ !draw @0.500,@0.500 @4.500,@0.500 @4.500,@4.500 @0.500,@4.500 @0.500,@0.500
+!}
+!outer_vlines(){
+ !draw @0.000,@0.000 @0.000,@5.000
+ !draw @5.000,@0.000 @5.000,@5.000
+!}
+!inner_vlines(){
+ !draw @0.500,@0.500 @0.500,@4.500
+ !draw @4.500,@0.500 @4.500,@4.500
+!}
+!outer_longrect(){
+ !draw @0.000,@0.000 @5.000,@0.000 @5.000,@20.000 @0.000,@20.000 @0.000,@0.000
+!}
+!inner_longrect(){
+ !draw @0.500,@0.500 @4.500,@0.500 @4.500,@19.500 @0.500,@19.500 @0.500,@0.500
+!}
+
+!org_oblong(){
+; original test oblong
+
+!draw 68.498,79.498 \
+71.502,79.498 \
+71.502,82.502 \
+68.498,82.502 \
+68.498,79.588 \
+*67.899,78.899 \
+72.101,78.899 \
+72.101,83.101 \
+67.899,83.101 \
+67.899,78.989 \
+*67.300,78.300 \
+72.700,78.300 \
+72.700,83.700 \
+67.300,83.700 \
+67.300,78.390 \
+
+!}
+
+!squares_tests(){
+
+!orgy=40
+
+!orgx=30
+!outer_longrect()
+
+!orgx=50
+!inner_longrect()
+!outer_longrect()
+
+!orgx=90
+!inner_square()
+!outer_square()
+
+!orgx=110
+!outer_square()
+
+!orgy=60
+!outer_vlines()
+
+!orgx=90
+!inner_vlines()
+!outer_vlines()
+
+!orgy=80
+!outer_square()
+!inner_square()
+
+!orgx=110
+!inner_square()
+
+!orgy=100
+!inner_vlines()
+
+!orgx=90
+!outer_vlines()
+!inner_vlines()
+
+!orgx=50
+!orgy=80
+!outer_longrect()
+!inner_longrect()
+
+!orgx=30
+!inner_longrect()
+
+!}
+
+!feedrate_tests(){
+
+!orgx=20
+!extruderate=0.045
+!feedrate_test1()
+
+!orgx=30
+!extruderate=0.060
+!feedrate_test1()
+
+!orgx=40
+!extruderate=0.080
+!feedrate_test1()
+
+!orgx=50
+!extruderate=0.100
+!feedrate_test1()
+
+!orgx=60
+!extruderate=0.125
+!feedrate_test1()
+
+!orgx=70
+!extruderate=0.150
+!feedrate_test1()
+
+!orgx=80
+!extruderate=0.200
+!feedrate_test1()
+
+!}
+
+!feedrate_test1(){
+
+!orgy=20
+!outer_longrect()
+
+!orgy=50
+!inner_longrect()
+!outer_longrect()
+
+!orgy=80
+!outer_longrect()
+!inner_longrect()
+
+!}
+
+!layer(){
+
+;--------------------
+
+;!squares_tests()
+;!org_oblong()
+!feedrate_tests()
+
+!}
+
+!layer()
+
+M104 S210 ; set temperature
+M140 S60 ; set bed temperature
+
+!zprint=0.7
+!layer()
+
+!zprint=1.1
+!layer()
+
+M83 ; extruder relative
+G1 F1000 ;1000mm/min extrusion
+G1 E-10; retract
+G1 X0 Y0 F10000
+G28 X0 Y0
+M104 S0 ; turn off temperature
+M140 S0 ; turn off bed
+M84     ; disable motors
diff --git a/sleepphone-cable-box.scad b/sleepphone-cable-box.scad
new file mode 100644 (file)
index 0000000..836f07d
--- /dev/null
@@ -0,0 +1,248 @@
+// -*- C -*-
+
+include <funcs.scad>
+
+wall = 0.75 * [1,1,1];
+wall_bot = 1.0;
+
+phone = [ 76.40, 30.96, 6.00 ]; // includes socket
+phone_button_z = 6.58;
+minwall = 0.50;
+
+expose = 4.00;
+
+cutout_dia = 7;
+cutout_between = 5;
+
+button_dz = 1.35;
+button_dz_centre = 1.35 + 0.75;
+button_dz_outer  = 1.35;
+
+button_dy_outer = 28.42;
+button_dy_inner = 19.05;
+button_dy_centre = 5.65;
+
+nrbutton_brace_x = 37.5;
+
+phone_slop = 0.5 * [1,1,0]
+           + 0.5 * [0,0,1];
+
+led = [25.9, 9.44]; // y is from edge
+led_dia = 4.4;
+
+// next values include slop
+plug_maxw = 10.95 + 0.35;
+plug_minw=   6.53 + 0.35;
+plug_sllen=  6.50;
+plug_totlen = 84.90 + 1.5 - 2.0; // to maxw, including phone
+
+plug_h = 6.5;
+plug_tooth_h = 0.5;
+plug_tooth_dy = 0.5;
+
+keeper_prong = 2;
+keeper_stalk_basewidth = 12;
+keeper_stalk_len = 70;
+keeper_stalk_gap = 1;
+keeper_stalk_thick = wall_bot;
+
+keeper_stalk_base_reinforce_len = 5;
+keeper_stalk_base_reinforce_thick = 2.0;
+
+// calculated
+
+top_z = max( phone[2] + wall[2],
+            phone_button_z + minwall )
+  + phone_slop[2];
+
+plugkeeper_x_maxw = phone[0] - plug_totlen;
+
+plugkeeper_p_max = [ 0, plug_maxw/2 ];
+plugkeeper_p_min = [ -plug_sllen, plug_minw/2 ];;
+plugkeeper_d_u = unitvector2d(
+                 clockwise2d(
+                 vecdiff2d( plugkeeper_p_max, plugkeeper_p_min )
+                            )
+                            );
+
+module MainProfileInnerHalf(){
+  p = phone + phone_slop;
+  pbc = p[2] + button_dz_centre;
+  pbo = p[2] + button_dz_outer;
+  polygon([[ -2,                 0    ],
+          [ p[1]/2,             0    ],
+          [ p[1]/2,             p[2] ],
+          [ button_dy_outer/2,  p[2] ],
+          [ button_dy_outer/2,  pbo  ],
+          [ button_dy_inner/2,  pbo  ],
+          [ button_dy_inner/2,  p[2] ],
+          [ button_dy_centre/2, p[2] ],
+          [ button_dy_centre/2, pbc  ],
+          [ -2,                 pbc  ]]);
+}
+
+module MainProfile(){
+  p = phone + phone_slop;
+  difference(){
+    for (m=[0,1]) mirror([m,0]) {
+       minkowski(){
+         translate([ -wall[1], -wall_bot ])
+           square([ wall[1]*2, wall_bot + wall[2] ]);
+         MainProfileInnerHalf();
+       }
+      }
+    for (m=[0,1]) mirror([m,0]) {
+       MainProfileInnerHalf();
+      }
+  }
+}
+
+module BraceProfileInitial(){
+  p = phone + phone_slop;
+  pbo = p[2] + button_dz_outer;
+  pbc = p[2] + button_dz_centre;
+  polygon([[ button_dy_outer/2 + 0.2,  p[2]           ],
+          [ button_dy_outer/2 + 0.2,  pbo  + wall[2] ],
+          [ button_dy_outer/2      ,  pbo  + wall[2] ],
+          [ button_dy_outer/2      ,  p[2]           ],
+          ]);
+}  
+
+module BraceProfileFinal(){
+  p = phone + phone_slop;
+  pbo = p[2] + button_dz_outer;
+  pbc = p[2] + button_dz_centre;
+  polygon([[ -1,                       p[2]           ],
+          [ -1,                       pbc  + wall[2] ],
+          [ 0,                        pbc  + wall[2] ],
+          [ 0,                        p[2]           ]
+          ]);
+}  
+
+module Brace(){
+  for (m=[0,1]) mirror([0,m,0]) {
+      hull(){
+       for (e=[0,1]) {
+         translate([ nrbutton_brace_x + e * phone[1]/2,
+                     0, 0 ]){
+           rotate([ 90,0,90 ]){
+             linear_extrude(height= (e==0 ? wall[0] : 0.1)){
+               hull(){
+                 BraceProfileInitial();
+                 if (e==0) BraceProfileFinal();
+             }
+             }
+           }
+         }
+       }
+      }
+    }
+}
+
+module BoxMain(){
+  rotate([0,0,90]) rotate([90,0,0]) {
+    translate([0,0, expose])
+      linear_extrude(height = phone[0] + wall[0] - expose, convexity=20)
+      MainProfile();
+    translate([0,0, phone[0]])
+      linear_extrude(height = wall[0], convexity=20)
+      hull() MainProfile();
+  }
+}
+
+module PlugKeeperProfileHalf(){
+  p_max = plugkeeper_p_max;
+  p_min = plugkeeper_p_min;
+  d = plugkeeper_d_u * keeper_prong;
+  
+  translate([ plugkeeper_x_maxw, 0 ]) {
+    polygon([ p_min,
+             p_max,
+             p_max + d,
+             p_min + d ]);
+  }
+}
+
+module PlugKeeperStalkProfile(){
+  hull(){
+    for (m=[0,1]) mirror([0,m,0]) PlugKeeperProfileHalf();
+    translate([ plugkeeper_x_maxw + keeper_stalk_len, 0,0 ])
+      square([ 0.1, keeper_stalk_basewidth ], center=true);
+  }
+}
+
+module PlugKeeper(){
+  for (m=[0,1]) mirror([0,m,0]) {
+      translate([0,0, -keeper_stalk_thick])
+       linear_extrude(height=plug_h + keeper_stalk_thick)
+       PlugKeeperProfileHalf();
+
+      translate([0, 0, plug_h - plug_tooth_h])
+       linear_extrude(height= plug_tooth_h)
+       translate(plugkeeper_d_u * -plug_tooth_dy)
+       PlugKeeperProfileHalf();
+
+    }
+}
+
+module Box(){
+  sidewall_cutout_z = phone[2] + phone_slop[2] + button_dz_outer;
+
+  difference(){
+    union(){
+      BoxMain();
+      Brace();
+    }
+
+    translate([ led[0], phone[1]/2 - led[1], 1 ])
+      rotate([0,0, 360/8/2])
+      cylinder(r = led_dia/2 / cos(360/8/2), h= phone[2]*2, $fn=8);
+
+    for (ys=[-1,+1]) {
+      translate([ -0.1, ys * keeper_stalk_gap, -wall[2]*2])
+       linear_extrude(height = wall[2]*3)
+       PlugKeeperStalkProfile();
+
+      translate([ phone[0] + wall[0],
+                 ys * (cutout_between/2 + cutout_dia/2),
+                 -10 ])
+       cylinder( r= cutout_dia/2, h = 50, $fn = 20 );
+
+      translate([expose, ys*phone[1]/2, sidewall_cutout_z/2])
+       rotate([90,0,0])
+       translate([0,0,-3])
+       cylinder( r= sidewall_cutout_z/2 - 0.1, h=6 , $fn=20 );
+    }
+  }
+
+  PlugKeeper();
+
+  translate([0,0, -keeper_stalk_thick])
+    linear_extrude(height = keeper_stalk_thick)
+    PlugKeeperStalkProfile();
+
+  translate([ plugkeeper_x_maxw + keeper_stalk_len +
+              -keeper_stalk_base_reinforce_len/2,
+              -keeper_stalk_basewidth/2,
+              0 ])
+    mirror([0,0,1])
+    cube([ keeper_stalk_base_reinforce_len,
+          keeper_stalk_basewidth,
+          keeper_stalk_base_reinforce_thick ]);
+}
+
+module BoxPrint(){
+  // This makes' Cura's support more optimal: specifically,
+  // it then doesn't seem to touch the back (bottom) wall
+  translate([0,0,phone[0]])
+    rotate([0,90,0])
+    Box();
+}
+
+//MainProfileInnerHalf();
+//MainProfile();
+//translate([0,0,1]) color("black") BraceProfileInitial();
+//translate([0,0,1]) color("black") BraceProfileFinal();
+//Brace();
+//Box();
+BoxPrint();
diff --git a/slic3r-config.ini b/slic3r-config.ini
new file mode 100644 (file)
index 0000000..c496b91
--- /dev/null
@@ -0,0 +1,98 @@
+# generated by Slic3r 0.9.7 on Fri Dec 18 00:34:03 2015
+acceleration = 0
+bed_size = 140,140
+bed_temperature = 60
+bottom_solid_layers = 2
+bridge_fan_speed = 100
+bridge_flow_ratio = 1
+bridge_speed = 60
+brim_width = 0
+complete_objects = 0
+cooling = 1
+disable_fan_first_layers = 1
+duplicate = 1
+duplicate_distance = 6
+duplicate_grid = 1,1
+end_gcode = M83 ; extruder relative\nG1 F1000 ;1000mm/min extrusion\nG1 E-10; retract\nG1 X0 Y0 F10000\nG28 X0 Y0\nM104 S0 ; turn off temperature\nM140 S0 ; turn off bed\nM84     ; disable motors
+external_perimeter_speed = 100%
+extra_perimeters = 1
+extruder_clearance_height = 20
+extruder_clearance_radius = 20
+extruder_offset = 0x0
+extrusion_axis = E
+extrusion_multiplier = 1.0
+extrusion_width = 0
+fan_always_on = 0
+fan_below_layer_time = 60
+filament_diameter = 1.77
+fill_angle = 45
+fill_density = 0.2
+fill_pattern = rectilinear
+first_layer_bed_temperature = 60
+first_layer_extrusion_width = 200%
+first_layer_height = 0.4
+first_layer_speed = 30%
+first_layer_temperature = 205
+g0 = 0
+gap_fill_speed = 20
+gcode_arcs = 0
+gcode_comments = 0
+gcode_flavor = reprap
+infill_acceleration = 50
+infill_every_layers = 1
+infill_extruder = 1
+infill_extrusion_width = 0
+infill_speed = 60
+layer_gcode = 
+layer_height = 0.4
+max_fan_speed = 100
+min_fan_speed = 35
+min_print_speed = 10
+min_skirt_length = 0
+notes = 
+nozzle_diameter = 0.50
+only_retract_when_crossing_perimeters = 1
+output_filename_format = [input_filename_base].gcode
+perimeter_acceleration = 25
+perimeter_extruder = 1
+perimeter_extrusion_width = 100%
+perimeter_speed = 30
+perimeters = 2
+post_process = 
+print_center = 70,70
+randomize_start = 1
+retract_before_travel = 2
+retract_length = 4.5
+retract_length_toolchange = 3
+retract_lift = 0.1
+retract_restart_extra = 0
+retract_restart_extra_toolchange = 0
+retract_speed = 30
+rotate = 0
+scale = 1
+skirt_distance = 3
+skirt_height = 1
+skirts = 2
+slowdown_below_layer_time = 15
+small_perimeter_speed = 30
+solid_fill_pattern = rectilinear
+solid_infill_below_area = 1
+solid_infill_every_layers = 0
+solid_infill_speed = 60
+start_gcode = G28 ; home all axes
+support_material = 0
+support_material_angle = 0
+support_material_extruder = 1
+support_material_extrusion_width = 0
+support_material_pattern = rectilinear
+support_material_spacing = 2.5
+support_material_speed = 60
+support_material_threshold = 45
+temperature = 205
+threads = 8
+top_solid_infill_speed = 50
+top_solid_layers = 2
+travel_speed = 130
+use_relative_e_distances = 0
+vibration_limit = 0
+z_offset = 0
diff --git a/smallfilamentclip.scad b/smallfilamentclip.scad
new file mode 100644 (file)
index 0000000..724d4cf
--- /dev/null
@@ -0,0 +1,40 @@
+include <cliphook.scad>
+include <filamentteeth.scad>
+
+rad=7.7;
+smrad=2.0;
+h=3.5;
+w=2.5;
+teethw=1.5;
+
+looprad=2.5;
+loopw=w;
+
+fdia=1.77;
+//fdia=3;
+
+d=0.01;
+
+module FilamentClip() {
+  linear_extrude(height=h) {
+    assign($fn=80) {
+      FlatArc(0,0, rad-w/2,rad+w/2, 80,361);
+    }
+    assign($fn=30) {
+      FlatArc(0,rad+looprad+w, looprad,looprad+loopw);
+    }
+    FlatArc(0, rad-smrad, smrad-w/2,smrad+w/2, -55,91);
+    FlatArc(rad-smrad, 0, smrad-w/2,smrad+w/2, 145,-1);
+  }
+
+  for (mir=[0,1]) {
+    mirror([0,mir,0])
+      mirror([1,0,0])
+      rotate([0,0,-56])
+      translate([rad+w*0.3+teethw*0.3+fdia/2 -0.6, -1.1, 0])
+      rotate([0,0,95])
+      FilamentTeeth(fdia=fdia);
+  }
+}
+
+FilamentClip();
diff --git a/splitpin.scad b/splitpin.scad
new file mode 100644 (file)
index 0000000..9631c95
--- /dev/null
@@ -0,0 +1,81 @@
+// -*- C -*-
+
+include <cliphook.scad>
+
+tau = 6.28318530718;
+function deg2rad(deg) = deg/360 * tau;
+function rad2deg(rad) = rad/tau * 360;
+
+module SplitPin(w=1.5, holeminrad=2.50, thick=3, deviationrad=1.5,
+               mainlen=15, handlerad=20, handlelen=12) {
+  spare = holeminrad*2 - deviationrad - w*2;
+  echo("splitpin spare",spare);
+  %translate([0,mainlen+handlelen,0]) cylinder(r=spare, h=thick);
+  %translate([0,mainlen,thick/2]) rotate([90,0,0])
+     cylinder(r=holeminrad, h=thick);
+
+  bent_dx = holeminrad;
+  unbent_dx = bent_dx + deviationrad;
+
+  unbent_subang = atan(unbent_dx / mainlen);
+  unbent_rad = mainlen / deg2rad(unbent_subang);
+
+  corner_x = unbent_rad * (1 - cos(unbent_subang));
+  corner_y = unbent_rad * sin(unbent_subang);
+
+  main_cx = unbent_rad;
+
+//  translate([w*1.5, 0, 0]) {
+//    translate([corner_x, corner_y, 10]) %cube([10,10,10]);
+//    translate([bent_dx, 0, 10]) %cube([10,10,10]);
+//    translate([unbent_dx, 5, 10]) %cube([10,10,10]);
+//  }
+
+  linear_extrude(height=thick) {
+    for (xmir=[0,1]) mirror([xmir,0,0])
+      FlatArc(0,0, w*0.5, w*1.5, 270-1,360);
+    translate([w*1.5, 0, 0]) {
+      FlatArc($fa=1, main_cx,0, unbent_rad, unbent_rad+w,
+             180-unbent_subang, 180);
+      translate([corner_x, corner_y]) rotate([0,0,-unbent_subang]) {
+       rotate([0,0,10])
+         translate([w*0.2,0,0])
+         translate([-(w + deviationrad), -0.1])
+         square(size=[w + deviationrad, w+0.1]);
+       FlatArc(-deviationrad + handlerad, w,
+               handlerad, handlerad+w,
+               180-rad2deg(handlelen/handlerad), 180+rad2deg(w/handlerad),
+               $fa=0.25, $fn=60);
+      }
+    }
+    mirror([1,0,0]) translate([w*1.5, 0, 0])
+      FlatArc($fa=1, main_cx,0, unbent_rad, unbent_rad+w,
+             180-(unbent_subang + rad2deg((handlelen+w)/unbent_rad)), 180);
+  }
+}
+
+module SplitPinCavity(w=1.5, holeminrad=2.50, thick=3, deviationrad=1.5,
+                     mainlen=15, slop=0.5, insertby = 5) {
+  smallgap2 = holeminrad;
+  biggap2 = smallgap2 + deviationrad + slop;
+  toegap2 = w*1.5 + slop;
+  toeend = -mainlen-insertby;
+
+  translate([0,thick/2,0]) rotate([90,0,0]) {
+    linear_extrude(height = thick + slop*2) {
+    for (xmir=[0,1]) mirror([xmir,0]) {
+       polygon([[-0.1, 1],
+                [(smallgap2+biggap2)/2, 1],
+                [smallgap2, -insertby],
+                [biggap2, -insertby],
+                [toegap2, toeend-1],
+                [-0.1, toeend-1]]);
+      }
+    }
+  }
+}
+
+SplitPin();
+translate([0,15+5,-10])
+  rotate([-90,0,0])
+  SplitPinCavity();
diff --git a/sprinkler-spike-receptacle.scad b/sprinkler-spike-receptacle.scad
new file mode 100644 (file)
index 0000000..c1ac3b5
--- /dev/null
@@ -0,0 +1,200 @@
+// -*- C -*-
+
+main_height = 95;
+
+spike_web_thick = 2.52 + 0.75;
+
+spike_top_width = 21.04 + 1.5;
+
+spike_botpos_height = 9.5;
+spike_botpos_width = 11.68 + 0.00;
+
+topwall_width = 1.5;
+
+mount_dist = 20;
+mount_width = 10;
+mount_height = 5;
+mount_hole_dia = 4.5;
+mount_head_dia = 7.5;
+mount_hole_th = 2.5;
+
+strap_height = main_height * 0.5;
+
+strap_width = 5.5;
+strap_thick = 2.5;
+strap_around = 2.5;
+strap_fixing_height = 4.0;
+strap_fixing_slope = 1.0;
+
+// calculated
+
+main_width = spike_top_width + topwall_width*2;
+
+pos_web_thick = spike_web_thick + topwall_width*2;
+
+module NegativePlan(){
+  x4z =
+    (spike_top_width - spike_botpos_width) /
+    (main_height - spike_botpos_height);
+
+  x0 = (spike_botpos_width - x4z * spike_botpos_height)/2;
+  x1 =  spike_top_width/2;
+  z1 = main_height;
+
+  polygon([[ x0, -5],
+          [ x0, 0],
+          [ x1, z1],
+          [ x1, z1+5],
+          [-x1, z1+5],
+          [-x1, z1],
+          [-x0, 0],
+          [-x0, -5]]);
+}
+
+module SomeMidRounding(sq_size, z_extra) {
+  translate([0,0,-z_extra])
+    linear_extrude(height= main_height + z_extra*2)
+    rotate(45)
+    square( sq_size, center=true );
+}
+
+module PositiveMidRounding(){
+  SomeMidRounding(pos_web_thick*2, 0);
+}
+
+module NegativeMidRounding(){
+  SomeMidRounding(spike_web_thick*2.5, 5);
+}
+
+module PositivePlan(){
+  w = main_width;
+  translate([ -w/2, 0 ])
+    square([ w, main_height ]);
+}
+
+module MultiplySolidifyPlan(th){
+  for (r=[0,90]) {
+    rotate([0,0,r])
+      rotate([90,0,0])
+      translate([0,0,-th/2])
+      linear_extrude(height=th)
+      children(0);
+  }
+}
+
+module MultiplyForFixings(){
+  for (r=[0:90:270])
+    rotate([0,0,r])
+    children(0);
+}
+
+module FixingsPositive(){
+  // mount
+  translate([ -1,
+             -mount_width/2,
+             0 ])
+    cube([ mount_dist + mount_width/2 + 1,
+          mount_width,
+          mount_height ]);
+
+  // strap
+  for (m=[0,1]) mirror([0,m,0]) {
+      translate([main_width/2, 0, strap_height]) {
+       hull(){
+         translate([ -strap_around,
+                     -pos_web_thick/2,
+                     -(strap_thick + strap_around) / strap_fixing_slope ])
+           cube([ strap_around,
+                  pos_web_thick/2 - strap_width/2,
+                  0.5 ]);
+         translate([ -strap_around,
+                     -(strap_around + strap_width/2),
+                     0 ])
+           cube([ strap_around*2 + strap_thick,
+                  strap_around,
+                  strap_fixing_height ]);
+       }
+       mirror([0,1,0])
+         translate([ strap_thick,
+                     -strap_width/2,
+                     0 ])
+         cube([ strap_around,
+                strap_around + strap_width,
+                strap_fixing_height ]);
+      }
+    }
+}
+
+module FixingsNegative(){
+  // mount hole
+  translate([ mount_dist, 0,0 ]) {
+    translate([0,0, -1])
+      cylinder(r= mount_hole_dia/2, h= 20, $fn=20);
+    translate([0,0, mount_hole_th])
+      cylinder(r = mount_head_dia/2, h=20, $fn=20);
+  }
+}
+
+module Main(){
+  difference(){
+    union(){
+      MultiplySolidifyPlan(pos_web_thick) PositivePlan();
+      PositiveMidRounding();
+      MultiplyForFixings() FixingsPositive();
+    }
+    MultiplySolidifyPlan(spike_web_thick) NegativePlan();
+    NegativeMidRounding();
+    MultiplyForFixings() FixingsNegative();
+  }
+}
+
+module PlanTest(){
+  linear_extrude(height=2.0){
+    difference(){
+      PositivePlan();
+      NegativePlan();
+    }
+    difference(){
+      circle(r = spike_botpos_width/2 + 5);
+      circle(r = spike_botpos_width/2);
+      translate([-50, 0]) square([100,50]);
+    }
+  }
+  linear_extrude(height=4.0){
+    difference(){
+      translate([ -main_width/2, 0 ]) square([ main_width, 2 ]);
+      NegativePlan();
+    }
+  }
+}
+
+module MainFitTest(){
+  for (top = [0,1]) {
+    translate([ top * (mount_dist*2 + mount_width), 0,0 ]){
+      intersection(){
+       translate([0, 0, (-main_height + 0.5) * top])
+         Main();
+       translate([-50,-50,0])
+         cube([100,100, spike_botpos_height + 1.5]);
+      }
+    }
+  }
+}
+
+module Tests(){
+  translate([-mount_dist*3, 0,0])
+    PlanTest();
+  MainFitTest();
+}
+
+module StrapFixingTest(){
+  intersection(){
+    Main();
+    translate([ -10, -10, 40 ])
+      cube([ 20, 40, 15 ]);
+  }
+}
+
+//Tests();
+//StrapFixingTest();
+Main();
diff --git a/startech-dell-usb-cable-retainer.scad b/startech-dell-usb-cable-retainer.scad
new file mode 100644 (file)
index 0000000..122f76c
--- /dev/null
@@ -0,0 +1,87 @@
+// -*- C -*-
+
+include <utils.scad>
+
+body_depth = 40.15 + 4;
+body_height = 16.78 + 0.50;
+
+back_round_depth = 2.0;
+back_round_rad = 8.0;
+back_above_height = 5.3;
+back_thick = 3.0;
+
+conn_thick = 6.42 + 2.25;
+wire_thick = 6.00 + 0.75;
+total_depth = 63.82 - 1.0;
+
+body_front_overlap = 3;
+
+prong_higher = 1.5;
+prong_depth = 5.0;
+prong_width = 2.0;
+
+base_thick = 4;
+
+$fa= 3;
+$fs= 0.3;
+
+// calculated
+
+epp0 = [ 0, -body_height/2 ];
+epp1 = [ 0, -conn_thick/2 ];
+epp2 = epp1 + [ -body_front_overlap, 0 ];
+epp3 = [ +body_depth -total_depth, epp2[1] ];
+epp4 = [ epp3[0], +conn_thick/2 +prong_higher ];
+epp4a = epp4 + prong_higher * [0,-1];
+epp4b = epp4 + prong_higher * [1,0];
+epp5 = epp4 + [ -prong_depth, 0 ];
+epp6 = [ epp5[0], epp0[1] -base_thick ];
+epp7 = [ epp2[0], epp6[1] ];
+epp12 = [ +body_depth +back_round_rad, 0 ];
+epp11 = [ +body_depth +back_round_depth, epp0[1] ];
+epp10 = [ epp11[0], +back_above_height ];
+epp9 = epp10 + [ +back_thick, 0 ];
+epp8 = [ epp9[0], epp7[1] ];
+  
+y1 = wire_thick/2;
+y2 = y1 + prong_width;
+
+module MainElevation(){
+  polygon([epp0,
+          epp1,
+          epp3,
+          epp4a,
+          epp4b,
+          epp5,
+          epp6,
+          epp8,
+          epp9,
+          epp10,
+          epp11
+          ]);
+  intersection(){
+    translate(epp12) circle(r= back_round_rad);
+    rectfromto(epp8,
+              epp10 + [-back_round_rad, 0]);
+  }
+}
+
+module Retainer(){
+  difference(){
+    linextr_y_xz( -y2, +y2 ) {
+      MainElevation();
+    }
+    linextr_y_xz( -y1, +y1 ) {
+      rectfromto( epp7 + [+1, -1],
+                 epp5 + [-1, +1] );
+    }
+  }
+}
+
+module RetainerPrint(){
+  rotate([0,0,180]) Retainer();
+}
+
+//MainElevation();
+
+RetainerPrint();
diff --git a/steamer-handle-clip.scad b/steamer-handle-clip.scad
new file mode 100644 (file)
index 0000000..5001861
--- /dev/null
@@ -0,0 +1,50 @@
+// -*- C -*-
+
+include <funcs.scad>
+include <utils.scad>
+
+width = 30 - 2;
+cup = 2.5;
+jaw = 32.36 - 2.00 - 2.00 - 3.00;
+th = 3.0;
+l = 15;
+
+a = cup;
+b = width/2;
+alpha = atan2(a, b);
+c = vectorlen2d([a, b]);
+r = a / (1 - cos(2*alpha));
+C = [0, a-r];
+
+$fa = 1;
+
+module HalfBaseline() {
+  intersection(){
+    translate(C + [0, jaw/2])
+      circle(r=r);
+    rectfromto([ -width/2, -1, ],
+              [  width/2, jaw ]);
+  }
+}
+
+module Baseline(){
+  HalfBaseline();
+  mirror([0,1]) HalfBaseline();
+}
+
+module Plan(){
+  difference(){
+    offset(delta=th) Baseline();
+    Baseline();
+    rectfromto([-width, -jaw/2],
+              [0,       jaw/2]);
+  }
+}
+
+module Thing(){
+  linextr(0,l) Plan();
+}
+
+//HalfPlan();
+//Plan();
+Thing();
diff --git a/stringing-test.scad b/stringing-test.scad
new file mode 100644 (file)
index 0000000..a75704d
--- /dev/null
@@ -0,0 +1,15 @@
+// -*- C -*-
+
+height = 15;
+$fn= 20;
+
+cylinder(r=7, h=height);
+
+for (r= [0:4]) {
+  rotate(r * 72) {
+    d = 10 * pow(1.5, r);
+    cube([d, 3, 2]);
+    translate([d, 0,0])
+      cube([7, 7, height]);
+  }
+}
diff --git a/summit-lantern-hook.scad b/summit-lantern-hook.scad
new file mode 100644 (file)
index 0000000..e91573a
--- /dev/null
@@ -0,0 +1,64 @@
+// -*- C -*-
+
+include <utils.scad>
+
+height = 60;
+curl = 10;
+width = 85;
+sides_depth = 50;
+th = 6;
+th2 = 4;
+
+$fa = 3;
+$fs = 0.3;
+
+// calculated
+
+upper_r = th/2;
+upper_ctr_maj_r = curl/2 + upper_r;
+
+zmin = curl/2 + th;
+
+module UpperPlan(){
+  circle(r = upper_r);
+}
+
+module EndCurl(){
+  rotate([90,0,0])
+    rotate_extrude(angle=180)
+    translate([upper_ctr_maj_r, 0])
+    UpperPlan();
+  translate([-upper_ctr_maj_r, 0,0])
+    sphere(r= upper_r);
+}
+
+module Upper(){
+  translate([upper_ctr_maj_r, 0, 0])
+    linextr(-0.1, height + 0.1)
+    UpperPlan();
+  translate([0, 0, height])
+    EndCurl();
+}
+
+module Lower(){
+  rotate([180,0,0])
+    EndCurl();
+  linextr(-zmin, -zmin + th) {
+    square(center=true, [th2, width]);
+    for (m=[0,1])
+      mirror([0,m])
+       hull()
+      {
+       for (x= sides_depth/2 * [-1,+1])
+         translate([ x, width/2 - th2/2 ])
+           circle(r= th2/2);
+      }
+  }
+}
+
+module Hook(){
+  Upper();
+  Lower();
+}
+
+Hook();
diff --git a/svg-prep-dxf b/svg-prep-dxf
new file mode 100755 (executable)
index 0000000..a5ecc38
--- /dev/null
@@ -0,0 +1,178 @@
+#!/bin/bash
+us=svg-prep-dxf
+usage () { cat <<END
+usage: $us [--flatness=VALUE] IN.svg OUT.dxf
+
+Turns a stroked path into its outline path; "union"s it (so that it
+does not self-intersect); approximates its bezier curves with line
+segments; and produces a dxf for openscad.
+END
+}
+# Copyright 2016 Ian Jackson
+#
+# 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.
+#
+# As a special exception, you have permission to link this program
+# with the CGAL library and distribute executables, as long as you
+# follow the requirements of the GNU GPL in regard to all of the
+# software in the executable aside from CGAL.
+#
+# 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.
+
+set -e
+
+fail () { echo >&2 "svg-dxf-prep: $*"; exit 1; }
+badusage () { usage >&2; exit 1; }
+
+flatness=0.01
+# ^ this number seems to work reasonably well
+#   although maybe it should be adjusted?
+
+while true; do
+    case "$1" in
+    --flatness=*)
+       flatness="${1#*=}"
+       ;;
+    -*)
+       badusage
+       ;;
+    *)
+       break
+       ;;
+    esac
+    shift
+done
+
+case "$#" in
+2)  ;;
+*)  badusage ;;
+esac
+
+source=$1
+dest=$2
+
+case "$source" in
+*.svg) ;;
+*)  badusage ;;
+esac
+
+case "$dest" in
+*.dxf) ;;
+*)  badusage ;;
+esac
+
+tmpdir="$(dirname "$dest")/.$(basename "$dest").tmpdir"
+rm -rf -- "$tmpdir"
+mkdir -- "$tmpdir"
+
+case "$tmpdir" in
+/*)  abstmpdir="$tmpdir" ;;
+*)   abstmpdir="$PWD/$tmpdir" ;;
+esac
+
+cp -- "$source" "$tmpdir/t.svg"
+
+# startx seems to send "client" stdout/stderr somewhere hopeless
+exec 4>&2
+
+baseextdir=$(inkscape -x)
+ourid=uk.org.greenend.chiark.ijackson.swirly.flatten
+
+# inkscape doesn't provide a way to specify a per-invocation
+# extension directory, but it does look in $HOME.
+# Anyway, we're going to want a fresh HOME for startx.
+ourxd="$tmpdir"/.config/inkscape/extensions
+mkdir -p "$ourxd"
+mkdir -p "$tmpdir/.local/share" # inkscape complains otherwise, wtf
+
+# Putting the absolute path in the XML doesn't seem to work:
+# inkscape complains that this "dependency" (implicit, apparently)
+# is not satisfied.
+# I'm not sure why this is, but this does work:
+ln -s -- "$baseextdir/flatten.py" "$ourxd"
+
+xmlstarlet \
+ed \
+ -N ie=http://www.inkscape.org/namespace/inkscape/extension \
+ -u "/ie:inkscape-extension/ie:_name" -v "Our Flatten Beziers" \
+ -u "/ie:inkscape-extension/ie:id" -v $ourid \
+ -d '/ie:inkscape-extension/ie:dependency' \
+ -u '/ie:inkscape-extension/ie:param[@name="flatness"]' -v $flatness \
+ "$baseextdir"/flatten.inx \
+ >"$ourxd"/our-flatten.inx
+# known bugs with this approach:
+#  - we rely on the .inx filename
+#  - we rely on flatten.py being in the extensions/ directory
+#    and called exactly that
+#  - we drop the dependencies rather than editing them
+
+cat <<'END' >"$tmpdir/run-inkscape"
+#!/bin/sh
+exec >&4 2>&4
+echo
+printf "running inkscape to transform and clean up..."
+cd "$HOME"
+inkscape \
+ --with-gui \
+ t.svg \
+ --verb=ToolNode \
+ --verb=EditSelectAll \
+ --verb=SelectionUnGroup \
+ --verb=EditSelectAll \
+ --verb=StrokeToPath \
+ --verb=ToolNode \
+ --verb=EditSelectAll \
+ --verb=SelectionUnion \
+ --verb=EditSelectAll \
+ --verb=uk.org.greenend.chiark.ijackson.swirly.flatten.noprefs \
+ --verb=FileSave \
+ --verb=FileQuit \
+
+echo done.
+echo
+END
+chmod +x "$tmpdir"/run-inkscape
+
+cat <<END
+
+Expect some complaints from xinit, for example
+  about XFree86_VT property
+  lost connection
+  file descriptors for console
+END
+
+HOME="$abstmpdir" \
+/usr/bin/startx \
+ "$abstmpdir"/run-inkscape \
+ -- \
+ `type -p Xvfb`
+
+# It's awkward to get inkscape to save as DXF noninteractively.
+# And inkscape doesn't seem to like to exit if I try to export
+# from the "interactive" one.
+
+# But anyway inkscape DXF is just made with pstoedit.  So save as SVG,
+# and then do a separate inkscape run to convert EPS, and run pstoedit
+# to make the DXF.
+
+inkscape --export-area-page \
+ -f "$tmpdir"/t.svg \
+ -E "$tmpdir"/t.eps \
+
+pstoedit -dt -f 'dxf:-polyaslines -mm' \
+ "$tmpdir"/t.eps \
+ "$tmpdir/"t.dxf
+
+mv -- "$tmpdir"/t.dxf "$dest"
+
+echo
+printf "$us: success, wrote %s\n" "$dest"
+echo
+
+exit 0
diff --git a/tablet-case-corner-mount.scad b/tablet-case-corner-mount.scad
new file mode 100644 (file)
index 0000000..d981afd
--- /dev/null
@@ -0,0 +1,86 @@
+// -*- C -*-
+
+main_sz = 22.5;
+
+wall_th = 3;
+
+front_hook = 3;
+front_slope = 1.0;
+
+front_th = 2;
+gap_th = 6.5 + 0.5;
+
+back_tot_l = 35.5;
+back_cut_l = 6;
+back_cut_w = 15.0 + 1.0;
+back_prong_w = 3;
+back_hole_d = 2;
+back_hole_dia = 1 + 1.5;
+
+back_th = back_hole_dia + 2.4;
+
+module MidPlan(){
+  polygon([[0,            0],
+          [0,            main_sz],
+          [wall_th,      main_sz],
+          [wall_th,      wall_th],
+          [main_sz,      wall_th],
+          [main_sz,      0]]);
+}
+
+module FrontElevation(){
+  hook_z = front_hook / front_slope;
+  translate([0, back_th+gap_th]) {
+    hull(){
+      square([wall_th, hook_z + 0.01]);
+      translate([front_hook, hook_z])
+       square([wall_th, 0.01]);
+    }
+  }
+}
+
+module FrontEdge(){
+  rotate([90,0,0]) linear_extrude(height=main_sz) FrontElevation();
+}
+
+module Front(){
+  mirror([0,1,0]) FrontEdge();
+  rotate([0,0,90]) FrontEdge();
+}
+
+module BackPlan(){
+  sqmid = main_sz * sqrt(0.5);
+  prlen = back_tot_l - sqmid;
+  prx = 0.5*back_cut_w + back_prong_w;
+  difference(){
+    union(){
+      square(main_sz);
+      rotate(-45) translate([-prx, sqmid])
+       square([prx*2,prlen]);
+    }
+    rotate(-45) translate([-back_cut_w/2, back_tot_l-back_cut_l])
+      square([back_cut_w, back_cut_l+1]);
+  }
+}
+
+module Hook(){
+  difference(){
+    union(){
+      linear_extrude(height=back_th)
+       BackPlan();
+      linear_extrude(height=back_th+gap_th+front_th)
+       MidPlan();
+      Front();
+    }
+    rotate([0,0,-45])
+      translate([0, back_tot_l - back_hole_d, back_th/2])
+      rotate([0,90,0]) translate([0,0,-50])
+      cylinder(h=100, r=back_hole_dia/2, $fn=40);
+  }
+}
+
+//MidPlan();
+//FrontPlan();
+//BackPlan();
+//Front();
+Hook();
diff --git a/tablet-stand.scad b/tablet-stand.scad
new file mode 100644 (file)
index 0000000..d7ef564
--- /dev/null
@@ -0,0 +1,72 @@
+// -*- C -*-
+
+whole_depth = 90;
+whole_width = 120;
+
+antifoot_width = 15;
+antifoot_height = 15;
+antifoot_slope = 1.0;
+antifoot_depth = 10;
+antifoot_base = 12;
+antifoot_front = 5;
+
+leg_width = 8;
+leg_thick = 8;
+
+post_height = 50;
+orifice_dia = 22.1 + 0.3;
+post_thick = 8;
+
+stretcher_thick = 5;
+stretcher_width = 8;
+
+antifoot_back = antifoot_depth + antifoot_height/antifoot_slope;
+post_rad = orifice_dia/2 + post_thick;
+
+module AntiFoot(){
+  translate([-antifoot_front-antifoot_back, antifoot_width/2, 0])
+    rotate([90,0,0])
+    translate([antifoot_front, antifoot_base, 0])
+    linear_extrude(height=antifoot_width)
+    polygon([[-antifoot_front, -antifoot_base],
+          [-antifoot_front,  antifoot_height],
+          [0,               antifoot_height],
+          [0,               0],
+          [antifoot_depth,  0],
+          [antifoot_back, antifoot_height],
+          [antifoot_back, -antifoot_base]]);
+  translate([-antifoot_back, 0, 0])
+    cube([stretcher_width, whole_width*0.55, stretcher_width]);
+}
+
+module LeftLeg(){
+  effective_depth = whole_depth - antifoot_back;
+  translate([-effective_depth, -whole_width/2, 0])
+    AntiFoot();
+  hull(){
+    translate([-effective_depth-leg_width/2, -whole_width/2, 0])
+      cylinder(r=leg_width/2, h=antifoot_base);
+    cylinder(r=leg_width/2, h=post_height);
+  }
+}
+
+module RightLeg(){
+  mirror([0,1,0]) LeftLeg();
+}
+
+module Post(){
+  cylinder(h=post_height, r=post_rad, $fn=70);
+}
+
+module Stand(){
+  difference(){
+    union(){
+      LeftLeg();
+      RightLeg();
+      Post();
+    }
+    translate([0,0,-1]) cylinder(h=post_height+2, r=orifice_dia/2);
+  }
+}
+
+Stand();
diff --git a/test-cup.scad b/test-cup.scad
new file mode 100644 (file)
index 0000000..5bb826a
--- /dev/null
@@ -0,0 +1,12 @@
+// -*- C -*-
+
+radius = 25/2;
+wall = 1.0;
+height = 40;
+floor = 1.0;
+
+difference(){
+  cylinder(r=radius+wall, h=height);
+  translate([0,0, floor])
+    cylinder(r=radius, h=height);
+}
diff --git a/test-object.scad b/test-object.scad
new file mode 100644 (file)
index 0000000..359254c
--- /dev/null
@@ -0,0 +1,6 @@
+size=1.7;
+scale([size,size,size])
+difference() {
+    cube(size=[10,10,4.0], center=true);
+    translate([0,0,2.0]) sphere(r=4.5, $fn=50);
+}
diff --git a/thread-external-test.scad b/thread-external-test.scad
new file mode 100644 (file)
index 0000000..6899632
--- /dev/null
@@ -0,0 +1,33 @@
+// -*- C -*-
+
+include <threads.scad>
+include <utils.scad>
+
+// https://en.wikipedia.org/wiki/ISO_metric_screw_thread
+
+// M6
+thread_nom = 6;
+thread_pitch = 1.00;
+thread_act = thread_nom - 0.300;
+head_size = 10;
+
+thread_len = 12.5;
+base_th = 1.5;
+
+$test = false;
+
+// calculated
+
+base_dia = head_size / cos(30);
+
+module MachineScrew(){
+  translate([0, 0, -0.1])
+    render()
+    metric_thread(diameter=thread_act, pitch=thread_pitch,
+                 leadin=1, internal=false,
+                 test=$test, length=thread_len + 0.1);
+  linextr(-base_th, 0)
+    circle(r= base_dia/2, $fn=6);
+}
+
+MachineScrew();
diff --git a/thread-internal-test.scad b/thread-internal-test.scad
new file mode 100644 (file)
index 0000000..cab40b6
--- /dev/null
@@ -0,0 +1,46 @@
+// -*- C -*-
+
+include <threads.scad>
+include <utils.scad>
+
+// https://en.wikipedia.org/wiki/ISO_metric_screw_thread
+
+// M6
+thread_nom = 4;
+thread_pitch = 0.70;
+thread_act = thread_nom + 0.375;
+head_size = 10;
+
+thread_len = 12.5;
+base_th = 1.5;
+base_sz = [40, head_size];
+
+$test = false;
+
+// calculated
+
+base_dia = head_size / cos(30);
+
+module ScrewThread(){
+  translate([0, 0, -0.1])
+    render()
+    metric_thread(diameter=thread_act, pitch=thread_pitch,
+                 leadin=1, internal=true,
+                 test=$test, length=thread_len + 0.1);
+}
+
+module TestThread(){
+  difference(){
+    union(){
+      linextr(-base_th, 0)
+       square(center=true, base_sz);
+
+      linextr(-base_th, thread_len - 0.1)
+       circle(r= base_dia/2, $fn=6);
+    }
+
+    ScrewThread();
+  }
+}
+
+TestThread();
diff --git a/threads.scad b/threads.scad
new file mode 100644 (file)
index 0000000..b2eee23
--- /dev/null
@@ -0,0 +1,407 @@
+/*\r
+ * ISO-standard metric threads, following this specification:\r
+ *          http://en.wikipedia.org/wiki/ISO_metric_screw_thread\r
+ *\r
+ * Copyright 2020 Dan Kirshner - dan_kirshner@yahoo.com\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation, either version 3 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * See <http://www.gnu.org/licenses/>.\r
+ *\r
+ * Version 2.5.  2020-04-11  Leadin option works for internal threads.\r
+ * Version 2.4.  2019-07-14  Add test option - do not render threads.\r
+ * Version 2.3.  2017-08-31  Default for leadin: 0 (best for internal threads).\r
+ * Version 2.2.  2017-01-01  Correction for angle; leadfac option.  (Thanks to\r
+ *                           Andrew Allen <a2intl@gmail.com>.)\r
+ * Version 2.1.  2016-12-04  Chamfer bottom end (low-z); leadin option.\r
+ * Version 2.0.  2016-11-05  Backwards compatibility (earlier OpenSCAD) fixes.\r
+ * Version 1.9.  2016-07-03  Option: tapered.\r
+ * Version 1.8.  2016-01-08  Option: (non-standard) angle.\r
+ * Version 1.7.  2015-11-28  Larger x-increment - for small-diameters.\r
+ * Version 1.6.  2015-09-01  Options: square threads, rectangular threads.\r
+ * Version 1.5.  2015-06-12  Options: thread_size, groove.\r
+ * Version 1.4.  2014-10-17  Use "faces" instead of "triangles" for polyhedron\r
+ * Version 1.3.  2013-12-01  Correct loop over turns -- don't have early cut-off\r
+ * Version 1.2.  2012-09-09  Use discrete polyhedra rather than linear_extrude ()\r
+ * Version 1.1.  2012-09-07  Corrected to right-hand threads!\r
+ */\r
+\r
+// Examples.\r
+//\r
+// Standard M8 x 1.\r
+// metric_thread (diameter=8, pitch=1, length=4);\r
+\r
+// Square thread.\r
+// metric_thread (diameter=8, pitch=1, length=4, square=true);\r
+\r
+// Non-standard: long pitch, same thread size.\r
+//metric_thread (diameter=8, pitch=4, length=4, thread_size=1, groove=true);\r
+\r
+// Non-standard: 20 mm diameter, long pitch, square "trough" width 3 mm,\r
+// depth 1 mm.\r
+//metric_thread (diameter=20, pitch=8, length=16, square=true, thread_size=6,\r
+//               groove=true, rectangle=0.333);\r
+\r
+// English: 1/4 x 20.\r
+//english_thread (diameter=1/4, threads_per_inch=20, length=1);\r
+\r
+// Tapered.  Example -- pipe size 3/4" -- per:\r
+// http://www.engineeringtoolbox.com/npt-national-pipe-taper-threads-d_750.html\r
+// english_thread (diameter=1.05, threads_per_inch=14, length=3/4, taper=1/16);\r
+\r
+// Thread for mounting on Rohloff hub.\r
+//difference () {\r
+//   cylinder (r=20, h=10, $fn=100);\r
+//\r
+//   metric_thread (diameter=34, pitch=1, length=10, internal=true, n_starts=6);\r
+//}\r
+\r
+\r
+// ----------------------------------------------------------------------------\r
+function segments (diameter) = min (50, max (ceil (diameter*6), 25));\r
+\r
+\r
+// ----------------------------------------------------------------------------\r
+// diameter -    outside diameter of threads in mm. Default: 8.\r
+// pitch    -    thread axial "travel" per turn in mm.  Default: 1.\r
+// length   -    overall axial length of thread in mm.  Default: 1.\r
+// internal -    true = clearances for internal thread (e.g., a nut).\r
+//               false = clearances for external thread (e.g., a bolt).\r
+//               (Internal threads should be "cut out" from a solid using\r
+//               difference ()).  Default: false.\r
+// n_starts -    Number of thread starts (e.g., DNA, a "double helix," has\r
+//               n_starts=2).  See wikipedia Screw_thread.  Default: 1.\r
+// thread_size - (non-standard) axial width of a single thread "V" - independent\r
+//               of pitch.  Default: same as pitch.\r
+// groove      - (non-standard) true = subtract inverted "V" from cylinder\r
+//                (rather thanadd protruding "V" to cylinder).  Default: false.\r
+// square      - true = square threads (per\r
+//               https://en.wikipedia.org/wiki/Square_thread_form).  Default:\r
+//               false.\r
+// rectangle   - (non-standard) "Rectangular" thread - ratio depth/(axial) width\r
+//               Default: 0 (standard "v" thread).\r
+// angle       - (non-standard) angle (deg) of thread side from perpendicular to\r
+//               axis (default = standard = 30 degrees).\r
+// taper       - diameter change per length (National Pipe Thread/ANSI B1.20.1\r
+//               is 1" diameter per 16" length). Taper decreases from 'diameter'\r
+//               as z increases.  Default: 0 (no taper).\r
+// leadin      - 0 (default): no chamfer; 1: chamfer (45 degree) at max-z end;\r
+//               2: chamfer at both ends, 3: chamfer at z=0 end.\r
+// leadfac     - scale of leadin chamfer length (default: 1.0 = 1/2 thread).\r
+// test        - true = do not render threads (just draw "blank" cylinder).\r
+//               Default: false (draw threads).\r
+module metric_thread (diameter=8, pitch=1, length=1, internal=false, n_starts=1,\r
+                      thread_size=-1, groove=false, square=false, rectangle=0,\r
+                      angle=30, taper=0, leadin=0, leadfac=1.0, test=false)\r
+{\r
+   // thread_size: size of thread "V" different than travel per turn (pitch).\r
+   // Default: same as pitch.\r
+   local_thread_size = thread_size == -1 ? pitch : thread_size;\r
+   local_rectangle = rectangle ? rectangle : 1;\r
+\r
+   n_segments = segments (diameter);\r
+   h = (test && ! internal) ? 0 : (square || rectangle) ? local_thread_size*local_rectangle/2 : local_thread_size / (2 * tan(angle));\r
+\r
+   h_fac1 = (square || rectangle) ? 0.90 : 0.625;\r
+\r
+   // External thread includes additional relief.\r
+   h_fac2 = (square || rectangle) ? 0.95 : 5.3/8;\r
+\r
+   tapered_diameter = diameter - length*taper;\r
+\r
+   difference () {\r
+      union () {\r
+         if (! groove) {\r
+            if (! test) {\r
+               metric_thread_turns (diameter, pitch, length, internal, n_starts,\r
+                                    local_thread_size, groove, square, rectangle, angle,\r
+                                    taper);\r
+            }\r
+         }\r
+\r
+         difference () {\r
+\r
+            // Solid center, including Dmin truncation.\r
+            if (groove) {\r
+               cylinder (r1=diameter/2, r2=tapered_diameter/2,\r
+                         h=length, $fn=n_segments);\r
+            } else if (internal) {\r
+               cylinder (r1=diameter/2 - h*h_fac1, r2=tapered_diameter/2 - h*h_fac1,\r
+                         h=length, $fn=n_segments);\r
+            } else {\r
+\r
+               // External thread.\r
+               cylinder (r1=diameter/2 - h*h_fac2, r2=tapered_diameter/2 - h*h_fac2,\r
+                         h=length, $fn=n_segments);\r
+            }\r
+\r
+            if (groove) {\r
+               if (! test) {\r
+                  metric_thread_turns (diameter, pitch, length, internal, n_starts,\r
+                                       local_thread_size, groove, square, rectangle,\r
+                                       angle, taper);\r
+               }\r
+            }\r
+         }\r
+\r
+         // Internal thread lead-in: take away from external solid.\r
+         if (internal) {\r
+\r
+            // "Negative chamfer" z=0 end if leadin is 2 or 3.\r
+            if (leadin == 2 || leadin == 3) {\r
+               cylinder (r1=diameter/2, r2=diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac,\r
+                         $fn=n_segments);\r
+            }\r
+\r
+            // "Negative chamfer" z-max end if leadin is 1 or 2.\r
+            if (leadin == 1 || leadin == 2) {\r
+               translate ([0, 0, length + 0.05 - h*h_fac1*leadfac]) {\r
+                  cylinder (r1=tapered_diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac,\r
+                            r2=tapered_diameter/2,\r
+                            $fn=n_segments);\r
+               }\r
+            }\r
+         }\r
+      }\r
+\r
+      if (! internal) {\r
+\r
+         // Chamfer z=0 end if leadin is 2 or 3.\r
+         if (leadin == 2 || leadin == 3) {\r
+            difference () {\r
+               cylinder (r=diameter/2 + 1, h=h*h_fac1*leadfac, $fn=n_segments);\r
+\r
+               cylinder (r2=diameter/2, r1=diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac,\r
+                         $fn=n_segments);\r
+            }\r
+         }\r
+\r
+         // Chamfer z-max end if leadin is 1 or 2.\r
+         if (leadin == 1 || leadin == 2) {\r
+            translate ([0, 0, length + 0.05 - h*h_fac1*leadfac]) {\r
+               difference () {\r
+                  cylinder (r=diameter/2 + 1, h=h*h_fac1*leadfac, $fn=n_segments);\r
+\r
+                  cylinder (r1=tapered_diameter/2, r2=tapered_diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac,\r
+                            $fn=n_segments);\r
+               }\r
+            }\r
+         }\r
+      }\r
+   }\r
+}\r
+\r
+\r
+// ----------------------------------------------------------------------------\r
+// Input units in inches.\r
+// Note: units of measure in drawing are mm!\r
+module english_thread (diameter=0.25, threads_per_inch=20, length=1,\r
+                      internal=false, n_starts=1, thread_size=-1, groove=false,\r
+                      square=false, rectangle=0, angle=30, taper=0, leadin=0,\r
+                      leadfac=1.0, test=false)\r
+{\r
+   // Convert to mm.\r
+   mm_diameter = diameter*25.4;\r
+   mm_pitch = (1.0/threads_per_inch)*25.4;\r
+   mm_length = length*25.4;\r
+\r
+   echo (str ("mm_diameter: ", mm_diameter));\r
+   echo (str ("mm_pitch: ", mm_pitch));\r
+   echo (str ("mm_length: ", mm_length));\r
+   metric_thread (mm_diameter, mm_pitch, mm_length, internal, n_starts,\r
+                  thread_size, groove, square, rectangle, angle, taper, leadin,\r
+                  leadfac, test);\r
+}\r
+\r
+// ----------------------------------------------------------------------------\r
+module metric_thread_turns (diameter, pitch, length, internal, n_starts,\r
+                            thread_size, groove, square, rectangle, angle,\r
+                            taper)\r
+{\r
+   // Number of turns needed.\r
+   n_turns = floor (length/pitch);\r
+\r
+   intersection () {\r
+\r
+      // Start one below z = 0.  Gives an extra turn at each end.\r
+      for (i=[-1*n_starts : n_turns+1]) {\r
+         translate ([0, 0, i*pitch]) {\r
+            metric_thread_turn (diameter, pitch, internal, n_starts,\r
+                                thread_size, groove, square, rectangle, angle,\r
+                                taper, i*pitch);\r
+         }\r
+      }\r
+\r
+      // Cut to length.\r
+      translate ([0, 0, length/2]) {\r
+         cube ([diameter*3, diameter*3, length], center=true);\r
+      }\r
+   }\r
+}\r
+\r
+\r
+// ----------------------------------------------------------------------------\r
+module metric_thread_turn (diameter, pitch, internal, n_starts, thread_size,\r
+                           groove, square, rectangle, angle, taper, z)\r
+{\r
+   n_segments = segments (diameter);\r
+   fraction_circle = 1.0/n_segments;\r
+   for (i=[0 : n_segments-1]) {\r
+      rotate ([0, 0, i*360*fraction_circle]) {\r
+         translate ([0, 0, i*n_starts*pitch*fraction_circle]) {\r
+            //current_diameter = diameter - taper*(z + i*n_starts*pitch*fraction_circle);\r
+            thread_polyhedron ((diameter - taper*(z + i*n_starts*pitch*fraction_circle))/2,\r
+                               pitch, internal, n_starts, thread_size, groove,\r
+                               square, rectangle, angle);\r
+         }\r
+      }\r
+   }\r
+}\r
+\r
+\r
+// ----------------------------------------------------------------------------\r
+module thread_polyhedron (radius, pitch, internal, n_starts, thread_size,\r
+                          groove, square, rectangle, angle)\r
+{\r
+   n_segments = segments (radius*2);\r
+   fraction_circle = 1.0/n_segments;\r
+\r
+   local_rectangle = rectangle ? rectangle : 1;\r
+\r
+   h = (square || rectangle) ? thread_size*local_rectangle/2 : thread_size / (2 * tan(angle));\r
+   outer_r = radius + (internal ? h/20 : 0); // Adds internal relief.\r
+   //echo (str ("outer_r: ", outer_r));\r
+\r
+   // A little extra on square thread -- make sure overlaps cylinder.\r
+   h_fac1 = (square || rectangle) ? 1.1 : 0.875;\r
+   inner_r = radius - h*h_fac1; // Does NOT do Dmin_truncation - do later with\r
+                                // cylinder.\r
+\r
+   translate_y = groove ? outer_r + inner_r : 0;\r
+   reflect_x   = groove ? 1 : 0;\r
+\r
+   // Make these just slightly bigger (keep in proportion) so polyhedra will\r
+   // overlap.\r
+   x_incr_outer = (! groove ? outer_r : inner_r) * fraction_circle * 2 * PI * 1.02;\r
+   x_incr_inner = (! groove ? inner_r : outer_r) * fraction_circle * 2 * PI * 1.02;\r
+   z_incr = n_starts * pitch * fraction_circle * 1.005;\r
+\r
+   /*\r
+    (angles x0 and x3 inner are actually 60 deg)\r
+\r
+                          /\  (x2_inner, z2_inner) [2]\r
+                         /  \\r
+   (x3_inner, z3_inner) /    \\r
+                  [3]   \     \\r
+                        |\     \ (x2_outer, z2_outer) [6]\r
+                        | \    /\r
+                        |  \  /|\r
+             z          |[7]\/ / (x1_outer, z1_outer) [5]\r
+             |          |   | /\r
+             |   x      |   |/\r
+             |  /       |   / (x0_outer, z0_outer) [4]\r
+             | /        |  /     (behind: (x1_inner, z1_inner) [1]\r
+             |/         | /\r
+    y________|          |/\r
+   (r)                  / (x0_inner, z0_inner) [0]\r
+\r
+   */\r
+\r
+   x1_outer = outer_r * fraction_circle * 2 * PI;\r
+\r
+   z0_outer = (outer_r - inner_r) * tan(angle);\r
+   //echo (str ("z0_outer: ", z0_outer));\r
+\r
+   //polygon ([[inner_r, 0], [outer_r, z0_outer],\r
+   //        [outer_r, 0.5*pitch], [inner_r, 0.5*pitch]]);\r
+   z1_outer = z0_outer + z_incr;\r
+\r
+   // Give internal square threads some clearance in the z direction, too.\r
+   bottom = internal ? 0.235 : 0.25;\r
+   top    = internal ? 0.765 : 0.75;\r
+\r
+   translate ([0, translate_y, 0]) {\r
+      mirror ([reflect_x, 0, 0]) {\r
+\r
+         if (square || rectangle) {\r
+\r
+            // Rule for face ordering: look at polyhedron from outside: points must\r
+            // be in clockwise order.\r
+            polyhedron (\r
+               points = [\r
+                         [-x_incr_inner/2, -inner_r, bottom*thread_size],         // [0]\r
+                         [x_incr_inner/2, -inner_r, bottom*thread_size + z_incr], // [1]\r
+                         [x_incr_inner/2, -inner_r, top*thread_size + z_incr],    // [2]\r
+                         [-x_incr_inner/2, -inner_r, top*thread_size],            // [3]\r
+\r
+                         [-x_incr_outer/2, -outer_r, bottom*thread_size],         // [4]\r
+                         [x_incr_outer/2, -outer_r, bottom*thread_size + z_incr], // [5]\r
+                         [x_incr_outer/2, -outer_r, top*thread_size + z_incr],    // [6]\r
+                         [-x_incr_outer/2, -outer_r, top*thread_size]             // [7]\r
+                        ],\r
+\r
+               faces = [\r
+                         [0, 3, 7, 4],  // This-side trapezoid\r
+\r
+                         [1, 5, 6, 2],  // Back-side trapezoid\r
+\r
+                         [0, 1, 2, 3],  // Inner rectangle\r
+\r
+                         [4, 7, 6, 5],  // Outer rectangle\r
+\r
+                         // These are not planar, so do with separate triangles.\r
+                         [7, 2, 6],     // Upper rectangle, bottom\r
+                         [7, 3, 2],     // Upper rectangle, top\r
+\r
+                         [0, 5, 1],     // Lower rectangle, bottom\r
+                         [0, 4, 5]      // Lower rectangle, top\r
+                        ]\r
+            );\r
+         } else {\r
+\r
+            // Rule for face ordering: look at polyhedron from outside: points must\r
+            // be in clockwise order.\r
+            polyhedron (\r
+               points = [\r
+                         [-x_incr_inner/2, -inner_r, 0],                        // [0]\r
+                         [x_incr_inner/2, -inner_r, z_incr],                    // [1]\r
+                         [x_incr_inner/2, -inner_r, thread_size + z_incr],      // [2]\r
+                         [-x_incr_inner/2, -inner_r, thread_size],              // [3]\r
+\r
+                         [-x_incr_outer/2, -outer_r, z0_outer],                 // [4]\r
+                         [x_incr_outer/2, -outer_r, z0_outer + z_incr],         // [5]\r
+                         [x_incr_outer/2, -outer_r, thread_size - z0_outer + z_incr], // [6]\r
+                         [-x_incr_outer/2, -outer_r, thread_size - z0_outer]    // [7]\r
+                        ],\r
+\r
+               faces = [\r
+                         [0, 3, 7, 4],  // This-side trapezoid\r
+\r
+                         [1, 5, 6, 2],  // Back-side trapezoid\r
+\r
+                         [0, 1, 2, 3],  // Inner rectangle\r
+\r
+                         [4, 7, 6, 5],  // Outer rectangle\r
+\r
+                         // These are not planar, so do with separate triangles.\r
+                         [7, 2, 6],     // Upper rectangle, bottom\r
+                         [7, 3, 2],     // Upper rectangle, top\r
+\r
+                         [0, 5, 1],     // Lower rectangle, bottom\r
+                         [0, 4, 5]      // Lower rectangle, top\r
+                        ]\r
+            );\r
+         }\r
+      }\r
+   }\r
+}\r
+\r
+\r
+\r
diff --git a/topeak-mtx-tortec-expeditionrack-adapter.scad b/topeak-mtx-tortec-expeditionrack-adapter.scad
new file mode 100644 (file)
index 0000000..c751f78
--- /dev/null
@@ -0,0 +1,502 @@
+// -*- C -*-
+
+// brk_*: "bracket", the Topeak MTX bracket
+// rack_*: the Tortec rack
+// adapt_*: the adapter, ie this file
+
+include <utils.scad>
+
+// strength factor, set to 1 for real prints
+//$strf = 0.33;
+$strf = 1;
+
+brk_recess_actual = 5.20;
+
+rack_rail_dia = 10.40 + 0.30;
+rack_width_inner = 115.86 - 1.0; // between insides of rails
+
+rear_elevation_nominal = 10.04;
+// ^ top of rack to bottom of bracket, at rack cross rail (fam)
+rear_to_front_distance = 230; // rack cross rail (fam) to very front end
+rear_to_cross_rail = 43.05; // bolt centre to rail centre, rail to rear
+rear_bolt_to_front_bolt = 155.4;
+front_elevation_nominal = 0; // this parameter adjusts rear too somehow?
+
+cross_rail_distance = 232.09;
+
+general_gap_y = 1.0;
+support_bridge_gap_z = 1.0;
+
+strap_w = 8.0 + 1.0;
+strap_th = 2.5;
+strap_barrel_dia = 14;
+strap_guide_sz = 1;
+
+brk_block_xw = 68.5;
+brk_block_z = 14.55 - 0.00;
+
+brk_bolt_dia = 5.0 + 0.5;
+brk_nearbolt_recess_dia = 8.86 + 1.5;
+brk_nearbolt_recess_depth = 1.09 + 0.25;
+
+bolt_nut_around = 5;
+bolt_nut_around_y_extra = 3;
+
+brk_bolt_eff_len = 11.78; // inside of recess, to end of bolt
+brk_bolt_len_slop = 0.5;
+brk_bolt_nut_th = 3.89;
+brk_bolt_nut_across_flats = 7.86 + 0.50;
+
+brk_overall_w = 90.07;
+
+fit_slope_len = 5;
+
+// "foreaftmaint" aka "fam" is the hook-like part that stops
+// the adapter sliding forwards/backwards along the rails
+foreaftmaint_r_slop = 0.0;
+foreaftmaint_y_slop = -0.25;
+foreaftmaint_top_block_zs = [34.0, 39.0]; // rearwards from bolt hole
+
+main_sz_y = $strf * 12;
+grasp_sz = $strf * 6;
+grasp_thin_sz = $strf * 0.5;
+beside_strap_sz = $strf * 8;
+main_sz_core_z = $strf * 12;
+
+// "length" in for-aft direction of interaction with rack rail
+min_on_rail_sz_z = $strf * 18;
+
+// when printer produces support
+support_around = 1.7; // how far does the support extend around (in XY)
+support_remnant = 0.75; // how much frass remains attached (Z height)
+
+$fa=10;
+$fs=1;
+
+// calculated
+
+bolt_z = -brk_block_z/2;
+
+front_to_rear_elevation_change =
+  rear_elevation_nominal - front_elevation_nominal;
+
+main_sz_rhs_z = max(min_on_rail_sz_z, beside_strap_sz*2 + strap_w);
+main_sz_lhs_z = min_on_rail_sz_z;
+
+main_sz_x_fam = main_sz_y;
+
+brk_bottom_y = -brk_recess_actual;
+adapt_main_top_y = brk_bottom_y - general_gap_y;
+
+// on LHS, so -ve
+rack_rail_x = -(rack_width_inner/2 + rack_rail_dia/2);
+rack_rail_outer_x = -(rack_width_inner/2 + rack_rail_dia);
+
+grasp_large_r = (rack_rail_dia + grasp_sz)/2;
+grasp_small_r = (rack_rail_dia + grasp_thin_sz)/2;
+grasp_large_x = rack_rail_outer_x + grasp_large_r;
+grasp_small_x = rack_rail_outer_x + grasp_small_r;
+
+block_x = grasp_large_x + grasp_large_r;
+block_y_min = adapt_main_top_y - main_sz_y;
+
+strap_barrel_x = rack_width_inner/2 + strap_barrel_dia/2;
+
+rack_shear_ratio = - front_to_rear_elevation_change / rear_to_front_distance;
+
+front_to_cross_rail =
+  cross_rail_distance * sqrt(1 - rack_shear_ratio * rack_shear_ratio)
+  - rear_bolt_to_front_bolt
+  - rear_to_cross_rail
+  - sqrt( pow( cross_rail_distance * rack_shear_ratio, 2 )
+         - pow(  front_to_rear_elevation_change, 2 ) )
+  ;
+
+brk_bolt_nut_top_y = -brk_nearbolt_recess_depth
+  - brk_bolt_eff_len + brk_bolt_nut_th + brk_bolt_len_slop;
+                      
+brk_bolt_nut_r = brk_bolt_nut_across_flats/2 / cos(360/12);
+
+function elevation_of_bolt_for(z) = rear_elevation_nominal
+  + front_elevation_nominal
+  + (z - brk_block_z/2) * rack_shear_ratio;
+
+function rack_rail_y_of_elevation(elevation_nominal) =
+  brk_bottom_y - elevation_nominal - general_gap_y  - rack_rail_dia/2;
+
+echo(rack_shear_ratio);
+
+module GraspElevation(){
+  hull(){
+    translate([ grasp_large_x, adapt_main_top_y - grasp_large_r ])
+      circle(grasp_large_r);
+
+    translate([ grasp_small_x, $rack_rail_y - rack_rail_dia/2 ])
+      circle(grasp_small_r);
+
+    translate([ rack_rail_x + grasp_large_r/2,
+               $rack_rail_y - rack_rail_dia/2 ])
+      circle(grasp_small_r);
+
+    translate([ grasp_large_x, $rack_rail_y + rack_rail_dia/2 ])
+      circle(grasp_large_r);
+
+    translate([ grasp_large_x + grasp_large_r/2,
+               $rack_rail_y + rack_rail_dia/2 ])
+      circle(grasp_large_r);
+  }
+}
+
+module BlockElevation(){
+  hull(){
+    rectfromto([ +block_x, adapt_main_top_y ],
+              [ -block_x, block_y_min ]);
+    rectfromto([ -grasp_large_x, adapt_main_top_y ],
+              [ +grasp_large_x, adapt_main_top_y - 0.1 ]);
+  }
+  hull(){
+    rectfromto([ +block_x, adapt_main_top_y ],
+              [ -block_x, block_y_min ]);
+    rectfromto([ grasp_large_x, block_y_min ],
+              [ 0, block_y_min + 0.1 ]);
+  }
+}
+
+module MainExtrude(z){
+  linextr(0, z)
+    children();
+}
+module RackShear(){
+  s = rack_shear_ratio * $reverse_sign;
+  multmatrix([ [ 1, 0,  0, 0 ],
+              [ 0, 1, s , 0 ],
+              [ 0, 0,  1, 0 ],
+              [ 0, 0,  0, 1 ] ])
+    children();
+}
+
+module GraspFixingElevation(){
+  intersection(){
+    union(){
+      hull(){
+       mirror([1,0]) {
+         GraspElevation();
+       }
+       translate([ -block_x, block_y_min ] + [0,0.1]*1 )
+         circle(0.1);
+      }
+      translate([ strap_barrel_x, $strap_barrel_y ])
+       circle(strap_barrel_dia/2 + strap_guide_sz);
+    }
+    union(){
+      rectfromto([0, $rack_rail_y],
+                [rack_width_inner, 50]);
+      intersection(){
+       translate([ rack_rail_x, $rack_rail_y ])
+         circle(r = rack_width_inner/2 - rack_rail_x);
+       polygon([ [ -block_x-0.1, 0 ],
+                 [ rack_width_inner/2, 0 ],
+                 $rail_fixing_fit_corner,
+                 $rail_fixing_fit_corner + [-1,-1] * fit_slope_len,
+                 [ -grasp_large_x - grasp_large_r*2, block_y_min ],
+                 [ -block_x-0.1 -2, block_y_min +2 ]]);
+      }
+    }
+  }
+}
+
+module StrapBarrelElevation(){
+  translate([ strap_barrel_x, $strap_barrel_y ])
+    circle(strap_barrel_dia/2);
+}
+
+// Bracket support block, goes up inside bracket
+// Z origin is bolt hole
+module BrkBlock(){
+  difference(){
+    linextr( -brk_block_z/2,
+            +brk_block_z/2 ) {
+      rectfromto([ -brk_block_xw/2, adapt_main_top_y - 0.1 ],
+                [ +brk_block_xw/2, 0 ]);
+    }
+    linextr_y_xz( -50, 10 ) {
+      translate([ 0, brk_block_z + bolt_z ])
+       square(center=true,
+              [ main_sz_x_fam + support_around*2,
+                support_remnant *2 ]);
+    }
+  }
+}
+
+// Z origin is bolt hole
+module BoltHole(){
+  linextr_y_xz( -100, 10 )
+    circle(brk_bolt_dia/2);
+
+  linextr_y_xz( -brk_nearbolt_recess_depth, 10)
+    circle(brk_nearbolt_recess_dia/2);
+
+  linextr_y_xz( -100, brk_bolt_nut_top_y ) {
+    hull()
+      for (dz = [0, support_bridge_gap_z])
+       translate([0, dz])
+         circle( r= brk_bolt_nut_r, $fn = 6 );
+  }
+}
+
+module IfFam(){
+  if ($foreaftmaint_dz) {
+    children();
+  }
+}
+
+module FamLinextr(){
+  IfFam()
+    linextr_x_yz(-main_sz_x_fam/2, +main_sz_x_fam/2)
+    rotate(-90)
+    children();
+}
+
+module FamGraspElevation(){
+  difference(){
+    hull(){
+      ybot = $rack_rail_y - rack_rail_dia/2 + grasp_large_r
+       - fit_slope_len * 0.5;
+      for (y = [
+               ybot,
+               adapt_main_top_y - grasp_large_r
+               ])
+       for (dx= [/*-1,*/ +1] * rack_rail_dia/2)
+         translate([ -$foreaftmaint_rail_z + dx, y ])
+           circle(r= grasp_large_r);
+    }
+    if ($foreaftmaint_cutoff) {
+      translate([ -$foreaftmaint_rail_z, 0 ])
+       rectfromto([-100, -100],
+                  [0, 100]);
+    }
+  }
+}
+
+module FamStemElevation(){
+  hull(){
+    rectfromto([ -$foreaftmaint_rail_z
+                , adapt_main_top_y ],
+              [ 0, block_y_min]);
+    translate([
+              -$foreaftmaint_rail_z,
+              $rack_rail_y +
+              rack_shear_ratio * $foreaftmaint_rail_z * $reverse_sign,
+              ])
+      square([0.1, rack_rail_dia * 0.5], center=true);
+  }
+}
+
+module Principal(){
+  // calculated
+  $rack_rail_y = rack_rail_y_of_elevation($elevation_nominal);
+
+  $strap_barrel_y = $rack_rail_y + rack_rail_dia/2 + strap_barrel_dia/2;
+
+  $rail_fixing_fit_corner = [
+    rack_width_inner/2,
+    $rack_rail_y - rack_rail_dia/2
+  ];
+
+  $foreaftmaint_rail_z = brk_block_z/2 + $foreaftmaint_dz - foreaftmaint_y_slop;
+
+  translate([0,0,brk_block_z/2])
+  mirror([0,0, $reverse_sign > 0 ? 0 : 1])
+  translate([0,0,-brk_block_z/2])
+  difference(){
+    union(){
+      MainExtrude(main_sz_lhs_z){
+       GraspElevation();
+      }
+      RackShear() MainExtrude(main_sz_rhs_z){
+       StrapBarrelElevation();
+      }
+      translate([ 0,0, brk_block_z/2]) {
+       BrkBlock();
+      }
+
+      difference(){
+       union(){
+         MainExtrude(main_sz_core_z){
+           BlockElevation();
+         }
+         if ($strf<1) {
+           MainExtrude(max(brk_block_z, main_sz_rhs_z)){
+             rectfromto([-8, adapt_main_top_y + 0.1],
+                        [+8, block_y_min]);
+             rectfromto([-block_x -5,  adapt_main_top_y],
+                        [-grasp_large_x, block_y_min]);
+           }
+         }
+         RackShear() MainExtrude(main_sz_rhs_z){
+           GraspFixingElevation();
+         }
+       }
+
+       translate([0,0, main_sz_rhs_z/2]) linextr(-strap_w/2, +strap_w/2) {
+         translate([ rack_width_inner/2 - strap_th, 0 ])
+           rectfromto([ 0, -50 ], [ 50, 50 ]);
+       }
+      }
+
+      FamLinextr(){
+       if ($foreaftmaint_top_block) {
+         rectfromto([ -foreaftmaint_top_block_zs[0] + bolt_z, 0 ],
+                    [ -foreaftmaint_top_block_zs[1] + bolt_z, block_y_min] );
+       }
+       FamGraspElevation();
+      }
+      intersection(){
+       union(){
+         RackShear()
+           FamLinextr()
+           FamGraspElevation();
+         FamLinextr()
+           FamStemElevation();
+       }
+       translate([ 0,
+                   adapt_main_top_y - 50,
+                   $foreaftmaint_rail_z ])
+         cube(center=true, 100);
+      }
+
+      linextr_y_xz( block_y_min - bolt_nut_around_y_extra , adapt_main_top_y )
+       intersection(){
+         translate([ 0, brk_block_z/2 ])
+           circle(r = bolt_nut_around + brk_bolt_nut_r );
+         rectfromto([-100, 0], [+100,+100]);
+      }
+    }
+
+    RackShear() linextr(-10, main_sz_lhs_z+main_sz_rhs_z) {
+      for (mx=[0,1]) {
+       mirror([mx,0]) {
+         translate([ rack_rail_x, $rack_rail_y ]){
+           hull(){
+             for (dx = [-rack_rail_dia, 0])
+               translate([dx, 0])
+                 circle(r= rack_rail_dia/2);
+           }
+         }
+       }
+      }
+    }
+
+    RackShear() IfFam(){
+      // Distance from bolt hole, in backwards direction
+      cr = rack_rail_dia/2 + foreaftmaint_r_slop;
+      translate([ 0, $rack_rail_y, $foreaftmaint_rail_z ])
+       linextr_x_yz(+rack_rail_x,
+                    -rack_rail_x) {
+       hull(){
+         for (dy=[0,50]) {
+           translate([-dy,0])
+             circle(r= cr);
+         }
+       }
+       hull(){
+         for (dd=[[0,0], [-1,-1], [-1,+1]]) {
+           translate(
+                     [-1, 0] * (rack_rail_dia - fit_slope_len)
+                     + 20 * dd
+                     )
+             circle(r= cr);
+         }
+       }
+      }
+    }
+
+    translate([ 0,0, brk_block_z/2]) BoltHole();
+  }
+}
+
+module ForRackForDemo(){
+  elevation = elevation_of_bolt_for(rear_to_cross_rail);
+  rack_rail_y = rack_rail_y_of_elevation(elevation);
+
+  rotate([atan(
+              front_to_rear_elevation_change /
+              cross_rail_distance
+              ), 0,0])
+    translate([0, rack_rail_y, brk_block_z/2 + rack_rail_y*rack_shear_ratio])
+    children();
+}
+
+module RackForDemoRails(){
+  ForRackForDemo() {
+    for (m=[0]) mirror([m,0,0]) {
+      linextr(-(50 + cross_rail_distance), 50 + rear_to_cross_rail)
+       translate([rack_rail_x, 0])
+       circle(r= rack_rail_dia/2);
+    }
+  }
+}
+
+module RackForDemoCrosses(){
+  ForRackForDemo() {
+    for (z = [
+             rear_to_cross_rail,
+             rear_to_cross_rail - cross_rail_distance,
+             ]) {
+      translate([0,0,z])
+       linextr_x_yz(rack_rail_x, -rack_rail_x)
+       circle(r= rack_rail_dia/2, $fn=8);
+    }
+  }
+}
+
+module Front(){ ////toplevel
+  rotate([180,0,0])
+  Principal($reverse_sign = -1,
+           $foreaftmaint_top_block = false,
+           $foreaftmaint_cutoff = true,
+           $elevation_nominal=
+      elevation_of_bolt_for(rear_to_cross_rail + rear_bolt_to_front_bolt),
+           $foreaftmaint_dz= front_to_cross_rail);
+}
+
+module Rear(){ ////toplevel
+  Principal($reverse_sign = +1,
+           $foreaftmaint_top_block = true,
+           $foreaftmaint_cutoff = false,
+           $elevation_nominal=
+      elevation_of_bolt_for(rear_to_cross_rail),
+           $foreaftmaint_dz= rear_to_cross_rail);
+}
+
+module SomeDemo(){
+  rotate([90, 0, 0]){
+    children();
+
+    color("blue")
+      translate([ 0, -2, -4 ])
+      square(center=true, [ brk_overall_w, 1 ]);
+
+    color("red")
+      translate([ 0, -brk_nearbolt_recess_depth, -4 ])
+      linextr_y_xz(-brk_bolt_eff_len, 0)
+      circle(r = brk_bolt_dia/2);
+
+  }
+}
+
+module FrontDemo(){ ////toplevel
+  SomeDemo() rotate([180,0,0]) Front();
+}
+module RearDemo(){ ////toplevel
+  SomeDemo() Rear();
+}
+module RearRackDemo(){ ////toplevel
+  rotate([atan(rack_shear_ratio),0,0]) SomeDemo() {
+    Rear();
+    translate([0, 0, -rear_bolt_to_front_bolt])
+      rotate([180,0,0]) Front();
+    %RackForDemoRails();
+    color("blue") RackForDemoCrosses();
+  }
+}
diff --git a/topeak-seatstay-lock.scad b/topeak-seatstay-lock.scad
new file mode 100644 (file)
index 0000000..072a557
--- /dev/null
@@ -0,0 +1,147 @@
+// -*- C -*-
+
+pump_dia = 27 + 0.9;
+seatstay_mindia = 14 + 0.5;
+seatstay_maxdia = 19 + 0.7;
+pump_seatstay_gap = 12.3;
+pump_seatstay_delta = 0.1;
+pump_ridge_width = 8 + 2.0;
+
+body_thick = 5;
+
+pin_workdepth = 16 - 1.0;
+pin_width = 11 + 1.0;
+pin_thick = 3 + 0.7;
+pin_base = 25;
+
+lock_manouvre_thick = 3.7 + 0.6;
+lock_manouvre_len = 18;
+lock_hang_width = 17.5;
+lock_manouvre_len_smaller = 13;
+
+body_depth_each = 5;
+clatter_gap = 0.5;
+
+roof_extent = 7;
+roof_thick = 2;
+
+// fudgeish
+
+cut_rotation = 2;
+holes_rotation = 9;
+pin_y_offset = 5.5;
+pin_x_offset = 0.5;
+ridge_rotation = 8.5;
+lock_hang_ratio = 7;
+
+// computed
+
+body_depth = pin_width + body_depth_each*2;
+
+module Holes(forbody=false){
+  translate([0, -pump_dia/2]);
+  rotate(-holes_rotation){
+    translate([-(pump_seatstay_gap/2 + pump_dia/2),
+              0]) {
+      if (!forbody)
+       rotate(-ridge_rotation + holes_rotation)
+         translate([-50, -pump_ridge_width/2])
+         square([50, pump_ridge_width]);
+      circle(r=pump_dia/2, $fn=80);
+    }
+    translate([+(pump_seatstay_gap/2 + seatstay_mindia/2),
+              pump_dia/2 -seatstay_maxdia/2 -pump_seatstay_delta]) {
+      hull(){
+       for (ud=[-1,1])
+         translate([0, ud * (seatstay_maxdia-seatstay_mindia)/2])
+           circle(r=seatstay_mindia/2, $fn=50);
+      }
+    }
+  }
+}
+
+module BodyPlan(){
+   minkowski(){
+    circle(body_thick);
+    hull()
+      Holes(true);
+  }
+}
+
+module Body(){
+  translate([0,0,body_depth/2])mirror([0,0,1]){
+    linear_extrude(height=body_depth){
+      difference(){
+       BodyPlan();
+       Holes();
+      }
+    }
+    linear_extrude(height=roof_thick){
+      difference(){
+       hull(){
+         BodyPlan();
+         translate([0,-roof_extent,0]) BodyPlan();
+       }
+       Holes();
+      }
+    }
+  }
+}
+
+module Pin(){
+  translate([pin_x_offset, pin_y_offset, 0]) rotate([0,90,0]){
+    translate([0, 0, -pin_thick/2])
+      linear_extrude(height=pin_thick){
+      translate([-pin_base/2, 0]) square([pin_base, 50]);
+      translate([-pin_width/2, -100]) square([pin_width, 101]);
+    }
+    hull() for (d=[0,10]) {
+      translate([d*lock_hang_ratio,-d,0])
+       translate([-lock_manouvre_thick/2,
+                  -pin_workdepth-100,
+                  -lock_manouvre_len_smaller])
+       cube([lock_manouvre_thick, 100,
+             lock_manouvre_len + lock_manouvre_len_smaller]);
+    }
+  }
+}
+
+module All(){
+  difference(){
+    Body();
+    Pin();
+  }
+}
+
+module Piece(pc,interval){
+  translate([0,-pc*interval,0])
+    intersection(){
+      rotate([0,0,pc*180-cut_rotation])
+      translate([-200,clatter_gap/2,-200]) cube([400,400,400]);
+    All();
+  }
+}
+
+module PiecePrint(pc){
+  rotate([0,0,90]) rotate([0,180,0])
+    Piece(pc,4);
+}
+
+module PiecesPrint(){
+  PiecePrint(0);
+  PiecePrint(1);
+}
+
+module Demo(){
+  for (pc=[0,1])
+    Piece(pc,0);
+}
+
+//Holes();
+//Demo();
+//All();
+//Pin();
+//Pieces();
+PiecesPrint();
+//PiecePrint(0);
+//PiecePrint(1);
diff --git a/toplevel-find b/toplevel-find
new file mode 100755 (executable)
index 0000000..c9174c9
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/perl -w
+use strict;
+use IO::File;
+
+@ARGV==1 or die;
+my $base = $ARGV[0];
+$base =~ m/^\-/ and die;
+
+sub read_file ($);
+sub read_file ($) {
+  my ($fn) = @_;
+  my $f = new IO::File "$fn", '<' or die "$fn $!";
+  while (<$f>) {
+    if (m#^//// toplevels-from:#) {
+      defined($_ = <$f>) or die $!;
+      m#^include\s+\<(\S+)>\s*$# or die;
+      read_file($1);
+      next;
+    }
+    next unless m#^\s*(?:////\s?)?module\s+(\w+)\b.*////toplevel\b#;
+    print "$base,$1\n" or die $!;
+  }
+  $f->error and die "$fn $!";
+}
+
+read_file("$base.scad");
+close STDOUT or die $!;
+
diff --git a/toplevel-make b/toplevel-make
new file mode 100755 (executable)
index 0000000..2df668b
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/perl -w
+use strict;
+die unless @ARGV==1;
+die unless $ARGV[0] =~ m/^(.+),(\w+)(?:\..*)?/;
+my ($base,$obj) = ($1,$2);
+print <<END or die $!;
+include <$base.scad>
+$obj();
+END
+close STDOUT or die $!;
diff --git a/tower-base.scad b/tower-base.scad
new file mode 100644 (file)
index 0000000..0e097e9
--- /dev/null
@@ -0,0 +1,152 @@
+/* -*- C -*- */
+
+motorwidth=35.7;
+motorheight=34.5;
+totalheight=58;
+
+pillarthick=8;
+sidethick=2.5;
+archthick=6.5;
+frameextra=3.5;
+framesplayx=5;
+framesplayy=5;
+botleftgap=4.5;
+botleftstand=0.75;
+archoutwards=(pillarthick-archthick)/sqrt(8);
+
+topgluecubex=18;
+topgluecubez=5;
+clippairy=16;
+clippairdz=-2.5;
+topgluecubedy=1;
+
+dovebasecutcylz=4;
+dovebasecutcylr=10;
+
+d=0.01;
+
+mw2=motorwidth/2;
+
+include <doveclip.scad>
+
+module corner() {
+  $fn=30;
+  frameheight= motorheight + frameextra;
+  slopeheight= totalheight - frameheight;
+  slopex = (mw2 + archoutwards - framesplayx)/slopeheight;
+  slopey = (mw2 + archoutwards - framesplayy)/slopeheight;
+  echo(sqrt(slopex*slopex + slopey*slopey));
+
+  translate([-mw2,-mw2,0]) union(){
+    difference(){
+      union(){
+       cylinder(r=pillarthick/2, h=frameheight);
+       translate([0,0,frameheight])
+         sphere(r=pillarthick/2);
+      }
+      translate([d,d,-1])
+       cube([mw2-1,mw2-1,frameheight+pillarthick+2]);
+    }
+    intersection(){
+      multmatrix
+       ([      [       1,      0,      slopey, -archoutwards ],
+               [       0,      1,      slopex, -archoutwards ],
+               [       0,      0,      1, frameheight  ],
+               [       0,      0,      0,      1       ]])
+       translate([0,0,-frameextra])
+       cylinder(r=archthick/2,
+                h=slopeheight+frameextra);
+      union() {
+       cylinder(r=pillarthick/2, h=frameheight);
+       translate([-100,-100,frameheight])
+         cube([200,200,100]);
+      }
+    }
+  }
+}
+
+module halfside() {
+  spacesz = (motorwidth - pillarthick/2*2) / 4;
+  panelheight = spacesz + sidethick;
+  panelbasez = motorheight+pillarthick/4-panelheight;
+  translate([0,-mw2,0]) {
+    translate([-mw2,-sidethick,0])
+      cube([motorwidth,sidethick,sidethick]);
+    difference(){
+      translate([-mw2,-sidethick, panelbasez])
+       cube([mw2,sidethick,panelheight]);
+      translate([-mw2+pillarthick/3, -sidethick, panelbasez])
+       rotate([0,45,0])
+       translate([0,-1,0])
+       cube([spacesz * sqrt(2),
+             sidethick+2,
+             spacesz * sqrt(2)]);
+    }
+    intersection(){
+      for (xz=[[-mw2+pillarthick/3-sidethick, 0,
+               panelbasez+sidethick],
+              [0, 0, panelbasez + sidethick/sqrt(2)]]) {
+       translate(xz)
+         translate([0,-sidethick,0])
+         rotate([0,55,0])
+         translate([0,0,-sidethick])
+         cube([100, sidethick, sidethick]);
+      }
+      translate([-mw2,-sidethick,0])
+       cube([motorwidth,sidethick,
+             motorheight+pillarthick]);
+    }
+  }
+}
+
+module towerbase() {
+  difference(){
+    union(){
+      for (mirx=[0,1]) for (miry=[0,1])
+                        mirror([mirx,0,0]) mirror([0,miry,0]) corner();
+      for (angle=[0,90,180]) {
+       rotate([0,0,angle]) halfside();
+       rotate([0,0,angle]) mirror([1,0,0]) halfside();
+      }
+    }
+//    multmatrix([[    -1,     0,      0, -mw2 - botleftstand ],
+//             [       0,      1,      0,      -100    ],
+//             [       1,      0,      1, -100 + botleftgap ],
+//             [       0,      0,      0,      1       ] ])
+//      cube([100,200,100]);
+  }
+  translate([clippairy/2,0,totalheight]) {
+    difference(){
+      translate([-clippairy+topgluecubedy/2,-topgluecubex/2,0])
+       cube([clippairy-topgluecubedy,topgluecubex,topgluecubez]);
+    }
+    translate([0,0,topgluecubez+clippairdz+DoveClip_depth()]) rotate([0,-90,0])
+//      DoveClipPair(h=clippairy);
+      DoveClipPairSane(h=clippairy, count=3);
+  }
+}
+
+if (towerbase_demo) {
+  intersection(){
+    translate([0,0,-50]) towerbase();
+    translate([-100,-100,0]) cube([200,200,32]);
+  }
+
+  intersection(){
+    translate([40,0,-60]) towerbase();
+    translate([-100,-100,0]) cube([200,200,32]);
+  }
+
+  translate([60,-90,0]) {
+    DoveClipPairSane(h=clippairy, count=3);
+    mirror([1,0,0]) translate([DoveClip_depth()-0.1,0,0]) cube([20,8,6]);
+  }
+
+  for (x=[0,20,40]) {
+    translate([x,-50,0]) DoveClipPin(h=clippairy);
+    translate([x+10,-50,0]) DoveClipPin(h=clippairy/2);
+    translate([x+10,-30,0]) DoveClipPin(h=clippairy/2);
+  }
+} else {
+  towerbase();
+}
diff --git a/trackpump-mutlihead-clip.scad b/trackpump-mutlihead-clip.scad
new file mode 100644 (file)
index 0000000..c5108fd
--- /dev/null
@@ -0,0 +1,174 @@
+// -*- C -*-
+
+include <commitid.scad>
+
+pump_main_dia = 38;
+pump_side_width = 5;
+pump_side_thick = 4;
+pump_shaft_dia = 14;
+baseplate = 3;
+
+pump_protr_flat = 3;
+pump_protr_slope = 0.9;
+
+hose_inner_dia = 20;
+hose_aperture = 11;
+hose_side_width = 5;
+hose_base_offset = 30;
+
+hose_side_thick = 6;
+hose_side_stalk_width = 6;
+
+pump_protr_protr = 3;
+pump_side_height = 20;
+
+// calculated
+pump_protr_slheight = pump_protr_protr / pump_protr_slope;
+
+pump_side_outer_rad = pump_side_width + pump_main_dia/2;
+
+baseplate_width_rad =
+  sqrt( pow(pump_side_outer_rad, 2)
+       -pow( pump_main_dia/2 - pump_protr_protr, 2) );
+
+xm = baseplate + pump_main_dia/2;
+
+pump_side_total_height =
+  pump_side_thick + pump_side_height + pump_protr_slheight + pump_protr_flat;
+
+$fa=5;
+
+module PumpSidePlan() {
+  or = pump_side_outer_rad;
+  difference(){
+    union(){
+      intersection(){
+       translate([-xm, 0]) circle(r=or);
+//     translate([-(xm+or), -or]) square([xm+or, or*2]);
+      }
+    }
+    translate([-xm-or, 0])
+      square(center=true, [pump_side_width*4, pump_shaft_dia]);
+  }
+}
+
+module PumpSideElevation(){
+  x3 = 0;
+  x2 = x3 - baseplate;
+  x1 = x2 - pump_main_dia;
+  x0 = x1 - pump_side_width;
+  x2a = x2 - pump_protr_protr;
+  x4 = x2 + pump_side_width;
+
+  z0 = 0;
+  z1 = z0 - pump_side_thick;
+  z2 = z1 - pump_side_height;
+  z2a = z2 - pump_protr_slheight;
+  z2b = z2a - pump_protr_flat;
+
+  arcx = x2-x1;
+  arcy = z1-z2;
+
+  translate([x0,z1]) square([x1-x0, z0-z1]);
+
+  difference(){
+    translate([x1,z2]) square([x3-x1, z0-z2]);
+    translate([x1,z2]) scale([1,arcy/arcx]) circle(r=arcx);
+  }
+
+  translate([x2,z2a]) square([x4-x2, z0-z2a]);
+
+  hull(){
+    translate([x2,z2a]) square([x4-x2, z2-z2a]);
+    translate([x2a,z2b]) square([x3-x2a, z2a-z2b]);
+  }
+}
+
+module PumpSide(){
+  br = baseplate_width_rad;
+  brs = hose_side_stalk_width/2;
+  echo(brs);
+
+  difference(){
+    intersection(){
+      translate([0,100,0])
+       rotate([90,0,0])
+       linear_extrude(height=200, convexity=10)
+       PumpSideElevation();
+      union(){
+       translate([0,0,-100])
+         linear_extrude(height=200, convexity=10)
+         PumpSidePlan();
+       // baseplate
+       hull(){
+         mirror([0,0,1])
+           translate([-xm, -brs, 0])
+           cube([pump_main_dia/2 + pump_side_width,
+                 brs*2,
+                 1]);
+         translate([-xm, -br, -pump_side_total_height])
+           cube([xm,
+                 br*2,
+                 pump_protr_flat]);
+       }
+      }
+    }
+    translate([-(baseplate + pump_main_dia/2), 0,
+               -(pump_side_thick + pump_side_height)])
+      cylinder(r=pump_main_dia/2, h=200);
+  }
+  rotate([0,0,180])
+    mirror([0,0,1])
+    translate([-0,
+              -br,
+              pump_side_total_height])
+    Commitid_BestCount_M([baseplate + pump_protr_protr,
+                         br*2]);
+}
+
+module HoseSidePlan(){
+  ro = hose_inner_dia/2 + hose_side_width;
+  ri = (hose_inner_dia/2);
+  st = hose_side_stalk_width/2;
+
+  apx = sqrt( ri*ri - (hose_aperture*hose_aperture)/4 );
+  apsq = hose_base_offset + apx - hose_aperture/2;
+  echo(apx,apsq);
+
+  difference(){
+    union(){
+      translate([-1, -st]) square([hose_base_offset+1, st*2]);
+      translate([hose_base_offset, 0]) circle(r= ro);
+    }
+    translate([hose_base_offset, 0]) circle(r= hose_inner_dia/2);
+    translate([apsq, 0])
+      rotate(-45)
+      square([50,50]);
+  }
+
+  //%translate([hose_base_offset + apx, 0]) square([50,50]);
+  //%square(center=true, [100, hose_aperture]);
+}
+
+module HoseSide(){
+  mirror([0,0,1])
+    linear_extrude(height=hose_side_thick, convexity=10)
+    HoseSidePlan();
+}
+
+module Clip(){
+  PumpSide();
+  HoseSide();
+}
+
+module ClipPrint(){
+  rotate([180,0,0])
+    Clip();
+}
+
+//PumpSidePlan();
+//PumpSideElevation();
+//PumpSide();
+//HoseSide();
+//Clip();
+ClipPrint();
diff --git a/trailerhubcap.scad b/trailerhubcap.scad
new file mode 100644 (file)
index 0000000..4ae4c7d
--- /dev/null
@@ -0,0 +1,80 @@
+// Copyright (C)2012 Ian Jackson
+// Licenced under the GNU General Public Licence, version 3, or
+// (at your option) any later version.  There is NO WARRANTY.
+
+maindia = 29.7;
+poleholeh = 5.0;
+polecovth = 0.4;
+poleholerad = 6;
+
+mainoverlap = 1.5;
+
+hookbasew = 5.0;
+hookfullw = 7;
+hookheight = 4.5;
+hookwidth = 8;
+hookbevelw = 0.75;
+hookbevelh = 1.5;
+
+fingernaildepth = 5;
+fingernailheight = 2.5;
+fingernailwidth = 6;
+
+bigrad = maindia/2 + mainoverlap;
+mainth = poleholeh + polecovth;
+hooklessdepth = hookfullw - hookbasew;
+
+module base() {
+  rotate_extrude(convexity=10)
+    mirror([1,0,0])
+    polygon(points=[[-bigrad, 0],
+                   [-bigrad + mainth, -mainth],
+                   [0, -mainth],
+                   [0, -poleholeh],
+                   [-poleholerad, -poleholeh],
+                   [-poleholerad, 0]]);
+}
+
+module fingernails() {
+  for (ang=[60,180,300])
+    rotate([0,0,ang])
+      translate([bigrad - fingernaildepth,
+                -fingernailwidth/2,
+                -fingernailheight])
+      cube([fingernaildepth + 1, fingernailwidth, fingernailheight + 1]);
+}
+
+module hookrim() {
+  rotate_extrude(convexity=10)
+    mirror([1,0,0])
+    translate([-maindia/2, 0, 0])
+    polygon(points=[[hooklessdepth, 0],
+                   [hookfullw, 0],
+                   [hookfullw*0.33, hookheight],
+                   [hookbevelw, hookheight],
+                   [0, hookheight-hookbevelh],
+                   [0, hooklessdepth]]);
+}
+
+module hooktriangles() {
+  for (ang=[0,120,240]) {
+    rotate([0,0,ang]) {
+      translate([0,0,-1]) {
+       linear_extrude(height=hookheight+2) {
+         polygon(points=[[0, 0],
+                         [maindia/2 + 1, -hookwidth],
+                         [maindia/2 + 1, +hookwidth]]);
+       }
+      }
+    }
+  }
+}
+
+difference(){
+  base();
+  fingernails();
+}
+intersection(){
+  hookrim();
+  hooktriangles();
+}
diff --git a/treefoil.scad.pl b/treefoil.scad.pl
new file mode 100755 (executable)
index 0000000..ef9ef26
--- /dev/null
@@ -0,0 +1,79 @@
+#!/usr/bin/perl -w
+
+# Use:
+#   - support X/Y dist 0.5mm
+
+use strict;
+
+our $shape = <<'END';
+xyyZZYYXYxxyzYYZXzzxyXXYXXXZxxxyyXXZyyyzXXzz
+zxxYYXXZXzzxyXXYZyyzxZZXZZZYzzzxxZZYxxxyZZyy
+yzzXXZZYZyyzxZZXYxxyzYYZYYYXyyyzzYYXzzzxYYxx
+END
+# simple version (unknotted, [0,1,2]^3):
+# YxxyzYYZXzzxyXXYZyyzxZZX
+#
+# New and less symmetric one that also fits in the 2x2x2 box:
+# YXXyzzYxYXZZxzyxYzyyZXZx
+# (email 8.9.2022)
+
+sub o { print @_ or die $!; }
+
+o <<'END';
+// -*- autogenerated, do not edit -*-
+
+module Trace() {
+END
+
+my @p = qw(0 0 0);
+
+$"=',';
+
+while ($shape =~ s/^\s*(\w)//) {
+    my $ix = index('xyz', (lc $1));
+    my $sign = $1 =~ /[A-Z]/ ? +1 : -1;
+    my @q = @p;
+    $q[$ix] += $sign;
+    o "    TraceEdge([@p],[@q]);\n";
+    @p = @q;
+}
+
+die @p unless "@p" eq '0,0,0';
+
+o "}\n\n";
+
+while (<DATA>) { o $_ }
+
+__DATA__
+
+thick = 6;
+edgeu = 10;
+//edgeu = 15;
+
+// calculated
+
+octa_long = thick;
+octa_short = octa_long / (1 + sqrt(2));
+
+module OctaThing() {
+    hull(){
+       for (r = [[0,0,0], [90,0,0], [0,90,0]]) {
+           rotate(r)
+               cube([ octa_short,octa_short, octa_long ], center=true);
+       }
+    }
+}
+
+module TraceEdge(p,q) {
+    hull(){
+        for (x=[p,q]) {
+            translate(x * edgeu)
+                OctaThing();
+        }
+    }
+}
+
+rotate([0,0,45])
+  Trace();
+
+       
diff --git a/tube-crossdrill-jig.scad b/tube-crossdrill-jig.scad
new file mode 100644 (file)
index 0000000..843a2a6
--- /dev/null
@@ -0,0 +1,138 @@
+// -*- C -*-
+
+//$fs=0.1;
+//$fa=3;
+$fs=0.2;
+$fa=6;
+
+rearwallthick = 3;
+basethick = 2;
+mainframeendthick = 2.5;
+
+tubedia = 16 + 0.8;
+tubetubethick=2;
+tubetubetopslop=1;
+
+boltholedia = 6.5 + 0.5;
+boltholeslotshorter = 6;
+mainframeholedia = 5 + 1.0;
+
+// "slot" refers to the things in the base of the drill press stand
+backslotedgespace = 59;
+slotwidth = 11.5 - 0.5;
+backslotmid2screwhole = 17;
+slotplugheight = 5.5;
+slotplugshorterlen =10;
+slotpluglongerlen = 20;
+
+//slotslope = 11.0 / 18.5;
+slotslope = 1 / tan(51);
+
+// "keepslot" refers to the screws in the wooden jig block
+keepslotstartz = 20;
+keepslotlen = 18;
+keepslotx = backslotedgespace / 2;
+keepslotwidth = 4;
+
+mainframeextraside = 12;
+mainframeextrafront = 15;
+
+rearwallstrengthwidth = 10;
+keepslotclear = 10;
+
+// computed values
+
+slotslopediag = sqrt(1 + slotslope*slotslope);
+slotwidthx = slotwidth * slotslopediag;
+
+slotxperlen = slotslope / slotslopediag;
+slotyperlen =         1 / slotslopediag;
+
+mainframeholex = backslotedgespace/2 + slotpluglongerlen * slotxperlen
+  + 0.5 * slotwidth * slotyperlen;
+
+mainframeholey = -slotpluglongerlen * slotyperlen
+  + 0.5 * slotwidth * slotxperlen;
+
+mainframemaxx = mainframeholex + mainframeextraside;
+mainframeminy = mainframeholey - mainframeextrafront;
+mainframemaxz = keepslotstartz + keepslotlen;
+
+module MainFrame(){
+  for (m=[0,1]) {
+    mirror([m,0,0]) {
+      translate([-1, mainframeminy, 0])
+       cube([mainframemaxx+1, -mainframeminy, basethick]);
+      translate([-1, -rearwallthick, 0])
+       cube([mainframemaxx+1, rearwallthick, mainframemaxz]);
+
+      for (x=[keepslotx - keepslotclear, mainframemaxx - 0.5]) {
+       translate([x,0,0])
+         rotate([90,0,-90])
+         linear_extrude(height=mainframeendthick)
+         polygon([[-mainframeminy, 0],
+                  [0, mainframemaxz],
+                  [0, 0]]);
+      }
+
+      translate([backslotedgespace/2, 0, 1])
+       mirror([0,0,1])
+       linear_extrude(height=slotplugheight+1)
+       polygon([[0,0],
+                [slotwidthx, 0],
+                [slotwidthx + slotplugshorterlen * slotxperlen,
+                 -slotplugshorterlen * slotyperlen],
+                [slotpluglongerlen * slotxperlen,
+                 -slotpluglongerlen * slotyperlen]]);
+      translate([-1,
+                -rearwallthick - boltholeslotshorter + 0.2,
+                tubedia + tubetubetopslop + tubetubethick + 4])
+       cube([keepslotx - keepslotclear + 1,
+             boltholeslotshorter + 0.5,
+             rearwallstrengthwidth]);
+    }
+  }
+}
+
+module TubeThing(extralen, dia, extraheight, underheight){
+  effheight = tubetubetopslop + extraheight + underheight;
+  len = -mainframeminy + extralen * 2;
+  translate([0, mainframeminy - extralen, -underheight]) {
+    translate([0,0, dia/2 + effheight])
+      rotate([-90,0,0]) cylinder(h=len, r=dia/2);
+    translate([-dia/2, 0, 0])
+      cube([dia, len, effheight + dia/2]);
+  }
+}
+
+module Jig(){
+  difference(){
+    union(){
+      MainFrame();
+      TubeThing(0, tubedia+tubetubethick*2, -tubetubethick, 0);
+    }
+    union(){
+      translate([0,0,-0.1])
+       TubeThing(10, tubedia, 0, 5);
+      translate([-boltholedia/2, mainframeminy - 1, -5])
+       cube([boltholedia,
+             -mainframeminy + 1 - rearwallthick - boltholeslotshorter,
+             mainframemaxz + 10]);
+      for (m=[0,1]) {
+       mirror([m,0,0]) {
+         translate([mainframeholex, mainframeholey, -30])
+           cylinder(h=basethick+40, r=mainframeholedia/2);
+         translate([keepslotx - keepslotwidth/2,
+                    -10, keepslotstartz])
+           cube([keepslotwidth, 20, keepslotlen + 10]);
+       }
+      }
+    }
+  }
+}
+
+//MainFrame();
+//TubeThing(0, tubedia);
+
+rotate([-90,0,0])
+  Jig();
diff --git a/utils.scad b/utils.scad
new file mode 100644 (file)
index 0000000..e440b5f
--- /dev/null
@@ -0,0 +1,51 @@
+// -*- C -*-
+
+// suitable for masking things within radius sqrt(2) only
+module FArcSegment_mask(beta) {
+  for (i=[0 : 0.75 : 3]) {
+    rotate(i*beta/4)
+      polygon([[0, 0],
+              [1, 0],
+              [cos(beta/4), sin(beta/4)]]);
+  }
+}
+
+module FArcSegment(xc,yc,inrad,outrad,alpha,delta) {
+  translate([xc,yc]) {
+    intersection() {
+      difference() {
+       circle(r=outrad, $fn=70);
+       circle(r=inrad, $fn=70);
+      }
+      rotate(alpha) scale(outrad*2) {
+       FArcSegment_mask(delta);
+      }
+    }
+  }
+}
+
+module rectfromto(a,b) {
+  ab = b - a;
+  translate([min(a[0], b[0]), min(a[1], b[1])])
+    square([abs(ab[0]), abs(ab[1])]);
+}
+module circleat(c,r) { translate(c) circle(r); }
+module linextr(z0,z1, convexity=20) {
+  translate([0,0,z0])
+    linear_extrude(height=z1-z0, convexity=convexity)
+    children();
+}
+
+module linextr_x_yz(x0,x1, convexity=20) { // XY turn into YZ
+  rotate([90,0,0])
+    rotate([0,90,0])
+    linextr(x0,x1, convexity=convexity)
+    children();
+}
+
+module linextr_y_xz(y0,y1, convexity=20) { // XY turn into YZ
+  rotate([0,0,180])
+    rotate([90,0,0])
+    linextr(y0,y1, convexity=convexity)
+    children();
+}
diff --git a/velux-window-grip.scad b/velux-window-grip.scad
new file mode 100644 (file)
index 0000000..d59a78f
--- /dev/null
@@ -0,0 +1,138 @@
+// -*- C -*-
+
+include <funcs.scad>
+
+// MainLoop
+
+main_thick = 9.0;
+main_in_dia = 28.9;
+
+horn_ext_dia = 20 - 0.5;
+
+horn_c_x = -4.6;
+horn_c_dy= -4;
+
+blhook_start_ang = 45;
+blhook_in_rad = 1.85;
+blhook_str_len = 2.9;
+
+width = 20;
+
+// Attach
+
+at_bolt_into = 13.0 + 0.5;
+at_tube_dia = 16.7 + 0.5;
+at_prong_minw = 4;
+at_rear_thick = 4.5;
+at_bolt_dia = 5 + 0.5;
+
+at_rear_width = at_tube_dia;
+at_stem_len = main_in_dia/2 * 0.3;
+
+at_prong_depth = at_bolt_into * 2;
+at_gap_width = at_tube_dia * 0.75;
+
+// computed
+
+blhook_mid_rad = blhook_in_rad + main_thick/2;
+mc_mid_rad = main_in_dia/2 + main_thick/2;
+
+mc_bl = circle_point([0,0], mc_mid_rad, 270-blhook_start_ang);
+
+at_block_x = at_tube_dia + at_prong_minw * 2;
+at_block_y = at_prong_depth + at_rear_thick;
+at_block_z = width;
+
+at_stem_yy = at_stem_len + mc_mid_rad;
+
+at_offset_y = at_block_y + at_stem_len + mc_mid_rad;
+
+$fs=0.05;
+
+horn_thick = main_thick;
+
+module MainLoop(){
+  intersection(){
+    difference(){
+      circle(r= main_in_dia/2 + main_thick, $fn=50);
+      circle(r= main_in_dia/2, $fn=50);
+    }
+    polygon([[0,0],
+            3*mc_bl,
+            [0, -100],
+            [100,-100],
+            [100,100],
+            [0,100]]);
+  }
+  translate(mc_bl)
+    circle(main_thick/2);
+  translate([horn_c_x, mc_mid_rad + horn_c_dy])
+    intersection(){
+    difference(){
+      circle(horn_ext_dia/2);
+      intersection(){
+       circle(horn_ext_dia/2 - horn_thick);
+       polygon([[-50,-50],
+                [-50,-horn_c_dy],
+                [50,-horn_c_dy],
+                [50,-50]]);
+      }
+    }
+    polygon([[0,0],
+             [-50,0],
+             [0,50]]);
+  }
+  translate([0,main_in_dia/2]) mirror([1,0])
+    square([-horn_c_x + horn_ext_dia/2 * 0.75, main_thick]);
+  translate(mc_bl){
+    translate([-blhook_str_len/2, 0])
+      square(center=true, [blhook_str_len, main_thick]);
+    translate([-blhook_str_len, blhook_mid_rad]){
+      intersection(){
+       difference(){
+         circle(r=blhook_mid_rad + main_thick/2);
+         circle(r=blhook_mid_rad - main_thick/2);
+       }
+       mirror([1,1]) square(50);
+      }
+    }
+  }
+}
+
+module MainLoopTest(){
+  linear_extrude(height=1.6)
+    MainLoop();
+}
+
+module Attach(){
+  difference(){
+    translate([0, at_block_y/2, 0])
+      cube(center=true, [at_block_x, at_block_y, at_block_z]);
+    translate([0, at_prong_depth/2-1, 0])
+      cube(center=true, [at_gap_width, at_prong_depth+2, at_block_z+1]);
+    translate([0,-1,0])
+      rotate([-90,0,0])
+      cylinder(r= at_tube_dia/2, h= at_prong_depth+1);
+    translate([-50, at_prong_depth-at_bolt_into, 0])
+      rotate([0,90,0])
+      cylinder(r= at_bolt_dia/2, h= 100);
+  }
+  difference(){
+    translate([0, at_block_y + at_stem_yy/2 - 0.1, 0])
+      cube(center=true, [at_tube_dia, at_stem_yy + 0.2, at_block_z]);
+    translate([0, at_offset_y, -50])
+      cylinder(r = mc_mid_rad, 100);
+  }
+}
+
+module Combine(){
+  rotate([0,0,45]) translate([0,-main_thick/2,0]){
+    linear_extrude(height=width)
+      translate([0,at_offset_y,0])
+      MainLoop();
+    translate([0,0, width/2])
+      Attach();
+  }
+}
+
+Combine();
diff --git a/velux-window-grip.slic3r b/velux-window-grip.slic3r
new file mode 100644 (file)
index 0000000..3a44d19
--- /dev/null
@@ -0,0 +1 @@
+solid_infill_every_layers = 11
diff --git a/wall-cable-hook.scad b/wall-cable-hook.scad
new file mode 100644 (file)
index 0000000..3076265
--- /dev/null
@@ -0,0 +1,40 @@
+// -*- C -*-
+
+circle_inner_rad = 10 + 0.5;
+
+thick = 3;
+
+tab_sz = 20;
+width = 20;
+
+screw_hole_dia = 4.5 + 0.5;
+
+// calculated
+
+circle_outer_rad = circle_inner_rad + thick;
+
+module Plan() {
+  difference(){
+    circle(r=circle_outer_rad, $fn=150);
+    circle(r=circle_inner_rad, $fn=150);
+    mirror([1,0]) square([50,50]);
+  }
+  translate([-circle_outer_rad, -0.1])
+    multmatrix([[1,0,0,0],
+               [-1,1,0,0],
+               [0,0,1,0],
+               [0,0,0,1]])
+    square([thick, tab_sz + circle_outer_rad + thick]);
+}
+
+module Hook(){
+  difference(){
+    linear_extrude(height=width) Plan();
+    if (false)
+      translate([-50, circle_outer_rad + tab_sz - width/2, width/2])
+      rotate([0,90,0])
+      cylinder(r= screw_hole_dia / 2, h=100, $fn=50);
+  }
+}
+
+Hook();
diff --git a/wardrobe-hook.scad b/wardrobe-hook.scad
new file mode 100644 (file)
index 0000000..959e967
--- /dev/null
@@ -0,0 +1,192 @@
+// -*- C -*-
+
+include <funcs.scad>
+include <utils.scad>
+
+tubeslop = 0.5;
+tubeheight = 30 + tubeslop;
+tubewidth = 15 + tubeslop;
+mainthick = 4;
+
+clipthick = 2;
+clipang = 135;
+
+stemlen = 40;
+
+topwidth = 20;
+
+hookinrad = 7.5;
+hookcurl = 60;
+hookwidth = 4;
+
+tuberad = tubewidth/2;
+bend = atan(tuberad/stemlen);
+mainoutrad = tuberad + mainthick;
+hookoutrad = hookinrad + hookwidth;
+hookcy = -(stemlen - hookoutrad);
+
+eltop = [topwidth/2, -tuberad + tubeheight + mainthick + 0.1];
+elmid = [topwidth/2, -tuberad];
+ellow = tangent_intersect_b([0,hookcy], hookinrad, elmid);
+ellowextra = 180 - tangent_intersect_beta([0,hookcy], hookinrad, elmid);
+
+module ClipPlan(qbend, qstemleny){
+  dy = tubeheight - tuberad*2;
+  FArcSegment(0, dy, tuberad, mainoutrad, -1, 181);
+  FArcSegment(0, 0,  tuberad, mainoutrad, -qbend, qbend+1);
+  translate([tuberad, 0]) square(center=false, size=[mainthick,dy]);
+  FArcSegment(0, 0, tuberad, tuberad + clipthick, 360-clipang, clipang+1);
+  rotate(-qbend) translate([tuberad, 0]) mirror([0,1])
+    square(center=false, size=[mainthick, qstemleny/cos(qbend)]);
+}
+
+module Plan(){
+  ClipPlan(bend,stemlen);
+}
+
+module ElevationCore(){
+  FArcSegment(0, hookcy, hookinrad, hookoutrad,
+             180 - ellowextra,
+             90 + hookcurl + ellowextra);
+  translate([-hookoutrad*sqrt(0.5),
+             hookcy - hookoutrad*sqrt(0.5) + 0.1])
+    mirror([1,0])
+    square(center=false, size=[topwidth, stemlen + tubeheight + 20]);
+  polygon([[-hookoutrad, ellow[1]],
+          reflect_in_y(eltop),
+          eltop,
+          elmid,
+          ellow]);
+}
+
+// after here is all 3D
+
+module Primary(){
+  intersection(){
+    translate([0,0, -(topwidth+10)/2])
+      linear_extrude(height=topwidth+10) Plan();
+    translate([50,0])
+      rotate([0,-90,0])
+      linear_extrude(height=100)
+      ElevationCore();
+  }
+}
+
+module PlaneAbove(){
+  translate([-100,-100,0]) cube(center=false,size=[200,200,200]);
+}
+
+taperangle = -270 + tangent_intersect_beta([-hookcy, 0],
+                                         hookoutrad,
+                                         [-eltop[1], -eltop[0]]);
+module HookL(){ ////toplevel
+  difference(){
+    rotate([taperangle,0,0])
+      translate([0,-eltop[1],0])
+      Primary();
+    translate([0,0,topwidth/2])
+      rotate([taperangle*2,0,0])
+      PlaneAbove();
+    translate([0,0,-topwidth/2])
+      mirror([0,0,1]) PlaneAbove(0);
+  }
+}
+
+// straight-on version, everything prefixed with s or S
+
+shookcy = -(stemlen-hookoutrad);
+sstemleny = -shookcy;
+sbend_raw = tangents_intersect_beta([0,0],tuberad,
+                                   [0,shookcy],hookinrad);
+sbend = angle_map_range(360-sbend_raw, -180);
+
+module SPlan(){
+  ClipPlan(sbend, sstemleny);
+  FArcSegment(0,shookcy, hookinrad,hookoutrad,
+             270 - hookcurl,
+             hookcurl + 90 - sbend);
+}
+
+module SElevation(){
+  boty = shookcy - hookoutrad - 1;
+  polygon([[-1,         tubeheight],
+          [topwidth,   tubeheight],
+          [topwidth,   elmid[1]],
+          [hookwidth,  shookcy],
+          [hookwidth,  boty],
+          [-1,         boty]]);
+}
+
+module SElevationPlaced(){
+  rotate([0,-90,0]) translate([0,0,-100]) linear_extrude(height=200)
+    SElevation();
+}
+
+module SHookL(){ ///toplevel
+  intersection(){
+    linear_extrude(height=topwidth) SPlan();
+    SElevationPlaced();
+  }
+}
+
+// straight-on version, reversed, everything prefixed with t or T
+
+tjoinrad = mainoutrad * 0.7;
+tstem0leny = tuberad - tjoinrad*0.5;
+tjoinoutrad = tjoinrad + mainthick;
+
+thookcy = shookcy;
+
+tjoin0c = [tuberad - tjoinrad, -tstem0leny];
+tjoin1c = [0,                      thookcy];
+
+tbend_raw = tangents_intersect_beta(tjoin0c, tjoinrad,
+                                   tjoin1c, -hookoutrad);
+tbend0 = angle_map_range(tbend_raw,       0);
+tbend1 = angle_map_range(tbend_raw + 180, -180);
+
+tbend0p = circle_point(tjoin0c, tjoinrad, tbend_raw);
+tbend1p = circle_point(tjoin1c, -hookoutrad, tbend_raw);
+
+module TPlan(){
+  ClipPlan(0, tstem0leny);
+  FArcSegment(tjoin0c[0],tjoin0c[1], tjoinrad,tjoinoutrad,
+             tbend0, 360-tbend0);
+  FArcSegment(0,shookcy, hookinrad,hookoutrad,
+             tbend1, 270+hookcurl - tbend1);
+  translate(tbend0p) {
+    rotate(tbend_raw+180) mirror([1,0]) {
+      translate([0,-0.1]) square(size=[mainthick, dist2d(tbend0p,tbend1p)+0.2]);
+    }
+  }
+}
+
+module THookR(){ ///toplevel
+  intersection(){
+    linear_extrude(height=topwidth) TPlan();
+    SElevationPlaced();
+  }
+}
+
+// other toplevels etc.
+
+module HookR(){ ////toplevel
+  mirror([1,0,0]) HookL();
+}
+
+module SHookR(){ ////toplevel
+  mirror([1,0,0]) SHookL();
+}
+
+module THookL(){ ////toplevel
+  mirror([1,0,0]) THookR();
+}
+
+module Demo(){ ////toplevel
+  translate([-30,tubeheight,0]) HookL();
+  translate([  0,tubeheight,0]) HookR();
+  translate([ 30,         0,0]) SHookL();
+  translate([ 60,         0,0]) SHookR();
+  translate([ 90,         0,0]) THookL();
+  translate([120,         0,0]) THookR();
+}
diff --git a/warptest.scad b/warptest.scad
new file mode 100644 (file)
index 0000000..ee5b3b2
--- /dev/null
@@ -0,0 +1,2 @@
+//rotate([0,0,45])
+cube([3,100,25]);
diff --git a/warptest2.scad b/warptest2.scad
new file mode 100644 (file)
index 0000000..d6a11b8
--- /dev/null
@@ -0,0 +1,8 @@
+// -*- C -*-
+rotate([90,0,0])
+linear_extrude(height=50){
+  polygon([[-3/2, 0],
+          [-11/2, 8],
+          [+11/2, 8],
+          [+3/2, 0]]);
+}
diff --git a/warptest3.scad b/warptest3.scad
new file mode 100644 (file)
index 0000000..ee80051
--- /dev/null
@@ -0,0 +1,30 @@
+// -*- C -*-
+
+dy= 145;
+dx=  65;
+
+h1= 8;
+h2= 14;
+ratio = 0.8;
+
+module Plan(){
+  polygon([[ -dx/2,  0    ],
+          [     0,  dy/2 ],
+          [  dx/2,  0    ],
+          [     0, -dy/2 ]]);
+}
+
+module Solid(){
+  rotate([0,0, -45]) {
+    hull(){
+      linear_extrude(height= h1) {
+       Plan();
+      }
+      linear_extrude(height= h2) {
+       scale(ratio) Plan();
+      }
+    }
+  }
+}
+
+Solid();
diff --git a/wine-vacuum-adapter.scad b/wine-vacuum-adapter.scad
new file mode 100644 (file)
index 0000000..5ce11a8
--- /dev/null
@@ -0,0 +1,74 @@
+// -*- C -*-
+
+india_nom = 27.0;
+india_slop = 0.63;
+
+middia_nom = 31.0;
+middia_slop = 0.10;
+
+outdia = 44.0;
+
+wall = 4;
+
+htop = 5;
+hbot = 7;
+
+slope = 0.65;
+
+$fa=3;
+$fs=0.1;
+
+// calculated
+
+india_use = india_nom + india_slop;
+middia_use = middia_nom - middia_slop;
+
+//echo("MIN WALL", (middia_use - india_use)/2);
+
+ppA = [middia_use/2, 0];
+ppB = ppA + [0, 1] * htop;
+ppC = ppB + [-1,0] * wall;
+ppD = [ppC[0], ppA[1] - wall/slope];
+ppE = [india_use/2, ppD[1] - (india_use/2 - ppD[0])/slope];
+ppF = ppE + [0,-1] * (htop + hbot);
+ppG = ppF + [1, 0] * wall;
+ppK = [outdia/2, ppA[1]];
+ppJ = ppK + [0,-1] * wall;
+ppH = [ppG[0], ppJ[1] - (ppJ[0]-ppG[0])/slope];
+
+module Plan1() {
+  polygon([[ india_use/2,    -hbot ],
+          [ outdia/2,       -hbot ],
+          [ outdia/2,       0     ],
+          [ middia_use/2,   0     ],
+          [ middia_use/2,   htop  ],
+          [ india_use/2,    htop  ]]);
+}
+
+module Plan3() {
+  p = [ ppA,
+       ppB,
+       ppC,
+       ppD,
+       ppE,
+       ppF,
+       ppG,
+       ppH,
+       ppJ,
+       ppK ];
+  echo(p);
+  polygon(p);
+}
+
+module Demo(){
+  color("blue") translate([0,0,1]) Plan1();
+  Plan3();
+}
+
+module Adapter(){
+  rotate_extrude(convexity=5)
+    Plan1();
+}
+
+//Demo();
+Adapter();
diff --git a/write-firmware b/write-firmware
new file mode 100755 (executable)
index 0000000..ff75d4b
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/bash
+set -e
+
+usage () { echo 'usage: write-firmware FILE [PORT]'; }
+
+home=/home/reprap/play
+ourfile="$home/Marlin.hex"
+
+infile="$1"
+
+case $# in
+1)   shift; port=/dev/ttyUSB0 ;;
+2)   shift; port="$1"; shift ;;
+*)   usage >&2; exit 1;;
+esac
+
+ad () {
+   avrdude -b 38400 -v -P $port -p atmega644P -c arduino "$@"
+}
+
+cp -v -- "$infile" "$ourfile"
+
+ad "$@"
+ad "$@" -U flash:w:$ourfile
diff --git a/xeno-drivebay-bracket.scad b/xeno-drivebay-bracket.scad
new file mode 100644 (file)
index 0000000..2073e05
--- /dev/null
@@ -0,0 +1,195 @@
+// -*- C -*-
+
+basel = 16;
+basew = 24;
+baset = 4.0;
+
+wallt = 2.5;
+
+wallh = 42;
+
+baseholesz = 3.7;
+baseholeslot = 6.5;
+baseholeslop = -0.5;
+
+holeslop = 0.5;
+
+webt = 2.5;
+
+pad = false;
+padw = 12;
+
+padt = webt;
+padl = padw;
+padholesz = 3.0;
+
+wallholeh = 6+14+2;
+wallholesz = 3.0;
+wallholeslot = 4.5;
+
+walll = basel + webt + (pad ? padl : -0.1);
+
+webw = min(basew, pad ? padw : padt);
+
+module slothole(sz, slot, thick, csunk=true, slop=holeslop) {
+  hull(){
+    for (y = [-slot/2,slot/2]) {
+      translate([0,y,-0.15])
+       cylinder(r1=sz/2 + slop,
+                r2=sz/2 + (csunk ? thick : 0) + slop,
+                h=thick+0.30);
+    }
+  }
+}
+
+module Bracket(){
+  difference(){
+    translate([0, -basew, 0])
+      cube([basel, basew, baset]);
+
+    translate([basel/2, -(basew+wallt)/2, 0])
+      slothole(baseholesz, baseholeslot, baset, slop=baseholeslop);
+  }
+
+  difference(){
+    translate([0.1, 0.3, 0.1])
+    rotate([90,0,0]) {
+      linear_extrude(height=wallt){
+       polygon([[0,0],
+                [0, wallh/2 + wallholesz/2 + wallt + wallt],
+                [basel, wallh],
+                [walll, wallh],
+                [walll, wallh - padt - padt],
+                [basel + webt, 0]]);
+      }
+    }
+
+    translate([basel/2, 0, wallholeh])
+      rotate([90,90,0])
+      slothole(wallholesz, wallholeslot, wallt, csunk=false);
+  }
+
+  translate([basel-0.01, 0, 0]) {
+    rotate([90,0,90]) {
+      linear_extrude(height=webt+0.02) {
+       polygon([[-basew, 0],
+                [-basew, baset],
+                [-webw, wallh],
+                [0, wallh],
+                [0, 0]]);
+      }
+    }
+  }
+
+  if (pad) {
+    translate([basel+webt, -padw, wallh-padt]) {
+      difference(){
+       cube([padl, padw, padt]);
+       translate([padl/2, padw/2, -1])
+         cylinder(r=padholesz/2 + holeslop, h=padt+2);
+      }
+    }
+  }
+}
+
+module BracketR(){ ////toplevel
+  rotate([-90,0,0]) Bracket();
+}
+
+module BracketL(){ ////toplevel
+  mirror([1,0,0]) BracketR();
+}
+
+protinnerh = 47;
+protinnerw = 53;
+protd = 45;
+protbaset = 4;
+protwallt = 2;
+protlidt = protwallt;
+protwingd = 28;
+protwingw = 23;
+
+module RearCableProtector(){
+  for (x = [-protwallt, protinnerw]) {
+    translate([x, 0, 0]) {
+      cube([protwallt, protd, protinnerh+protlidt]);
+    }
+  }
+  translate([-(protwallt-0.1), 0, protinnerh])
+    cube([protinnerw + (protwallt-0.1)*2, protd, protlidt]);
+  for (lr = [1,0]) {
+    translate([(lr ? -(protwingw + protwallt) : protinnerw), 0, 0]) {
+      difference(){
+       translate([0, 0, 0])
+         cube([protwingw, protwingd, protbaset]);
+       translate([protwingw/2, protwingd/2, 0])
+         rotate([0,0, lr ? 45 : -45])
+         slothole(baseholesz, baseholeslot, baset, slop=baseholeslop);
+      }
+    }
+  }
+}
+
+module RearCableProtectorT(){ ////toplevel
+  rotate([90,0,0]) RearCableProtector();
+}
+
+chabd = 20;
+chablidw = 40;
+chabinnerh = 11;
+chabwallt = 2;
+chablidt = 2;
+chabwebt = 2.5;
+chabbaset = baset;
+chabbasew = 20;
+chabslot = 3;
+chablidholed = 3;
+chabwebh = 5;
+
+module ChannelBracket(){
+  translate([0, -chabd, 0])
+    cube([chabwallt, chabd, chabinnerh+chablidt]);
+  translate([-chablidw, -chabd, chabinnerh]) {
+    difference(){
+      cube([chablidw + chabwallt - 0.1, chabd - 0.1, chablidt]);
+      translate([chablidw/2, chabd/2, -1])
+       cylinder(r=chablidholed/2, h=chablidt+2, $fn=20);
+    }
+  }
+  translate([chabwallt-0.1, -chabd, 0]) {
+    difference(){
+      cube([chabbasew, chabd-0.1, chabbaset]);
+      translate([chabbasew/2, (chabd-chabwebt)/2, 0])
+       rotate([0,0,90])
+       slothole(baseholesz, chabslot, baset, slop=baseholeslop);
+    }
+  }
+  rotate([90,0,0]) linear_extrude(height=chabwebt) {
+    polygon([[-chablidw, chabinnerh],
+            [-chablidw, chablidt+chabinnerh],
+            [-chabwebh, chablidt+chabinnerh+chabwebh],
+            [+chabwebh, chablidt+chabinnerh+chabwebh],
+            [+chabbasew, chabbaset],
+            [+chabbasew, 0],
+            [0, 0],
+            [0, chabinnerh]]);
+  }
+}
+
+module ChannelBracketT(){
+  rotate([-90,0,0]) ChannelBracket();
+}
+
+module Kit(){ ////toplevel
+  for (y=[0, -wallh-5]) {
+    translate([0,y,0]) {
+      translate([5,0,0]) BracketR();
+      BracketL();
+    }
+  }
+}
+
+//Kit();
+//BracketR();
+//RearCableProtectorT();
+//ChannelBracketT();
diff --git a/y-large-axlebar-washer.scad b/y-large-axlebar-washer.scad
new file mode 100644 (file)
index 0000000..de8553e
--- /dev/null
@@ -0,0 +1,15 @@
+// -*- C -*-
+
+$fa=3;
+$fs=0.1;
+
+r0 = 12 + 0.75;
+r1 = 22;
+h = 7.36 - 0.20;
+
+linear_extrude(height=h, convexity=3) {
+  difference(){
+    circle(r = r1/2);
+    circle(r = r0/2);
+  }
+}
diff --git a/yubikey-5c-nano-loop.scad b/yubikey-5c-nano-loop.scad
new file mode 100644 (file)
index 0000000..654e11c
--- /dev/null
@@ -0,0 +1,74 @@
+// -*- C -*-
+
+include <utils.scad>
+
+base = [ 8.4, 4.1 ];
+base_th = 0.7;
+base_slope = 2.0;
+
+hoop_th = 2.1;
+hoop_inner_dia = 3.0;
+
+time_square = 8;
+
+$fa = 3;
+$fs = 0.1;
+
+// caclulated
+
+loop_post_z = hoop_inner_dia/2;
+max_z = hoop_inner_dia + hoop_th;
+
+module Base() {
+  hull(){
+    linextr(-base_th, -base_th + 0.01)
+      square(base, center=true);
+    linextr(-base_th, 0) {
+      square(base - 2 * base_th * base_slope * [1,1], center=true);
+      LoopPlan2();
+    }
+  }
+}
+
+module LoopPlan() {
+  translate([hoop_inner_dia/2 + hoop_th/2, 0])
+    circle(r = hoop_th/2);
+}
+
+module LoopPlan2() {
+  for (m=[0,1]) {
+    mirror([m,0,0]) LoopPlan();
+  }
+}
+
+module Loop() {
+  translate([0,0, loop_post_z]) {
+    intersection(){
+      rotate([90, 0,0]){
+       rotate_extrude(){
+         LoopPlan();
+       }
+      }
+      linextr(-0.1, hoop_inner_dia*2)
+       square(hoop_inner_dia*4, center=true);
+    }
+  }
+  linextr(0, loop_post_z)
+    LoopPlan2();
+}
+
+module UseUpTime() {
+  linextr(-base_th, max_z)
+    translate([0, base[0] * 3, 0])
+    square(time_square, center=true);
+}
+
+module Whole() {
+  rotate([0,0, 90]) {
+    Base();
+    Loop();
+  }
+  UseUpTime();
+}
+
+Whole();