--- /dev/null
+*~
+*.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
--- /dev/null
+// -*- 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();
--- /dev/null
+ 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>.
--- /dev/null
+Go back to revision e1cdc45 aka "last-with-huxley".
+
+The files for our old Huxley have been removed from this tree since then.
--- /dev/null
+# reprap-objects Makefile
+#
+# Build scripts for various 3D designs
+# Copyright 2012-2023 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 digispark-with-cable chimney-cable-retainer $(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 diziet-utils/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
+ @$i
+
+.PRECIOUS: maglite-holder-torch-curve.eps
+maglite-holder-torch-curve.eps: maglite-holder-torch.fig
+ fig2dev -D +1:70 -L eps <$< >$@.tmp
+ @$i
+
+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
+ @$i
+
+FILAMENTSPOOL_NUMBERS=$(shell seq 300 100 1500)
+filamentspool-number-n%.eps: filamentspool-number.eps.pl
+ ./$< $* $o
+
+FILAMENTSPOOL_DXFS=$(foreach n,$(FILAMENTSPOOL_NUMBERS), \
+ filamentspool-number-n$n.dxf)
+
+$(addsuffix .auto.stl, $(foreach f,$(FILAMENTSPOOL_AUTOS),$(shell \
+ $(DUTILS)/toplevel-find $(CWD)/$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
+ ./$< $* $o
+
+screw-recess-test-number-s%.eps: screw-recess-test-number-s%.fig
+ fig2dev -L eps <$< >$@.tmp
+ @$i
+
+screw-recess-test-numbers screw-recess-test.stl: $(SCREWRECESSTEST_DXFS)
+
+question-question.eps: question-question.fig
+ fig2dev -L eps <$< >$@.tmp
+ @$i
+
+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
+ @$i
+
+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
+ $i
+
--- /dev/null
+# Diziet's (Ian Jackson)'s 3D models
+
+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.
+
+# Copyright
+
+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.
+
-# 3D printing utility files and build system
+This directory contains source code a number of objects of various
+kinds, and a simple build system.
-This is a set of files I use for making 3D objects.
+In general for a simple file FOO.scad you can say `make FOO.stl'.
-It mostly consists of my own work, but occasionally I include other
-people's work if that other work has a suitable licence.
-# How to use
+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'.
-To add to your project:
-```
-git subtree add -P diziet-utils https://salsa.debian.org/iwj/3d-utils.git main
-```
+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.
-To update to a new version:
-```
-git subtree pull -P diziet-utils https://salsa.debian.org/iwj/3d-utils.git main
-```
+Unless otherwise noted every object and file here is:
-# Licence
+ Copyright Ian Jackson
+ Licenced under the GNU General Public Licence, version 3, or
+ (at your option) any later version. There is NO WARRANTY.
-Everything is GPLv3+ (or compatible).
--- /dev/null
+// -*- 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);
+}
--- /dev/null
+// -*- 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]]);;
+ }
+}
--- /dev/null
+// -*- 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();
+}
--- /dev/null
+// -*- 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();
--- /dev/null
+perimeters = 4
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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]);
+}
--- /dev/null
+// -*- 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);
+ }
+}
--- /dev/null
+// -*- 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();
+}
+
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+fill_angle = 0
+skirt_distance = 1
--- /dev/null
+fill_angle = 0
+skirt_distance = 1
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+
+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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+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();
--- /dev/null
+// -*- 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);
+ }
+}
--- /dev/null
+// -*- C -*-
+
+include <utils.scad>
+
+inrear_d_real = 20;
+inrear_d = 15;
+bar_th = 5;
+general_th = 5;
+between_cables = 150;
+around_cables = 20;
+cable_dia = 15;
+
+total_d = 40;
+above_h = 40;
+
+// calculated
+
+cable_x = around_cables + cable_dia/2;
+total_x = cable_x * 2 + between_cables;
+
+below_h = above_h;
+
+sit_angle = atan2(inrear_d_real - inrear_d, below_h);
+
+module CoreElevation(){
+ rotate(-sit_angle)
+ rectfromto([ 0, 0 ],
+ [ total_d, general_th ]);
+ rectfromto([ 0, 0 ],
+ [ general_th, above_h ]);
+ translate([ inrear_d, -above_h ])
+ rectfromto([ 0,0 ],
+ [ -bar_th, bar_th ]);
+}
+
+module BarMountElevation(){
+ hull(){
+ rotate(-sit_angle)
+ rectfromto([ 0, 0 ],
+ [ inrear_d, general_th ]);
+ translate([ 0, -below_h ])
+ rectfromto([ 0,0 ],
+ [ inrear_d, bar_th ]);
+ }
+}
+
+module Retainer(){ ////toplevel
+ difference(){
+ union(){
+ linextr_x_yz(0, total_x)
+ mirror([1,0])
+ CoreElevation();
+
+ for (x = [0, 0.5, 1] * (total_x - general_th))
+ translate([ x, 0,0 ])
+ linextr_x_yz(0, general_th)
+ mirror([1,0])
+ BarMountElevation();
+ }
+
+ for (x = [cable_x, total_x - cable_x])
+ translate([x, 0, 0])
+ linextr(-below_h/2, 100)
+ hull(){
+ translate([ 0, -(general_th + cable_dia/2) ])
+ circle(r = cable_dia/2);
+ translate([ 0, -(total_d + 1)])
+ square([ cable_dia, 1], center=true);
+ }
+ }
+}
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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);
+}
--- /dev/null
+// -*- C -*-
+include <commitid.scad>
+Commitid_2DDemo();
--- /dev/null
+#!/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;
+}
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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);
+ }
+}
+
--- /dev/null
+// -*- 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);
--- /dev/null
+// -*- C -*-
+//
+// Print (fine detail settings):
+//
+// * Bottom
+// * MiddlePrint
+// * CoverPrint
+
+include <utils.scad>
+
+usb_w = 12.01 + 0.19;
+usb_wall_w = 0.51;
+usb_tongue_d = 8.97 - 0.2;
+usb_tongue_w_slop = +0.5;
+usb_wall_h = 4.54 - 2.04;
+usb_ceil_th = 0.425;
+
+wall_th = 1.5; // XXXX rename wall_th
+
+board_l = 17.56 + 0.2;
+board_w = 19.14 + 0.2;
+board_th = 1.92 + 0.1;
+
+sw_to_edge = board_w/2 + 0.1;
+
+front_wall_th = 0.75;
+// egress_w = 8.0;
+
+wall_y_min = -board_l - wall_th;
+ceil_y_min = wall_y_min - 5;;
+
+small_walls = [
+ [ [0, 0], [-sw_to_edge, -1.0] ],
+ [ [sw_to_edge-4.5, -4.5], [sw_to_edge, -5.7] ],
+// [ [3.0, -11.72], [sw_to_edge, -13.38] ],
+ [ [-sw_to_edge+3.85, -14.90], [sw_to_edge, -13.38] ],
+ ];
+chip_cutout = [[ -sw_to_edge + 4.20, -3.75 ],
+ [ -sw_to_edge + 11.95, -11.90 ]];
+
+strain_w = 3.5 + 0.5;
+strain_t = 1.5 + 0.5;
+strain_pitch_across = 5;
+strain_pitch_along = 10;
+strain_groove_d = 2;
+strain_groove_w = 4.5;
+strain_around = [2.25, 2.00];
+
+cover_strap_c_d_y = 5.5; // from front of board
+cover_registration_sz_y = 2;
+cover_registration_sz_z = 3;
+midbot_registraton_sz_x = 3;
+
+cable_space_z = 6;
+cable_dia = 6;
+bottom_floor_th = 1.5;
+
+fit_gap_z = 0.5;
+fit_gap_y = 0.25;
+side_x_gap = 0.5;
+
+cover_ceil_th = 0.9;
+
+cover_strap_sz_x = wall_th * 3.5;
+
+// calculated
+
+strap_w = strain_w;
+cover_strap_cutout_z = wall_th;
+
+middle_top_z = usb_wall_h;
+middle_base_z = -board_th;
+bottom_base_z = middle_base_z - cable_space_z - bottom_floor_th;;
+
+front_y_max = front_wall_th;
+main_y_min = -board_l - wall_th;
+
+strain_0_y_c = main_y_min - strain_w/2;
+strain_1_y_c = strain_0_y_c - strain_pitch_along;
+total_y_min = strain_1_y_c - strain_w/2 - wall_th;
+
+bottom_wall_top_z = (middle_top_z + middle_base_z) * 0.5 - fit_gap_z/2;
+cover_wall_bot_z = (middle_top_z + middle_base_z) * 0.5 + fit_gap_z/2;
+cover_top_z = middle_top_z + cover_ceil_th;
+
+middle_side_wall_x = +board_w/2 + wall_th;
+total_side_wall_x = middle_side_wall_x + wall_th + side_x_gap;
+
+cover_registration_c_dy = -cover_strap_c_d_y - strap_w/2
+ - wall_th - cover_registration_sz_y/2;
+
+midbot_registration_sz_y = cover_registration_sz_y;
+midbot_registration_sz_z = cover_registration_sz_z;
+midbot_registration_y_min = total_y_min + wall_th*2;
+midbot_registration_y_max = midbot_registration_y_min
+ + midbot_registration_sz_y;
+midbot_registration_y_around_max = midbot_registration_y_max
+ + wall_th*2;
+midbot_registration_bottom_x = board_w/2 - midbot_registraton_sz_x;
+
+midbot_strap_c_y = 0.5 * (strain_0_y_c + strain_1_y_c);
+
+module BothSides(){
+ for (m=[0,1]) {
+ mirror([m,0,0]) {
+ children();
+ }
+ }
+}
+
+module NormalStrapCutouts(y_c, z, rot){
+ BothSides(){
+ translate([ -total_side_wall_x, y_c, z ])
+ rotate([0, rot, 0])
+ cube([ wall_th,
+ strap_w,
+ 10 ],
+ center=true);
+ }
+}
+module BottomStrapCutouts(y_c){
+ NormalStrapCutouts(y_c, bottom_base_z, -45);
+}
+module CoverStrapCutouts(){
+ BothSides(){
+ translate([ -total_side_wall_x, -cover_strap_c_d_y, cover_top_z ])
+ cube([ cover_strap_sz_x*2,
+ strap_w,
+ cover_strap_cutout_z*2 ],
+ center=true);
+ }
+}
+
+module FrontWallsPlan(slop) {
+ BothSides(){
+ rectfromto([ -board_w/2 - wall_th, 0 ],
+ [ -usb_w/2 - slop, front_wall_th ]);
+ }
+}
+module MiddleSmallWallsPlan() {
+ for (m=[0,1]) {
+ mirror([m,0]) {
+ rectfromto([ -usb_w/2, -0.01 ],
+ [ -usb_w/2 + usb_wall_w, usb_tongue_d ]);
+ }
+ }
+ FrontWallsPlan(0);
+ for (w=small_walls) {
+ rectfromto(w[0], w[1]);
+ }
+}
+module MiddleCeilPlan() {
+ difference(){
+ BothSides(){
+ rectfromto([ -usb_w/2, -0.01 ],
+ [ 0.1, usb_tongue_d ]);
+ rectfromto([ -board_w/2 - wall_th, 0 ],
+ [ 0.1, ceil_y_min ]);
+ }
+ rectfromto(chip_cutout[0], chip_cutout[1]);
+ }
+}
+module MiddleMainWallsPlan() {
+ BothSides(){
+ rectfromto([ -board_w/2 - wall_th, 0 ],
+ [ -board_w/2, wall_y_min ]);
+ }
+ FrontWallsPlan(usb_tongue_w_slop);
+ rectfromto([ -board_w/2 - wall_th + 0, - board_l ],
+ [ +board_w/2 + wall_th, total_y_min ]);
+}
+
+module RegistrationsMinkowski(){
+ minkowski(){
+ cube([ 1, fit_gap_y*2, fit_gap_z*2 ], center=true);
+ children();
+ }
+}
+module CoverRegistrations(){
+ linextr_y_xz(cover_registration_c_dy - strap_w/2,
+ cover_registration_c_dy + strap_w/2) {
+ difference(){
+ rectfromto([ -total_side_wall_x,
+ cover_wall_bot_z - cover_registration_sz_z ],
+ [ +total_side_wall_x, cover_top_z ]);
+ hull(){
+ MiddleElevationForCutout();
+ translate([0, -20]) MiddleElevationForCutout();
+ }
+ }
+ }
+}
+module MidBotRegistrations(){
+ linextr_y_xz(midbot_registration_y_min,
+ midbot_registration_y_max) {
+ BothSides(){
+ rectfromto([ midbot_registration_bottom_x, middle_base_z + 0.1 ],
+ [ middle_side_wall_x, middle_base_z
+ - midbot_registration_sz_z ]);
+ }
+ }
+}
+
+module MiddleStrainHoles(){
+ BothSides(){
+ for (y_c = [strain_0_y_c, strain_1_y_c]) {
+ translate([strain_pitch_across/2, y_c, 0])
+ square([ strain_t, strain_w ], center=true);
+ }
+ }
+}
+module Middle(){ ////toplevel
+ difference(){
+ union(){
+ linextr(0, usb_wall_h)
+ MiddleSmallWallsPlan();
+ linextr(usb_wall_h - usb_ceil_th, usb_wall_h)
+ MiddleCeilPlan();
+ linextr(-board_th, usb_wall_h)
+ MiddleMainWallsPlan();
+ BothSides()
+ linextr(cover_wall_bot_z, middle_top_z)
+ rectfromto([ -(board_w/2 + 0.1), total_y_min ],
+ [ -total_side_wall_x, main_y_min - fit_gap_y ]);
+ MidBotRegistrations();
+ }
+
+ linextr(-20, 20)
+ MiddleStrainHoles();
+ linextr_y_xz(total_y_min-1, main_y_min)
+ translate([0, middle_base_z])
+ scale([1, strain_groove_d/strain_groove_w])
+ circle(strain_groove_w/2, $fn = 8);
+ NormalStrapCutouts(midbot_strap_c_y,
+ middle_top_z, 45);
+ }
+}
+module MiddlePrint(){ ////toplevel
+ rotate([180,0,0]) Middle();
+}
+
+module MiddleElevationForCutout(){
+ rectfromto([ -(middle_side_wall_x + side_x_gap),
+ middle_base_z - fit_gap_z ],
+ [ +(middle_side_wall_x + side_x_gap), middle_top_z ]);
+}
+module BottomMainElevation(){
+ difference(){
+ rectfromto([ -total_side_wall_x, bottom_base_z ],
+ [ +total_side_wall_x, bottom_wall_top_z ]);
+
+ MiddleElevationForCutout();
+ }
+}
+module Bottom(){ ////toplevel
+ difference(){
+ union(){
+ linextr_y_xz(total_y_min, front_y_max)
+ BottomMainElevation();
+ }
+
+ linextr_y_xz(midbot_registration_y_around_max,
+ front_y_max - wall_th)
+ rectfromto([ -board_w/2, bottom_base_z + bottom_floor_th ],
+ [ +board_w/2, 20 ]);
+
+ linextr_y_xz(total_y_min + wall_th,
+ front_y_max - wall_th)
+ rectfromto([ -midbot_registration_bottom_x,
+ bottom_base_z + bottom_floor_th ],
+ [ +midbot_registration_bottom_x, 20 ]);
+
+ linextr_y_xz(total_y_min - 1,
+ total_y_min + wall_th + 1){
+ translate([ 0, middle_base_z ]){
+ hull(){
+ translate([ 0, -cable_dia/2 ])
+ circle(r = cable_dia/2, $fa = 10, $fs = 1);
+ square([ cable_dia, 0.1 ], center=true);
+ }
+ }
+ }
+ RegistrationsMinkowski()
+ CoverRegistrations();
+ RegistrationsMinkowski()
+ MidBotRegistrations();
+ BottomStrapCutouts(-cover_strap_c_d_y);
+ BottomStrapCutouts(midbot_strap_c_y);
+ }
+}
+
+module CoverMainElevation(){
+ difference(){
+ rectfromto([ -total_side_wall_x, cover_wall_bot_z ],
+ [ +total_side_wall_x, cover_top_z ]);
+
+ MiddleElevationForCutout();
+ }
+}
+module Cover(){ ////toplevel
+ difference(){
+ union(){
+ linextr_y_xz(main_y_min, front_y_max)
+ CoverMainElevation();
+ CoverRegistrations();
+ }
+ CoverStrapCutouts();
+ linextr(-20,20) {
+ minkowski(){
+ square(strain_around * 2, center=true);
+ hull() MiddleStrainHoles();
+ }
+ }
+ }
+}
+module CoverPrint(){ ////toplevel
+ rotate([180,0,0]) Cover();
+}
+
+module BottomDemo(){ ////toplevel
+ translate([0, 0, -0.25]) Bottom();
+ %Middle();
+ translate([0, 0, +0.25]) Cover();
+}
+module ImpressionDemo(){ ////toplevel
+ color("black") translate([0, 0, -0.25]) Bottom();
+ %Middle();
+ %translate([0, 0, +0.25]) Cover();
+}
--- /dev/null
+#!/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";
+}
--- /dev/null
+ 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>.
--- /dev/null
+# 3D printing utility files and build system
+
+This is a set of files I use for making 3D objects.
+
+It mostly consists of my own work, but occasionally I include other
+people's work if that other work has a suitable licence.
+
+# How to use
+
+To add to your project:
+
+```
+git subtree add -P diziet-utils https://salsa.debian.org/iwj/3d-utils.git main
+```
+
+To update to a new version:
+
+```
+git subtree pull -P diziet-utils https://salsa.debian.org/iwj/3d-utils.git main
+```
+
+# Licence
+
+Everything is GPLv3+ (or compatible).
--- /dev/null
+/*\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
--- /dev/null
+// -*- 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();
+}
--- /dev/null
+// -*- 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);
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
+ }
+ }
+}
--- /dev/null
+bed_temperature = 80
+temperature = 205
--- /dev/null
+// -*- 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();
--- /dev/null
+#!/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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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;
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- C -*-
+
+//// toplevels-from:
+include <fairphone4-case.scad>
+
+$fa = 20;
+$fs = 2;
--- /dev/null
+// -*- 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;
--- /dev/null
+// -*- 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;
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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]);
+}
--- /dev/null
+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();
--- /dev/null
+//// toplevels-from:
+include <filamentspool.scad>
+lightduty = true;
+fdia = 2.85;
--- /dev/null
+#!/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
--- /dev/null
+//// toplevels-from:
+include <filamentspool.scad>
+lightduty = true;
+fdia = 1.75;
--- /dev/null
+// -*- C -*-
+include <filamentspool.scad>
+lightduty = false;
+fdia = 2.85;
+storarm_spools=3;
+//// module StorageArmLeft ////toplevel
+//// module StorageArmRight ////toplevel
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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]);
+ }
+ }
+}
+
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+first_layer_extrusion_width = 100%
--- /dev/null
+// -*- 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));
--- /dev/null
+// -*- 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]);
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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));
+ }
+ }
+ }
+}
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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);
+}
--- /dev/null
+fill_density = 0.1
--- /dev/null
+#!/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 $!;
+}
--- /dev/null
+#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
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
+ }
+}
--- /dev/null
+// -*- 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();
--- /dev/null
+bottom_solid_layers = 3
+top_solid_layers = 3
+perimeters = 3
--- /dev/null
+#!/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();
--- /dev/null
+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]);
+}
--- /dev/null
+// -*- 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(){ } }
+}
+
--- /dev/null
+// -*- 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();
+
--- /dev/null
+// -*- C -*-
+
+include <utils.scad>
+
+$fa = 1;
+$fs = 0.1;
+
+linextr(0, 0.425 + 0.125 * 2) {
+ difference(){
+ circle(r= 15 /2);
+ circle(r= (8 + 0.5) /2);
+ }
+}
--- /dev/null
+#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
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+#!/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);
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- C -*-
+
+//// toplevels-from:
+include <nook-case.scad>
+
+$test = true;
--- /dev/null
+// -*- 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();
+}
--- /dev/null
+// -*- 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);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+#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
--- /dev/null
+// -*- 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();
--- /dev/null
+fill_density = 0.5
--- /dev/null
+#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
--- /dev/null
+// -*- 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);
--- /dev/null
+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]);
+}
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+first_layer_bed_temperature = 80
+bed_temperature = 70
+skirts = 3
+first_layer_temperature = 190
+temperature = 177
+extrusion_multiplier = 0.9
+fill_density = 1.0
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- C -*-
+
+//// toplevels-from:
+include <poster-tube-lid.scad>
+
+coarse = true;
--- /dev/null
+#!/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();
--- /dev/null
+// -*- 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();
--- /dev/null
+%!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
--- /dev/null
+<?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>
--- /dev/null
+// -*- 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();
+}
--- /dev/null
+// -*- 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();
--- /dev/null
+// autogenerated by quacks-ingredients-updates-levels - do not edit
+$phase=1;
+module Token_L(){ Token_L1(); }
+//// toplevels-from:
+include <quacks-ingredients.scad>
--- /dev/null
+// autogenerated by quacks-ingredients-updates-levels - do not edit
+$phase=2;
+module Token_L(){ Token_L2(); }
+//// toplevels-from:
+include <quacks-ingredients.scad>
--- /dev/null
+// autogenerated by quacks-ingredients-updates-levels - do not edit
+$phase=3;
+module Token_L(){ Token_L3(); }
+//// toplevels-from:
+include <quacks-ingredients.scad>
--- /dev/null
+// autogenerated by quacks-ingredients-updates-levels - do not edit
+$phase=4;
+module Token_L(){ Token_L4(); }
+//// toplevels-from:
+include <quacks-ingredients.scad>
--- /dev/null
+// autogenerated by quacks-ingredients-updates-levels - do not edit
+$phase=5;
+module Token_L(){ Token_L5(); }
+//// toplevels-from:
+include <quacks-ingredients.scad>
--- /dev/null
+#!/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
--- /dev/null
+// 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
--- /dev/null
+// -*- 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);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+// -*- 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();
--- /dev/null
+[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)
+
--- /dev/null
+#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
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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);
+ }
+ }
+}
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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(); }
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- C -*-
+
+// Per linear bracket print
+// LinearBracketA
+// GeneralB
+// Pin
+
+//// toplevels-from:
+include <scaffold-clamp-common.scad>
+
+module DemoA(){ LinearBracketA(); }
--- /dev/null
+// -*- 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(); }
--- /dev/null
+// -*- C -*-
+
+// Per tensioner print
+// VHookA
+// GeneralB
+// Pin
+
+//// toplevels-from:
+include <scaffold-clamp-common.scad>
+
+module DemoA(){ VHookA(); }
--- /dev/null
+#!/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
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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')
--- /dev/null
+// -*- 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 ],
+ ]);
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+//// 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;
--- /dev/null
+#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
--- /dev/null
+//// 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;
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+//// toplevels-from:sewing-table.scad
+include <simplephone-case.scad>
+$test = true;
--- /dev/null
+// -*- 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);
--- /dev/null
+; -*- 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
--- /dev/null
+// -*- 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();
--- /dev/null
+# 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
--- /dev/null
+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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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]);
+ }
+}
--- /dev/null
+// -*- 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();
--- /dev/null
+#!/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
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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);
+}
--- /dev/null
+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);
+}
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
+++ /dev/null
-/*\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
--- /dev/null
+diziet-utils/threads.scad
\ No newline at end of file
--- /dev/null
+// -*- 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();
+ }
+}
--- /dev/null
+// -*- 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);
--- /dev/null
+/* -*- 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();
+}
--- /dev/null
+// -*- 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();
--- /dev/null
+// 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();
+}
--- /dev/null
+#!/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();
+
+
--- /dev/null
+// -*- 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();
+++ /dev/null
-// -*- 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();
-}
--- /dev/null
+diziet-utils/utils.scad
\ No newline at end of file
--- /dev/null
+// -*- 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();
--- /dev/null
+solid_infill_every_layers = 11
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
+}
--- /dev/null
+//rotate([0,0,45])
+cube([3,100,25]);
--- /dev/null
+// -*- C -*-
+rotate([90,0,0])
+linear_extrude(height=50){
+ polygon([[-3/2, 0],
+ [-11/2, 8],
+ [+11/2, 8],
+ [+3/2, 0]]);
+}
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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();
--- /dev/null
+// -*- 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);
+ }
+}
--- /dev/null
+// -*- 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();