From: Ian Jackson Date: Thu, 28 Dec 2023 11:14:32 +0000 (+0000) Subject: Merge commit '875be22af707972efae3359b08ec78a328c91f59' X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=3eac3c6395f84d9fc5ed6bdcd91e36ae1c7d10b9;hp=875be22af707972efae3359b08ec78a328c91f59;p=reprap-play.git Merge commit '875be22af707972efae3359b08ec78a328c91f59' Signed-off-by: Ian Jackson --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f328663 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +*~ +*.gcode +light-bracket.stl +*.aside +*.stl +*,*.auto.scad +.*.d +*.tmp +*.fig.bak +funcs.scad +nutbox.scad +powerbank-anker-10000.dxf* +knifeblock-knives-*.dxf* +pandemic-counter-l*.dxf +pandemic-counter-l*.eps +pandemic-quarantine-l*.dxf +pandemic-quarantine-l*.eps +screw-recess-test-number-s*.* +question-question.dxf +question-question.eps +lemon-stand.scad +electron-token.scad +commitid.scad +commitid-best-test.scad +filamentspool-number-n*.dxf +filamentspool-number-n*.eps +sealing-box.scad +sewing-table.scad +maglite-holder-torch-curve.dxf +maglite-holder-torch-curve.eps +sewing-table-rear-profile.dxf +sewing-table-rear-profile.eps +sewing-table-front-profile.dxf +sewing-table-front-profile.eps +sewing-table-end-profile.dxf +sewing-table-end-profile.eps +sewing-table,Demo-flat.png +poster-tube-lid-parametric.scad +treefoil.scad +quacks-L*.auto.ini diff --git a/35mmjack-dummy.scad b/35mmjack-dummy.scad new file mode 100644 index 0000000..1a675a5 --- /dev/null +++ b/35mmjack-dummy.scad @@ -0,0 +1,32 @@ +// -*- C -*- + +p2 = [ 0, 3.0 /2 ]; +p1 = p2 + [ -1.0, -1.0 ]; +p3 = [ 3.0, 2.5 /2 ]; +p4 = [ p3[0] + (3.2-2.5)/2 , 3.2 /2 ]; +p8 = [ 13.0, 8.0 /2 ]; +p5 = [ p8[0] - 8.5, 3.2 /2 ]; +p6 = [ p5[0] + (3.5-3.2)/2, 3.5 /2]; +p7 = [ p8[0], p6[1] ]; +p9 = p8 + [ 10, 0 ]; + +$fa = 1; +$fs = 0.1; + +module Plan(){ + polygon([[ p1[0], 0.1 ], + p1, p2, p3, p4, p5, p6, p7, p8, p9, + [ p9[0], 0.1 ]]); +} + +module Dummy(){ + rotate_extrude() + rotate([0,0,-90]) + Plan(); + translate([0,0, -p1[0]]) + mirror([0,0,1]) + cylinder(r= 0.2, h= p9[0] - p1[0]); +} + +//Plan(); +Dummy(); diff --git a/GPL-3 b/GPL-3 new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/GPL-3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/Huxley-INSTRUCTIONS b/Huxley-INSTRUCTIONS new file mode 100644 index 0000000..45f9b77 --- /dev/null +++ b/Huxley-INSTRUCTIONS @@ -0,0 +1,3 @@ +Go back to revision e1cdc45 aka "last-with-huxley". + +The files for our old Huxley have been removed from this tree since then. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f2302b6 --- /dev/null +++ b/Makefile @@ -0,0 +1,150 @@ +# 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 . + +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 $@.tmp + $i + diff --git a/README b/README new file mode 100644 index 0000000..5c5af41 --- /dev/null +++ b/README @@ -0,0 +1,27 @@ +# 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. + diff --git a/README.md b/README.md index ec4c50d..36ca447 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,26 @@ -# 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). diff --git a/adafruit-powerboost-1000.scad b/adafruit-powerboost-1000.scad new file mode 100644 index 0000000..683eb81 --- /dev/null +++ b/adafruit-powerboost-1000.scad @@ -0,0 +1,175 @@ +// -*- C -*- + +psu_sz_nom = [ 11.43*2, 36.20 ]; + +//// toplevels-from: +include + +psu_hole_pos = [ 2.05, // from back edge of psu_sz[0] + 0.55 * 0.5 * 25.4, // from centreline + ]; + +psu_led_low_x = 4; + +psu_led_usbend_y_min = 4.5; +psu_led_chrg_min_x = -1.0; +psu_led_chrg_max_x = 3.0; +psu_led_chrg_both_sz_y = 9.4; + +psu_led_low_sz_x = 4.5; +psu_led_low_min_y = -1.5; +psu_led_low_max_y = 3.25; + +psu_led_baffle_th = 0.8; +psu_led_baffle_ends = 1.5; + +psu_baffle_th = [ 0.8, 3.5 ]; +psu_innerend_led_depth = 7; + +psu_led_legend_line = 0.75; + +psu_led_legend_battery_l = 6.0; +psu_led_legend_battery_w = 4.0; +psu_led_legend_battery_nub_l = 0.75; +psu_led_legend_battery_nub_w = 1.5; + +psu_led_legend_power_dia = 5.0; + +psu_led_legend_gap = 1.25; + +// ----- calculated ----- + +psu_led_legend_battery_edge = psu_led_legend_line; + +psu_led_legend_power_tick_l = + psu_led_legend_power_dia * 0.65; +psu_led_legend_power_tick_dy = psu_led_legend_line; + +psu_led_legend_power_sz_y = + psu_led_legend_power_dia/2 + psu_led_legend_power_tick_l/2 + - psu_led_legend_power_tick_dy; + +psu_innerend_led_x_midder = - psu_hole_pos[1] - psu_hole_dia/2; + +module PsuLedBafflePlan(){ + AtPsuMountCorner(0,0) { + translate([ (psu_led_chrg_min_x + psu_led_chrg_max_x)/2, + psu_led_usbend_y_min + psu_led_chrg_both_sz_y/2 ]) + square(center=true, + [ psu_led_chrg_max_x - psu_led_chrg_min_x + + psu_led_baffle_ends*2, + psu_led_baffle_th ]); + } +} + +module PsuLedWindowsPlanCore(){ + difference(){ + union(){ + // Two LEDs one side of inlet connector + // "Full" (near edge) and "Chrg" (inner) + AtPsuMountCorner(0,0) { + translate([0, psu_led_usbend_y_min ]) + rectfromto([ psu_led_chrg_min_x, 0 ], + [ psu_led_chrg_max_x, + psu_led_chrg_both_sz_y ]); + } + + // One LED, "Low", other side of inlet connector + AtPsuMountCorner(1,0) { + translate([0, psu_led_usbend_y_min ]) + rectfromto([ 0, + psu_led_low_min_y ], + [ psu_led_low_sz_x, + psu_led_low_max_y ]); + } + + // One LED, PWR, near outlet USB pads + AtPsuMountCorner(0,1){ + rectfromto([0,0], + [psu_sz[0]/2 + psu_innerend_led_x_midder, + psu_innerend_led_depth]); + } + } + } +} + +module PsuLedLegendBattery(percent=50){ + e = psu_led_legend_battery_edge; + empty_l = (100-percent)/100 * (psu_led_legend_battery_l - e*2); + difference(){ + union(){ + square([psu_led_legend_battery_l, + psu_led_legend_battery_w], center=true); + translate([psu_led_legend_battery_l/2, 0]) + square([psu_led_legend_battery_nub_l*2, + psu_led_legend_battery_nub_w], center=true); + } + if (empty_l > 0) + translate([-(psu_led_legend_battery_l/2-e), + -(psu_led_legend_battery_w/2-e)]) + square([empty_l, psu_led_legend_battery_w - e*2]); + } +} + +module PsuLedLegendPowerSymbol(){ + $fn=30; + tick_mid = [0, psu_led_legend_power_dia/2 - psu_led_legend_power_tick_dy]; + + cut_slope = ( psu_led_legend_gap + psu_led_legend_line/2 ) / tick_mid[1]; + cut_y = psu_led_legend_power_dia + 1; + + translate(tick_mid) + square([psu_led_legend_line, psu_led_legend_power_tick_l], center=true); + + difference(){ + circle(r= psu_led_legend_power_dia/2); + circle(r= psu_led_legend_power_dia/2 - psu_led_legend_line); + + polygon([[0, 0], + [-cut_y * cut_slope, cut_y], + [ cut_y * cut_slope, cut_y]]); + + if(0) translate(tick_mid) + square([psu_led_legend_line, psu_led_legend_power_tick_l] + + [psu_led_legend_gap*2, 0.1], + center=true); + } +} + +module PsuLedLegendsPlan(){ + translate([psu_led_legend_power_dia/2 + + psu_innerend_led_x_midder + + psu_led_legend_gap, + psu_sz[1]/2 + - psu_innerend_led_depth/2 + ]) + rotate([0,0,180]) + PsuLedLegendPowerSymbol(); + + for (full=[0,1]) { + translate([-psu_sz[0]/2 + + psu_led_legend_battery_l/2 + + psu_led_chrg_max_x + + psu_led_legend_gap, + -psu_sz[1]/2 + + psu_led_usbend_y_min + + psu_led_chrg_both_sz_y * 0.5 + + max( + psu_led_legend_battery_w + psu_led_legend_gap, + psu_led_chrg_both_sz_y * 0.5 + ) * (0.5 - full) + ]) + PsuLedLegendBattery(full ? 100 : 50); + } + + translate([psu_sz[0]/2 + - psu_led_legend_battery_nub_l + - psu_led_legend_battery_l/2, + -psu_sz[1]/2 + + psu_led_legend_gap + + psu_led_usbend_y_min + + psu_led_low_max_y + + psu_led_legend_battery_w/2 + ]) + PsuLedLegendBattery(0); +} diff --git a/adafruit-powerboost-500.scad b/adafruit-powerboost-500.scad new file mode 100644 index 0000000..d10002e --- /dev/null +++ b/adafruit-powerboost-500.scad @@ -0,0 +1,66 @@ +// -*- C -*- + +psu_sz_nom = [ 21.59, 35.56 ]; + +//// toplevels-from: +include + +psu_baffle_cnr_y = 7.45; // from connector end +psu_baffle_th = [ 0.8, 3.5 ]; +psu_usbend_led_x = 4.5; +psu_innerend_led_depth = 10; + +// ----- calculated ----- + +psu_usbend_led_depth = psu_baffle_cnr_y*2 - psu_usbend_led_x; + + +module PsuLedBafflePlan(){ + baffle_tr = [0, psu_baffle_cnr_y] + + 0.5 * [psu_baffle_th[1], psu_baffle_th[0]]; + translate([0, -psu_sz[1]/2]) { + mirror([1,0,0]) { + rectfromto([-psu_baffle_th[1]/2, 0], + baffle_tr); + rectfromto([-psu_sz[0]/2 - psu_board_support_wall *2, + baffle_tr[1] - psu_baffle_th[0]], + baffle_tr); + } + } +} + +module PsuLedLegendsPlan(){ +} + +module PsuLedWindowsPlanCore(){ + difference(){ + union(){ + // Two LEDs incl "Chrg", one side of inlet connector + AtPsuMountCorner(1,0) { + rectfromto([ -(psu_board_support_wall + 0.1), + +psu_usbend_led_x ], + [ psu_sz[0]/2, + +psu_usbend_led_depth ]); + } + + // One LED, "Low", other side of inlet connector + AtPsuMountCorner(0,0) { + sz = psu_baffle_cnr_y - psu_board_support_wall - psu_baffle_th[0]; + translate([0, psu_baffle_cnr_y]) + rectfromto([ -(psu_board_support_wall + 0.1), + -sz/2 ], + [ psu_sz[0]/2, + +sz/2 ]); + } + + // One LED, PWR, near outlet USB pads + AtPsuMountCorner(0,1){ + rectfromto([0,0], + [psu_sz[0]/2 - psu_hole_pos[1] - psu_hole_dia/2, + psu_innerend_led_depth]); + } + } + translate([0, -psu_sz[1]/2]) + square(center=true, [psu_baffle_th[1], psu_sz[1]]);; + } +} diff --git a/adafruit-powerboost-common.scad b/adafruit-powerboost-common.scad new file mode 100644 index 0000000..b62643a --- /dev/null +++ b/adafruit-powerboost-common.scad @@ -0,0 +1,337 @@ +// -*- C -*- + +include +include + +psu_sz = psu_sz_nom + [ 0.11, 0.44 ] + [ 0.25, 0.25 ]; + +psu_hole_pos = [ 2.05, // from back edge of psu_sz[0] + 0.55 * 0.5 * 25.4, // from centreline + ]; + +psu_th = 1.70 + 0.25; +psu_th_for_clamp = 1.50; + +psu_hole_dia = 2.5 - 0.5; +psu_connector_z = 2.9 + 0.1; +psu_connector_z_overlap = 0.15; +psu_connector_depth = 6.25; +psu_connector_w = 8.0 + 0.5; +psu_usb_protr = 0.6; + +psu_clamp_th = 4.0 + 0.75; +psu_clamp_w = 8.0; +psu_clamp_gap = 0.4; + +psu_board_clamp_ovlp = 4.5; +psu_board_nutbox = nutbox_data_M3; + +psu_board_gap = 0.5; +psu_board_support_wall = 2; +psu_board_support_ovlp = 4.5; +psu_board_support_ovlp_ceil = 2; +psu_board_support_z = 2; + +psu_baffle_gap = 1.0 + 0.5; + +psu_y = +psu_sz[1]/2 + psu_usb_protr; + +psu_usba_v_apart = 7.0; +psu_usba_v_from_edge = 4.86; +psu_usba_v_space_below = 1.5; +psu_usba_v_space_w = 1.7; +psu_usba_v_space_l = 3.0; + +psu_test_ceil = 2.5; + +// ----- calculated ----- + +psu_z = NutBox_h_base(psu_board_nutbox); +psu_z_down = psu_z + 0.1; +psu_fix_sz = NutBox_outer_size(psu_board_nutbox); +psu_board_nutbox_y = psu_sz[1]/2 + psu_board_nutbox[0]/2; + +psu_mount_outer_sz_x = psu_sz[0] + psu_board_support_wall * 2; // centred +psu_mount_outer_sz_y = psu_y + max(psu_board_support_wall, // at psu_y + psu_board_nutbox_y + psu_fix_sz/2); + +module PsuBoardRepresentation(){ + linear_extrude(height= psu_th) + square(center=true, [psu_sz[0],psu_sz[1]]); +} + +module PsuRepresentation(){ + PsuBoardRepresentation(); + translate([0, -psu_sz[1]/2, -psu_connector_z]) + linear_extrude(height= psu_connector_z + psu_connector_z_overlap) + rectfromto([ -psu_connector_w/2, -10 ], + [ +psu_connector_w/2, psu_connector_depth ]); +} + +module AtPsuMountCorner(mx,my){ + mirror([mx,0,0]) + mirror([0,my,0]) + translate(-0.5 * [psu_sz[0], psu_sz[1], 0] + -1 * [0,0, psu_z_down]) + children(); +} + +module PsuMountCornerExtrude(mx,my, plus_z=psu_board_support_z){ + AtPsuMountCorner(mx,my){ + linear_extrude(height= psu_z_down + plus_z, convexity=10) { + children(); + } + } +} + +module PsuUsbAVSpacePlan(){ + for (x= [-1,+1] * psu_usba_v_apart/2) { + translate([x, -psu_usba_v_from_edge ]) { + hull(){ + for (y= [-1,+1] * 0.5 * (psu_usba_v_space_l - psu_usba_v_space_w)) { + translate([0,y]) + circle(r= psu_usba_v_space_w); + } + } + } + } +} + +module PsuMountPositiveMain(){ + for (mx=[0,1]) { + for (my=[0,1]) { + PsuMountCornerExtrude(mx,my){ + rectfromto(-[1,1]*psu_board_support_wall, + +[1,1]*psu_board_support_ovlp); + } + } + // mount above at plug end + PsuMountCornerExtrude(mx,0, psu_th + psu_board_support_wall){ + rectfromto(-[1,1]*psu_board_support_wall, + [psu_board_support_ovlp, + psu_board_support_ovlp_ceil]); + } + } + translate([0,0, -psu_z_down]) + linear_extrude(psu_z_down - psu_baffle_gap, convexity=10) + PsuLedBafflePlan(); +} + +module PsuMountNegative(){ + axis = [0, -psu_sz[1]/2, psu_th]; + PsuRepresentation(); + translate(axis) + rotate([atan(2 * psu_board_support_z / psu_sz[1]), + 0,0]) + translate(-axis) + PsuBoardRepresentation(); +} + +module PsuMountPositive(){ + difference(){ + intersection(){ + PsuMountPositiveMain(); + linextr_y_xz(-psu_y, psu_sz[1]*2) square(100, center=true); + } + PsuMountNegative(); + intersection(){ + hull(){ + PsuBoardRepresentation(); + translate([0,0,5]) PsuBoardRepresentation(); + } + translate([-20,0,-20]) cube(40); + } + } + for (mx=[0,1]) { + PsuMountCornerExtrude(mx,1){ + translate([psu_sz[0]/2 - psu_hole_pos[1], + psu_hole_pos[0]] + + psu_board_gap * [1,1] ) + circle(r= psu_hole_dia/2); + } + } + difference(){ + translate([0, psu_board_nutbox_y, 0]) + rotate([0,0,180]) + NutBox(psu_board_nutbox, psu_z_down); + translate([0, psu_sz[1]/2, 0]) + linextr(-psu_usba_v_space_below, +10) + PsuUsbAVSpacePlan(); + } +} + +module PsuClamp(){ ////toplevel + rotate([180,0,0]) difference(){ + linear_extrude(height=psu_clamp_th + psu_th_for_clamp, convexity=5) { + difference(){ + hull(){ + circle(r = psu_fix_sz/2); + translate([ -psu_board_nutbox[0]/2, 0]) + square(center=true, [ psu_board_clamp_ovlp*2, psu_clamp_w ]); + } + circle(r = psu_board_nutbox[0]/2); + } + } + translate([0,0,-1]) linear_extrude(height=psu_th_for_clamp+1) { + translate([ -psu_board_nutbox[0]/2 + psu_clamp_gap, 0 ]) + mirror([1,0]) + translate([0,-20]) square(40); + } + linextr(-10,10) { + rotate(-90) + translate([0, -psu_board_nutbox[0]/2]) + PsuUsbAVSpacePlan(); + } + } +} + +module PsuLedWindowsPlan(){ + difference(){ + PsuLedWindowsPlanCore(); + PsuLedBafflePlan(); + } +} + +module PsuLedWindowsWindows(ceil){ + translate([0,0, -psu_z - ceil]) + linextr(0, psu_initial_layer_thick) + offset(delta=psu_window_ledge) + PsuLedWindowsPlan(); +} + +module PsuFirstLayerNegative(ceil){ + translate([0, 0, -psu_z - ceil]) + linextr(-1, psu_initial_layer_thick) + children(); +} + +module PsuMountWindowsNegative(ceil){ + linextr(-10, 0.1) + PsuLedWindowsPlan(); + PsuFirstLayerNegative(ceil) + offset(delta= psu_window_ledge + psu_multicolour_gap) + PsuLedWindowsPlan(); +} + +module PsuLedLegendsNegative(ceil){ + PsuFirstLayerNegative(ceil) + offset(delta= psu_multicolour_gap) + PsuLedLegendsPlan(); +} + +module PsuMountDemo() { ////toplevel + ceil = psu_test_ceil; + + translate([0, psu_y, psu_z]) { + difference(){ + PsuMountPositive(); + linextr(-20, 0.1) + PsuLedWindowsPlan(); + } + %PsuMountNegative(); + + color("yellow") translate([0,0, -psu_z - ceil]) + linear_extrude(height=0.4, convexity=10) + PsuLedWindowsPlan(); + + color("blue") translate([0,0, -psu_z - ceil]) + linear_extrude(height=0.4, convexity=10) + PsuLedLegendsPlan(); + + translate([0, psu_board_nutbox_y, 10]) + rotate([180,0,0]) + rotate([0,0,-90]) + PsuClamp(); + } +} + +module PsuMountTest() { ////toplevel + ceil = psu_test_ceil; + $fs = 0.1; + $fa = 3; + difference(){ + union(){ + translate([0, psu_y, psu_z]) + PsuMountPositive(); + difference(){ + + // rectangular box with wall + linextr_x_yz(-psu_mount_outer_sz_x/2, + +psu_mount_outer_sz_x/2) { + difference(){ + rectfromto([0, -ceil], + [psu_mount_outer_sz_y, psu_z + 10]); + rectfromto([ceil,0], 400*[1,1]); + } + } + + translate([0, psu_y, psu_z]) { + PsuMountNegative(); + } + } + } + translate([0, psu_y, psu_z]) { + PsuMountWindowsNegative(ceil); + PsuLedLegendsNegative(ceil); + } + } +} + +psu_multicolour_gap = 0.075; +psu_initial_layer_thick = 0.400; +psu_initial_layer_width = 0.750; +psu_window_ledge = 0.50; // each side + +psu_frame_gap = 1.0; + +module PsuMountLayerFrame(bl, tr, ix) { + gap0 = [1,1] * (psu_frame_gap + psu_initial_layer_width*(ix+0)); + gap1 = [1,1] * (psu_frame_gap + psu_initial_layer_width*(ix+1)); + linextr(0, psu_initial_layer_thick) { + difference(){ + rectfromto(bl-gap1, tr+gap1); + rectfromto(bl-gap0, tr+gap0); + } + } +} + +module PsuMountTestFullLayerFrame(ix) { + PsuMountLayerFrame([-0.5 * psu_mount_outer_sz_x, 0], + [+0.5 * psu_mount_outer_sz_x, + psu_mount_outer_sz_y], + ix); +} + +module PsuMountTestFullMain() { ////toplevel + ceil = psu_test_ceil; + + PsuMountTestFullLayerFrame(2); + + difference(){ + translate([0,0, ceil]) + PsuMountTest(); + } +} + +module PsuMountTestFullOneLayer(ix) { + PsuMountTestFullLayerFrame(ix); + linextr(0, psu_initial_layer_thick) { + translate([0, psu_y]) children(); + } +} + +module PsuMountTestFullText() { ////toplevel + PsuMountTestFullOneLayer(0) + PsuLedLegendsPlan(); +} +module PsuMountTestFullWindows() { ////toplevel + PsuMountTestFullLayerFrame(1); + translate([0, psu_y, psu_z + psu_test_ceil]) + PsuLedWindowsWindows(psu_test_ceil); +} + +module PsuMountTestFullDemo() { ////toplevel + color("blue") PsuMountTestFullMain(); + color("yellow") PsuMountTestFullText(); + %PsuMountTestFullWindows(); +} diff --git a/anglepoise-neck.scad b/anglepoise-neck.scad new file mode 100644 index 0000000..aa39ce2 --- /dev/null +++ b/anglepoise-neck.scad @@ -0,0 +1,83 @@ +// -*- C -*- + +arm_depth = 25; +arm_innerwidth = 9.60 - 0.50; +arm_innerheight = 8.90 - 0.50; +arm_pin_depth = 18.50 + 1.0; +arm_pin_dia = 1.5 + 0.7; + +armpart_hex_rad = 15; +armpart_main_thick = 8; + +hingepin_dia = 3 + 1.0; +hingenut_width = 6 + 1.0; +hingenut_depth = 4; +hingenut_clear = 5; + +headpart_main_dia = 15 + 0.3; +headpart_main_len = 16 + 1.1; +headpart_stub_protrude = 2; +headpart_stub_width = 11.7 - 0.6; + +headpart_flatten_angle = 45; + +// computed + +armpart_hinge_height = arm_innerheight + hingenut_width/2 + hingenut_clear; +armpart_main_height = armpart_hinge_height + headpart_stub_width / 2; +armpart_main_width = headpart_stub_width; +armpart_x_unit = armpart_hex_rad * tan(30); +headpart_flatten_z = headpart_main_dia/2 * cos(headpart_flatten_angle); +headpart_stub_support_x = headpart_stub_width * cos(59) / 2; +headpart_stub_len = headpart_stub_protrude + headpart_main_dia/2; +hingenut_depth_y = + sqrt(headpart_main_dia*headpart_main_dia/4 - hingenut_width*hingenut_width/4) + - hingenut_depth; + +module ArmPart(){ ////toplevel + difference(){ + translate([-arm_innerwidth/2, 1, 0]) + mirror([0,-1,0]) + cube([arm_innerwidth, arm_depth+1, arm_innerheight]); + translate([0, -arm_pin_depth, -50]) + cylinder(r=arm_pin_dia/2, h=100, $fn=20); + } + difference(){ + translate([-armpart_main_width/2, 0, 0]) + cube([armpart_main_width, armpart_main_thick, armpart_main_height]); + translate([0,50,armpart_hinge_height]) + rotate([90,0,0]) + cylinder(r=hingepin_dia/2, h=100, $fn=20); + } +} + +module HeadPart(){ ////toplevel + difference(){ + union(){ + translate([-headpart_main_len/2, 0,0]) + rotate([0,90,0]) + cylinder(r=headpart_main_dia/2, h=headpart_main_len, $fn=40); + rotate([90,0,0]) + cylinder(h = headpart_stub_len, + r = headpart_stub_width/2, + $fn = 6); + translate([-headpart_stub_support_x, + -headpart_stub_len, + -headpart_main_dia/2]) + cube([headpart_stub_support_x*2, + headpart_stub_len, + headpart_main_dia/2]); + } + translate([-100,-100,-100]) + cube([200,200, 100 - headpart_flatten_z]); + rotate([90,0,0]) + translate([0,0, -100]) + cylinder(r=hingepin_dia/2, h = 200, $fn=20); + translate([0,hingenut_depth_y,0]) + rotate([90,0,180]) + cylinder(r=hingenut_width/2/cos(30), h=20, $fn=6); + } +} + +//ArmPart(); +//HeadPart(); diff --git a/anglepoise-neck.slic3r b/anglepoise-neck.slic3r new file mode 100644 index 0000000..b5079ea --- /dev/null +++ b/anglepoise-neck.slic3r @@ -0,0 +1 @@ +perimeters = 4 diff --git a/anke-gps-bracket.scad b/anke-gps-bracket.scad new file mode 100644 index 0000000..084eefd --- /dev/null +++ b/anke-gps-bracket.scad @@ -0,0 +1,374 @@ +// -*- C -*- + +include + +// Dimensions of the main GPS body +outerw = 120; +outerh = 75; +outert = 15; +outerbackbevel = 3; + +// Dimensions for the holder +holder_outerw = outerw - 0.0; +holder_outerh = outerh + 0.0; +holder_outert = outert + 0.0; + +// Dimensions for the model +model_outerw = outerw + 2.5; +model_outerh = outerh - 0.2; +model_outert = outert - 1.0; + +// Dimensions of the bezel area round the edges +bezelw = 11 - 0.5; +bezelboth = 11 - 0.5; +bezeltoph = 7 - 0.5; + +// Dimensions of the speaker at the back +spkrdia = 22; +spkr2bot = 19; +spkr2rhs = 25; + +// Dimensions of the plug and wire +plugw = 12; +plugh = 9; +plug2bot = 11; +plug2lhs = 11; +plugtotald = 15; +pluggapd = 5; + +// Dimensions of the hole in the tray +// width and height (vertical) at the top +nestleh = 53; +nestlew = 60.9; +// depths (back to front distance): +nestledl = 40.2; +nestledr = 43.9; +// differences in width, depth, at bottom: +nestledwl = 2.1; +nestledwr = 1.4; +nestleddf = 4.0; +nestleddbl = 5.7; +nestleddbr = 5.2; + +// Adjustment for the GPS attitude and position +gpsazimuth = 45; +gpselevation = 40; +gpsrightwardoffset = 5; +gpsrearwardoffset = 2; +gpsrightwardoffsetonbar = 0; + +// Amount of wire protrusion to allow for +plugwiremoreh = 25; + +// Slops and steps etc. +plugslop = 0.5; +plughstep = 1.5; +bodylhsrhsslop = 0.5; +holderhgap = 5; +holderbezelmore = 2; +nestlebevel = 1; + +// Dimensions for strength only +screent = 1.0; +plugstrutw = 4; +plugstrutt = min(model_outert, 5); +nestledoveclipw = 20; +holderh = model_outerh * 0.5; +holderwallt = 2.5; +holderbackt = 2.8; +holderdccount = 2; +holderdoveclipl = 15; +chassish = 13; +chassist = 13; +nestlefloorh = 4.7; +nestleceilh = 6.0; +nestlewallmin = 10.0; +nestlearchslope = 0.75 * sqrt(0.5); + +// Consequential values +holderdcw = DoveClipPairSane_width(holderdccount); + +module GpsPlugPlug(slop){ + effhslop = slop - plughstep; + effplugw = plugw + slop*2; + effplugh = plugh + effhslop*2; + translate([plug2lhs-slop, plug2bot-effhslop, -1]) + cube([effplugw, effplugh, model_outert+2]); +} + +module GpsBodyOuterBevel(len){ + translate([0,-1,0]) { + rotate([-90,0,0]) { + linear_extrude(height=len+2) { + polygon([[-outerbackbevel, 0], + [ 0, outerbackbevel], + [outerbackbevel, 0], + [ 0, -outerbackbevel]]); + } + } + } +} + +module GpsBody() { ////toplevel + difference(){ + union(){ + difference(){ + cube([model_outerw, model_outerh, model_outert]); + translate([bezelw, bezelboth, screent]) + cube([model_outerw-bezelw*2, + model_outerh-bezelboth-bezeltoph, + model_outert]); + translate([model_outerw-spkr2rhs, spkr2bot, -1]) + cylinder(r=spkrdia/2, h=model_outert+2); + } + translate([plug2lhs+plugw/2, plug2bot+plugh/2, 0]) + cylinder(r=(plugw+plugh)/2, h=model_outert); + for (x=[plug2lhs-plugstrutw, plug2lhs+plugw]) + translate([x, 0.1, 0]) + cube([plugstrutw, model_outerh-0.2, plugstrutt-0.10]); + } + GpsPlugPlug(0); + for (x=[0,model_outerw]) translate([x,0,0]) GpsBodyOuterBevel(model_outerh); + for (y=[0,model_outerh]) translate([0,y,0]) + rotate([0,0,-90]) GpsBodyOuterBevel(model_outerw); + } +} + +module GpsPlug() { + plugwireh = plug2bot + plugwiremoreh; + translate([-plugslop,0,0]) GpsPlugPlug(-plugslop); + mirror([0,0,1]) translate([plug2lhs, plug2bot, 0]) { + cube([plugw, plugh, plugtotald-0.05]); + translate([0, -plugwireh, pluggapd]) + cube([plugw, plugwireh+0.05, plugtotald-pluggapd]); + } +} + +lhsteethu = 2; + +module GpsLHSMask(xslop=0){ + translate([plug2lhs + plugw+plugh+plugstrutw, + 0, + -50]) { + for (iter=[-100/lhsteethu : 100/lhsteethu]) { + translate([0, iter*lhsteethu*2, 0]) { + linear_extrude(height=100) { + polygon([[-300, 0], + [ 0, 0], + [lhsteethu,lhsteethu], + [ 0, lhsteethu*2], + [-300, lhsteethu*2+0.1]]); + } + } + } + } +} + +module GpsAssembled(){ ////toplevel + GpsBody(); + GpsPlug(); +} + +module GpsBodyLT(){ + intersection(){ + GpsBody(); + GpsLHSMask(); + } +} + +module GpsBodyRT(){ + difference(){ + GpsBody(); + GpsLHSMask(bodylhsrhsslop); + } +} + +module GpsPlugT(){ ////toplevel + rotate([0,-90,0]) GpsPlug(); +} + +module NestleCubeCutout(ca,cb,d){ + dist = cb - ca; + cuth = -nestleh + nestlefloorh; + mirror([0,1,0]){ + translate([0,1,0]) + rotate([90,0,0]){ + linear_extrude(height=d+2){ + polygon([[ca+nestlebevel, cuth], + [ca, cuth+nestlebevel*2], + [ca, -dist/2/nestlearchslope-nestleceilh], + [(ca+cb)/2, -nestleceilh], + [cb, -dist/2/nestlearchslope-nestleceilh], + [cb, cuth+nestlebevel*2], + [cb-nestlebevel, cuth]]); + } + } + } +} + +module NestleCube(){ ////toplevel + midw = nestlew/2; + midd = min(nestledl,nestledr); + midddb = max(nestleddbl,nestleddbr); + + based0 = nestleddf; + based1 = midd - midddb; + basew0 = -nestledwr; + basew1 = +nestledwl-nestlew; + + echo("wl,wr=", basew1, basew0); + echo("df,dbl,dbm,dbr", + based0, nestledl-nestleddbl, based1, nestledr-nestleddbr); + + cutd0 = based0 + nestlewallmin; + cutd1 = based1 - nestlewallmin; + cutw0 = basew0 - nestlewallmin; + cutw1 = basew1 + nestlewallmin; + + bevth = -nestleh + nestlebevel*2; + bevw = nestlebevel; + bevd = nestlebevel; + + translate([-(basew0+basew1)/2, -(based0+based1)/2, 0]) { + difference(){ + polyhedron + (points= + [[ +0 , +0, 0], // 0 + [ +0 , +nestledr, 0], // 1 + [ -midw , +midd, 0], // 2 + [ -nestlew, +nestledl, 0], // 3 + [ -nestlew, +0, 0], // 4 + [-nestledwr+0 , +nestleddf +0, bevth], // 5 + [-nestledwr+0 , -nestleddbr+nestledr, bevth], // 6 + [ -midw , -midddb +midd, bevth], // 7 + [+nestledwl-nestlew, -nestleddbl+nestledl, bevth], // 8 + [+nestledwl-nestlew, +nestleddf +0, bevth], // 9 + [-nestledwr+0 -bevw, +nestleddf +0 +bevd, -nestleh], // 10 + [-nestledwr+0 -bevw, -nestleddbr+nestledr-bevd, -nestleh], // 11 + [ -midw , -midddb +midd -bevd, -nestleh], // 12 + [+nestledwl-nestlew+bevw, -nestleddbl+nestledl-bevd, -nestleh], // 13 + [+nestledwl-nestlew+bevw, +nestleddf +0 +bevd, -nestleh]], // 14 + triangles=[// main side panels + [0,1,6],[6,5,0], + [1,2,7],[7,6,1], + [2,3,8],[8,7,2], + [3,4,9],[9,8,3], + [4,0,5],[5,9,4], + // bevels + [6,7,12],[12,11,6], + [7,8,13],[13,12,7], + [8,9,14],[14,13,8], + [9,5,10],[10,14,9], + [5,6,11],[11,10,5], + // top and bottom + [4,3,2],[2,1,0],[0,4,2], + [12,13,14],[10,11,12],[12,14,10]], + convexity=3); + union(){ + #NestleCubeCutout(cutw1, cutw0, max(nestledl,nestledr)); + #rotate([0,0,90]) NestleCubeCutout(cutd0, cutd1, nestlew); + } + } + } + + translate([gpsrightwardoffset,-gpsrearwardoffset,0]) + rotate([0,0,90+gpsazimuth]) + translate([nestledoveclipw/2,0,DoveClip_depth()-0.5]) + rotate([0,-90,0]) + DoveClipPairSane(count=3, h=nestledoveclipw); +} + +module NestleCubeBaseTest(){ ////toplevel + intersection(){ + translate([0,0,nestleh]) NestleCube(); + translate([-100,-100,0]) cube([200,200,nestlebevel*5]); + } + cube([5,5,10]); +} + +module NestleCubeCeilTest(){ ////toplevel + intersection(){ + translate([0,0,3]) NestleCube(); + translate([-100,-100,0]) cube([200,200,5.5]); + } + cube([5,5,10]); +} + +module NestleCubePin(){ ////toplevel + DoveClipPin(nestledoveclipw*0.4); +} + +module HolderSideL(){ ////toplevel + minz = -(bezelw - holderbezelmore) - holderbackt; + holdert = holder_outert + holderwallt*2; + cylr = 0.5*sqrt(holderdcw*holderdcw + holderdoveclipl*holderdoveclipl); + difference(){ + translate([-holderh, + -holderwallt, + minz]) { + cube([holderh + holderhgap + cylr, + holdert, + -minz]); + translate([holderh + holderhgap + cylr, holdert/2, 0]) { + cylinder(r=cylr, h=-minz); + rotate([0,0,gpselevation]) + translate([0, -holderdoveclipl/2, -minz + DoveClip_depth()]) + rotate([0,-90,-90]) + DoveClipPairSane(count=holderdccount, h=holderdoveclipl); + } + } + translate([-holderh-1, + 0, + minz + holderbackt]) + cube([holderh+1, + holder_outert, + bezelw]); + } +} + +module HolderSideR(){ ////toplevel + mirror([0,1,0]) HolderSideL(); +} + +module ChassisBar(){ ////toplevel + dist = holder_outerw - 2*((bezelw - holderbezelmore) + DoveClip_depth()); + cliph = holderdcw; + for (mir=[0,1]) { + mirror([mir,0,0]) { + translate([dist/2, cliph/2, 0]) + DoveClipPairSane(h=holderdoveclipl, count=holderdccount); + translate([-1, 0, 0]) + cube([dist/2 - DoveClip_depth() + 1.1, chassish, chassist]); + } + } + translate([-gpsrightwardoffsetonbar, -DoveClip_depth(), 0]) + rotate([0,0,-90]) + DoveClipPairSane(h=nestledoveclipw, count=3, + baseextend=chassist/2); +} + +module HolderSidePin(){ ////toplevel + DoveClipPin(holderdoveclipl*0.5); +} + +module Pins(){ ///toplevel + for (i=[1:4*holderdccount]) { + translate([i*10, 0, 0]) HolderSidePin(); + } + for (i=[1:6]) { + translate([i*10, 20, 0]) NestleCubePin(); + } +} + +//GpsPlugT(); +//GpsAssembled(); +//GpsBody(); +//NestleCube(); +//NestleCubeBaseTest(); +//NestleCubeCeilTest(); +//NestleCubePin(); +//HolderSideL(); +//HolderSideR(); +//HolderSidePin(); +//ChassisBar(); +//Pins(); diff --git a/atreic-piano-stand.scad b/atreic-piano-stand.scad new file mode 100644 index 0000000..be81606 --- /dev/null +++ b/atreic-piano-stand.scad @@ -0,0 +1,11 @@ +// -*- C -*- +height = 40; +depth = 20; +thick = 3; +width = 40; + +difference(){ + cube([width, depth, height]); + translate([thick, -1, thick]) + cube([width - thick*2, depth+2, height]); +} diff --git a/axlepin.scad b/axlepin.scad new file mode 100644 index 0000000..8b47771 --- /dev/null +++ b/axlepin.scad @@ -0,0 +1,47 @@ +// -*- C -*- +// +// axlepin.scad +// +// 3D designs for for securing things on axles +// Copyright 2012,2016 Ian Jackson +// +// This work is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This work is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this work. If not, see . + + + +function AxlePin_holerad() = 2; +function AxlePin_zoffset(holerad=2, slop=0.5) = (holerad - slop)*0.7; + +module AxlePin(axlerad, pinlen, holerad=2, tabthick=5, slop=0.5){ + pinr = holerad - slop; + intersection(){ + union(){ + translate([0, -pinlen/2, 0]) rotate([-90,0,0]) + cylinder(r=pinr, h=pinlen, $fn=10); + translate([-tabthick, axlerad, -holerad]) + cube([tabthick*2, holerad*2, holerad*2]); + } + translate([-50,-50,-AxlePin_zoffset(holerad,slop)]) + cube([100,100,50]); + } +} + +function Washer_thick() = 1.2; + +module Washer(axlerad, washerrad, thick=1.2, slop=0.5){ + difference(){ + cylinder(h=thick, r=washerrad); + translate([0,0,-1]) cylinder(h=thick+2, r=axlerad+slop); + } +} diff --git a/belt-cut-jig-common.scad b/belt-cut-jig-common.scad new file mode 100644 index 0000000..5464678 --- /dev/null +++ b/belt-cut-jig-common.scad @@ -0,0 +1,95 @@ +// -*- C -*- + +jig_max_len = 160; // print diagonally +//jig_max_len = 30; + +registrationgroove_width = 0.8; +registrationgroove_depth = 1.2; + +registrationprotrusion_poke = 3; +registrationprotrusion_slope = 0.75; + +jig_overlap = 1; + +jig_ends_extra = 2; + +jig_iters = floor((jig_max_len - jig_ends_extra) / jig_interval); +//jig_iters=2; +echo(jig_iters); + +module RegistrationGroove(l){ + // runs along the +ve X axis for length l but at correct z pos + translate([0, 0, jig_main_zsz + 0.1]) { + rotate([90,0,90]) + linear_extrude(height=l) + polygon([[-registrationgroove_width/2, 0], + [ +registrationgroove_width/2, 0], + [ 0, -registrationgroove_depth ]]); + } +} + +module OneJig(){ + difference(){ + translate([-(jig_interval/2 + jig_overlap), + jig_min_y, + -strap_thick]) + cube([jig_interval + 2, + jig_max_y - jig_min_y, + jig_main_zsz + strap_thick]); + OneJigCutout(); + translate([-100, -strap_width/2, -10]) + cube([200, strap_width, 10]); + translate([-100,0,0]) + RegistrationGroove(200); + for (xfrac=[-1/4,0,+1/4]) + translate([jig_interval * xfrac, -100, 0]) + rotate([0,0,90]) + RegistrationGroove(200); + } +} + +module RegistrationProtrusion(){ + // points towards the positive x axis + xsz = registrationprotrusion_poke; + ysz = registrationprotrusion_poke; + diag_sz = xsz * sqrt(2); + zsz = diag_sz / registrationprotrusion_slope; + hull(){ + translate([0, 0, 0.1]){ + linear_extrude(height=0.1) + polygon([[ 0, -ysz ], + [ xsz, 0 ], + [ 0, ysz ]]); + translate([-0.1, 0, zsz ]) + rotate([0,0,45]) + cube(0.1); + } + } +} + +module Jig(){ + for(end=[0,1]){ + for(yfrac=[-1/2, 0, 1/2]){ + translate([ end + ? jig_interval * (jig_iters - 0.5) + : -jig_interval/2, + yfrac * strap_width, + 0]) + rotate([0,0, end ? 0 : 180]) + translate([ jig_overlap, 0, 0 ]) + RegistrationProtrusion(); + } + } + for (i=[0:jig_iters-1]) { + translate([jig_interval * i, 0, 0]) + OneJig(); + } +} + +module JigPrint(){ + rotate([0,0,-45]) + translate([0,0,jig_main_zsz]) + rotate([180,0,0]) + Jig(); +} + diff --git a/belt-hole-cut-jig-simple.scad b/belt-hole-cut-jig-simple.scad new file mode 100644 index 0000000..68ef720 --- /dev/null +++ b/belt-hole-cut-jig-simple.scad @@ -0,0 +1,96 @@ +// -*- C -*- + +strap_thick = 3; +strap_width = 26.75 + 0.7; + +punch_dia = 11.10; + +punch_slop = 0.5; + +jig_interval = 20; + +reg_blocks = 3; + +jig_iters = 7; + +roof_thick = 4; +regblock_thick = 4; +punchtube_thick = 1.8; + +total_h = 33; +punchfree_h = 8; + +reg_prot_width = 4; + +// computed: + +punchhole_r = punch_dia/2 + punch_slop; +mainframe_l = jig_interval * jig_iters; + +mainframe_w = strap_width + reg_prot_width*2; + +echo(mainframe_l); + +module RegBlockOutline(){ + difference(){ + translate([0, -mainframe_w/2]) + mirror([1,0]) + square([total_h, mainframe_w]); + translate([1, -strap_width/2]) + mirror([1,0]) + square([strap_thick+1, strap_width]); + } +} + +module RegBlock(){ + translate([regblock_thick/2,0,total_h]) + rotate([0,-90,0]) + linear_extrude(height=regblock_thick) + RegBlockOutline(); +} + +module MainFrame(){ + translate([jig_interval/2, -mainframe_w/2, 0]) + mirror([1,0,0]) + cube([mainframe_l, mainframe_w, roof_thick]); + for (rbi=[0:reg_blocks-1]) { + translate([0 + + -(mainframe_l-jig_interval)/(reg_blocks-1) * rbi, + 0,0]) + RegBlock(); + } +} + +module PerHole(){ + for (holei=[0:jig_iters-1]) { + translate([-jig_interval * holei, 0, 0]) + child(0); + } +} + +module Shells(){ + PerHole(){ + cylinder(r=punchhole_r+punchtube_thick, h=total_h-punchfree_h, $fn=50); + } +} + +module Punches(){ + PerHole(){ + translate([0,0,-1]){ + cylinder(r=punchhole_r, h=total_h+2, $fn=100); + %cylinder(r=punch_dia/2, h=total_h); + } + } +} + +module Jig(){ + difference(){ + union(){ + MainFrame(); + Shells(); + } + Punches(); + } +} + +Jig(); diff --git a/belt-hole-cut-jig.scad b/belt-hole-cut-jig.scad new file mode 100644 index 0000000..36380a0 --- /dev/null +++ b/belt-hole-cut-jig.scad @@ -0,0 +1,38 @@ +// -*- C -*- + +strap_thick = 3; +strap_width = 26.75 + 0.7; + +jig_interval = 20; + +edgewall_width = 3; + +jig_ywidth = 17; + +jig_min_y = -jig_ywidth; +jig_max_y = +jig_ywidth; + + +jig_main_zsz = 28; +punch_dia = 12.75; + +punch_slop = 0.5; + +// common stuff + +include + +module OneJigCutout(){ + translate([0,0,-10]) + cylinder(r= punch_dia/2 + punch_slop, h=100, $fn=50); +} + +module Demo(){ ////toplevel + Jig(); +} + +module JigT(){ ////toplevel + JigPrint(); +} + +JigT(); diff --git a/belt-slot-cut-jig,JigT.auto.slic3r b/belt-slot-cut-jig,JigT.auto.slic3r new file mode 100644 index 0000000..33b5b29 --- /dev/null +++ b/belt-slot-cut-jig,JigT.auto.slic3r @@ -0,0 +1,2 @@ +fill_angle = 0 +skirt_distance = 1 diff --git a/belt-slot-cut-jig,Kit.auto.slic3r b/belt-slot-cut-jig,Kit.auto.slic3r new file mode 100644 index 0000000..33b5b29 --- /dev/null +++ b/belt-slot-cut-jig,Kit.auto.slic3r @@ -0,0 +1,2 @@ +fill_angle = 0 +skirt_distance = 1 diff --git a/belt-slot-cut-jig.scad b/belt-slot-cut-jig.scad new file mode 100644 index 0000000..543a53f --- /dev/null +++ b/belt-slot-cut-jig.scad @@ -0,0 +1,195 @@ +// -*- C -*- + +// todo +// various registration marks +// protrustions at ends at strap width and middle +// grooves on top face at 1/4,1/2,3/4 length and 1/2 width + +strap_thick = 3; +strap_width = 26.75 + 0.7; + +jig_interval = 25; + +edgewall_width = 3; +crewpunch_slop = 0.3; +main_slop = 0.25; + +holder_min_wall = 2; +holder_attach_near_wall = 5; + +holder_attach_xsz = 5; +holder_ctie_width = 4.0 + 0.5; +holder_ctie_thick = 3.0 + 0.5; +holder_attach_walls = 3; +holder_attach_roof = 2.5; + +holder_corner_round = 2.0; + +punch_travel = 8; + +// from careful measurement + +crewpunch_shape = + [[ 6, [0.6, 6.0], [1.6, 12.3] ], + [ 8, [1.1, 6.2], [1.9, 12.5] ], + [ 10, [1.6, 6.5], [2.1, 12.8] ], + [ 12, [1.8, 6.6], [2.3, 12.7] ], + [ 14, [2.1, 6.8], [2.6, 13.0] ], + [ 16, [2.4, 6.9], [2.7, 13.2] ], + [ 18, [2.5, 7.0], [2.9, 13.3] ], + [ 22, [3.1, 7.1], [3.2, 13.4] ], + [ 26, [3.3, 7.2], [3.5, 13.6] ], + ]; + +crewpunch_shaft_max_y = 7.5; + +crewpunch_systematic_size_error = +0.36; + +crewpunch_smallest_shape = crewpunch_shape[0]; +crewpunch_biggest_shape = crewpunch_shape[len(crewpunch_shape)-1]; + +crewpunch_skew_angle = 3.5; //degrees +crewpunch_skew_yoff = +1.1; //mm + +// computed + +punch_dx = 0.5 * (-crewpunch_biggest_shape[2][0] + +crewpunch_biggest_shape[2][1]); +punch_dy = 0.5 * (+crewpunch_biggest_shape[1][1] + -crewpunch_biggest_shape[1][0]) + crewpunch_skew_yoff; + +attach_ysz = holder_attach_walls*2 + holder_ctie_width; + +holder_block_zsz = crewpunch_biggest_shape[0] - crewpunch_smallest_shape[0]; +holder_xsz = crewpunch_biggest_shape[2][0] + crewpunch_biggest_shape[2][1] + + holder_min_wall*2; + +holder_skewangle_yextra = holder_xsz/2 * sin(abs(crewpunch_skew_angle)); + +holder_max_y = punch_dy + crewpunch_biggest_shape[1][0] + holder_min_wall + + crewpunch_systematic_size_error + holder_skewangle_yextra; + +holder_attach_max_y = punch_dy + - max(crewpunch_biggest_shape[1][1], crewpunch_shaft_max_y) + - crewpunch_systematic_size_error - holder_skewangle_yextra; + +holder_block_min_y = punch_dy + - crewpunch_biggest_shape[1][1] - holder_attach_near_wall + + - crewpunch_systematic_size_error - holder_skewangle_yextra; + +holder_all_min_y = holder_attach_max_y - attach_ysz; + +jig_max_y = max(holder_max_y + main_slop, strap_width/2) + edgewall_width; +jig_min_y = min(holder_all_min_y - main_slop, -strap_width/2) - edgewall_width; + +jig_main_zsz = holder_block_zsz + punch_travel; + +// common stuff + +include + +// objects + +module CrewPunch(){ + ourslop = crewpunch_slop - crewpunch_systematic_size_error; + hull(){ + for(layer=crewpunch_shape){ + translate([0,0, layer[0]]){ + for(xind=[0,1]) //translate([xind?0:1,0,0]) + for(yind=[0,1]) //translate([0,yind?0.5:0,0]) + mirror([xind?1:0,0,0]) mirror([0,yind?0:1,0]){ + translate([-0.1,-0.1,-0.1]) + cube([0.1 + layer[2][xind] + ourslop, + 0.1 + layer[1][1-yind] + ourslop, + 0.2]); + } + } + } + } +} + +module MaybeRoundedCube(sizes, roundedness){ + if (roundedness > 0) { + translate([roundedness, roundedness, 0]){ + minkowski(){ + cube([sizes[0] - roundedness*2, + sizes[1] - roundedness*2, + sizes[2]]); + cylinder(h=0.05, r=roundedness, $fn=20); + } + } + } else { + cube(sizes); + } +} + +module PunchHolder(cutouts=true){ + roundedness = cutouts ? holder_corner_round : 0; + difference(){ + translate([-holder_xsz/2, holder_block_min_y, 0]) + MaybeRoundedCube([holder_xsz, + holder_max_y - holder_block_min_y, + holder_block_zsz], + roundedness); + if (cutouts) + rotate([0,0,-crewpunch_skew_angle]) + translate([punch_dx, + punch_dy, + -crewpunch_smallest_shape[0]]) + CrewPunch(); + } + difference(){ + translate([-holder_attach_xsz/2, holder_all_min_y, 0]) + MaybeRoundedCube([holder_attach_xsz, + attach_ysz, + holder_block_zsz + punch_travel + + holder_ctie_thick + holder_attach_roof + 1], + roundedness); + if (cutouts) + translate([-30, + holder_all_min_y + holder_attach_walls, + holder_block_zsz + punch_travel]) + cube([60, holder_ctie_width, holder_ctie_thick]); + } +} + +module OneJigCutout(){ + minkowski(){ + cube([main_slop*2, main_slop*2, 50], center=true); + PunchHolder(false); + } +} + +module JigT(){ ////toplevel + JigPrint(); +} + +module PunchHolderT(){ ////toplevel + PunchHolder(true); +} + +module Demo(){ ////toplevel + %PunchHolder(); + Jig(); +} + +module Kit(){ ////toplevel + JigT(); + rotate([0,0,-45]){ + translate([(jig_iters-1)*jig_interval/2, + jig_min_y - holder_max_y - 5, + 0]) + PunchHolder(); + } +} + +//CrewPunch(); +//PunchHolder(); +//PunchHolder(false); +//OneJig(); +//Jig(); +Demo(); +//JigT(); +//RegistrationProtrusion(); +//PunchHolderT(); +//Kit(); diff --git a/bike-lipo-box-gland.scad b/bike-lipo-box-gland.scad new file mode 100644 index 0000000..0e9228b --- /dev/null +++ b/bike-lipo-box-gland.scad @@ -0,0 +1,59 @@ +// -*- C -*- + +include + +cable_dias = [6.5, 8.2]; + +cd = cable_dias[1] + 0.5; +wall = 2.5; + +function Gland_xlen(cabledia) = cabledia * 1.5; +function Gland_xdia(cabledia) = cabledia * 2.0; +function Gland_xoutdia(cabledia) = Gland_xdia(cabledia) * 1.1 + 0.5; + +// origin is centre, on outside +// outside is in direction of positive X axies +module GlandNegative(cabledia){ + xlen = Gland_xlen(cabledia); + xdia = Gland_xdia(cabledia); + + hull(){ + rotate([0,90,0]) cylinder(r= cabledia/2, h=1); + translate([xdia,0,0]) rotate([0,90,0]) cylinder(r= xdia/2, h=1); + } + translate([-10,0,0]) + rotate([0,90,0]) + cylinder(r= cabledia/2, h=11); +} + +module GlandPositive(cabledia){ + translate([-0.1, 0,0]) + rotate([0,90,0]) + cylinder(r= Gland_xoutdia(cabledia)/2, h= Gland_xlen(cabledia) + 0.1); +} + +platesz = [wall, 24, 28]; +plateoff = [-platesz[0]/2, -platesz[1]/2, -platesz[2] + platesz[1]/2]; + +module Plate(){ + difference(){ + union(){ + GlandPositive(cd); + translate(plateoff) + cube(platesz); + } + GlandNegative(cd); + } +} + +module Test(){ ////toplevel + Plate(); + translate(plateoff){ + difference(){ + cube([15, 20, 1.2]); + Commitid_BestCount_M([15, 20]); + } + } +} + +//Test(); diff --git a/bike-lipo-box.scad b/bike-lipo-box.scad new file mode 100644 index 0000000..edd3701 --- /dev/null +++ b/bike-lipo-box.scad @@ -0,0 +1,294 @@ +// -*- C -*- + +include +include +include +include + +pxp6012_rad = 22.5 / 2 + 0.5; // make circular hole this size in outer wall +pxp6012_rad_outer = 32.0 / 2 - 0.5; + +s1930_y = 30.2 + 0.2; +s1930_x = 22 + 0.2; +s1930_y_outer = 36.4 + 0.2; +s1930_x_outer = 27.6 + 0.2; + +s1930_recess = 3; +s1930_around = 3; +s1930_behind = 3; + +jdae12pa_rad = 12 / 2 + 0.5; +jdae12pa_rad_outer = 19 / 2 + 0.5; // head of an "M12 bolt" + +totx_inner = 180; +toty_outer = 95; +totz_inner = 27.0; + +wallthick = 2.5; + +cabledia = 8.7; + +strap_w = 5 + 1; +strap_th = 4 + 1; +strap_pillar = 3; +strap_pillard = 5; +strap_over = 2; + +lipokeeper_w = 10; +lipokeeper_h = 8; +lipokeeper_d_min = 2; +lipokeeper_slope = 0.75; +lipokeeper_end_h = 12; +lipokeeper_end_d_min = 15; + +straps_at_box = [45, 95, 125, 160]; +straps_every = 30; + +// calculated + +totx_outer = totx_inner + wallthick*2; +toty_inner = toty_outer - wallthick*2; +totz_outer = totz_inner + wallthick*2; + +sb_box_sz = [totx_outer, totz_outer, toty_inner]; + +// origin is at centre on outer face wall +// outside is towards positive x +// mounting is vertical +module S1930_Positive(){ + d = s1930_recess + s1930_behind; + translate([-d/2, 0,0]) + cube([d, + s1930_x_outer + s1930_around, + s1930_y_outer + s1930_around], center=true); +} +module S1930_Negative(){ + cube([60, s1930_x, s1930_y], + center=true); + translate([1, 0,0]) + cube([s1930_recess*2+2, s1930_x_outer, s1930_y_outer], + center=true); +} + +module TestWall(){ ////toplevel + sw_ctr = [25, wallthick, 25]; + + rotate([0,0,-90]){ + difference(){ + union(){ + cube([50, wallthick, 42]); + } + + translate([30, -1, 20]) + rotate([-90,0,0]) + cylinder(r = pxp6012_rad, h=10, $fn=60); + + rotate([90,0,0]) + Commitid_BestCount([15,40]); + } + } + + difference(){ + union(){ + cube([50, wallthick, 50]); + translate(sw_ctr) + rotate([0,0,90]) + S1930_Positive(); + } + + translate(sw_ctr) { + rotate([0,0,90]) + S1930_Negative(); + } + } +} + +ts_totx = 30; +ts_toty = 25; +ts_totz_inner = 8; + +ts_box_sz = [ts_totx, ts_toty, ts_totz_inner]; + +$sealingbox_wallth = wallthick; +$sealingbox_floorth = wallthick; +$sealingbox_ceilth = wallthick; + +module TestSealBox(){ ////toplevel + $sealingbox_sz = ts_box_sz; + + SealingBox_RectBox(); + ts_cidoff = ($sealingbox_cnrrad * (1-.7) + wallthick * .8) * [1,1]; + translate(ts_cidoff) + Commitid_BestCount([ts_totx,ts_toty] - 2*ts_cidoff); +} + +module TestSealLid(){ ////toplevel + $sealingbox_sz = ts_box_sz; + + difference(){ + SealingBox_RectLid(); + + translate([ts_totx * .75, ts_toty/2, 0]) + cylinder(h=100, r=5); + + translate([-wallthick + $sealingbox_cnrrad*.5, + $sealingbox_cnrrad*.5 - wallthick, + ts_totz_inner + $sealingbox_ceilth]) + Commitid_BestCount([ts_totx * .75 - 2.5 - ($sealingbox_cnrrad*.5), + ts_toty - ($sealingbox_cnrrad*.5 - wallthick)*2]); + } +} + +module TestSealLidPrint(){ ////toplevel + rotate([180,0,0]) TestSealLid(); +} + +module ProfileDemos(){ ////toplevel + $sealingbox_sz = ts_box_sz; + + SealingBox_WallProfile(); + color("blue") SealingBox_FloorProfile(); + SealingBox_LidProfile(); + color("blue") SealingBox_CeilProfile(); + color("red") translate([-5,0]) square([1,ts_totz_inner]); +} + +module AtGlands(){ + for (dgy=[-15,-45]) { + translate([totx_inner + wallthick - $sealingbox_cnrrad * .3, + toty_inner + dgy, + totz_inner/2]) + children(); + } +} + +module StrapKeepers(at){ + strap_x_tot = strap_w + strap_pillar*2; + + for (sx= at) { + echo("strapkeeper at ",sx); + translate([sx - strap_x_tot, 0, 0]) + difference(){ + translate([0,0, -0.1]) + cube([strap_x_tot, strap_pillard, strap_th + strap_over]); + translate([strap_pillar, -1, 0]) + cube([strap_w, strap_pillard+2, strap_th]); + } + } +} + +chargingconn_x = pxp6012_rad_outer + 1 + $sealingbox_cnrrad; +switch_x = chargingconn_x + pxp6012_rad_outer + + s1930_y_outer/2 + s1930_around; + +module AtSealingBox(){ + rotate([90,0,0]) + translate([-wallthick,-wallthick, -toty_inner]) + children(); +} + +module Box(){ ////toplevel + $sealingbox_sz = sb_box_sz; + + difference(){ + union(){ + AtSealingBox() + SealingBox_RectBox(); + + translate([switch_x, toty_inner, totz_inner/2]) + rotate([90,0,90]) + S1930_Positive(); + + // keepers for lipo + for (keepers= [[ 35, lipokeeper_d_min, lipokeeper_h, + [ 40, 80, 120, 150 ] ], + [ 10, lipokeeper_end_d_min, lipokeeper_end_h, + [ 25 ] ] + // each entry: [ y, d_min, h, [ x, ...] ] + ]) + for (kx= keepers[3]) { + translate([kx, keepers[0], -1]) + hull(){ + cube([lipokeeper_w, keepers[1], keepers[2] +1]); + cube([lipokeeper_w, + keepers[1] + keepers[2] / lipokeeper_slope, + 1]); + } + } + + AtGlands() + GlandPositive(cabledia); + + translate([0, toty_inner+wallthick, -wallthick]) + rotate([180, 0,0]) + StrapKeepers(straps_at_box); + } + + // charging connector + translate([chargingconn_x, + toty_inner - (pxp6012_rad_outer + 5), + 10]) + cylinder(r= pxp6012_rad, h= totz_outer); + + // vent connector + translate([chargingconn_x, + toty_inner - (pxp6012_rad_outer*2 + 5 + 15 + + jdae12pa_rad_outer), + 10]) + cylinder(r= jdae12pa_rad, h= totz_outer); + + translate([switch_x, toty_inner, totz_inner/2]) + rotate([90,0,90]) + S1930_Negative(); + + AtGlands() + GlandNegative(cabledia); + + translate(-$sealingbox_cnrrad * [1,1,0] + + [totx_inner, toty_inner/2, -wallthick]) + rotate([0,0,180]) + scale([2,2,1]) + Commitid_Full16_M(); + } +} + +module BoxPrint(){ ////toplevel + rotate([-90,0,-90]) + Box(); +} + +module Lid(){ ////toplevel + $sealingbox_sz = sb_box_sz; + difference(){ + union(){ + AtSealingBox() + SealingBox_RectLid(); + translate([0, -wallthick, -SealingBox_lidbigger()]) + mirror([0,0,1]) + StrapKeepers([ straps_every : straps_every + : totx_inner-straps_every ]); + } + + translate($sealingbox_cnrrad * [1,0,1]) + rotate([90,0,0]) + scale([1.5, 1.5, 1]) + Commitid_Small16_M(); + } +} + +module LidPrint(){ ////toplevel + rotate([90,0,-90]) + Lid(); +} + +module Demo(){ ////toplevel + color("blue") Box(); + color("red") Lid(); +} + +//TestWall(); +//ProfileDemos(); +//TestSealBox(); +//TestSealLid(); +//FArcSegment_mask(350); +//StrapKeepers(); diff --git a/bike-phone-mount.scad b/bike-phone-mount.scad new file mode 100644 index 0000000..bb69726 --- /dev/null +++ b/bike-phone-mount.scad @@ -0,0 +1,101 @@ +// -*- C -*- + +// should rename this to actual name of the product + +include + +mount_lip_height = 2.0 - 0.15 - 0.15; +mount_lip_depth = 2.5 /*?*/ - 0.30; +mount_neck_width = 26.5 - 0.55 - 0.15; +mount_neck_length = 1.5 + 0.50; + +mount_diag_outer = 34.8 - 0.50; +mount_diag_inner = 34.6 - 0.20 - 0.50; + +mount_slope = .65; +mount_extra_slope = 3; + +mount_demo_ceil = 4; + +// calculated + +mnep0 = [0,0]; +mnep1 = mnep0 + [0,1] * mount_neck_length; +mnep7 = mnep0 + [1,0] * mount_lip_depth; +mnep2 = [ mnep7[0] + mount_extra_slope, mnep1[1] + mount_slope * (mnep7[0] + mount_extra_slope - mnep1[0]) ]; +mnep3 = mnep2 + [0, 0.1]; +mnep4 = [ mnep0[0]-1, mnep3[1] ]; +mnep6 = mnep7 + [0,-1] * mount_lip_height; +mnep5 = [ mnep4[0], mnep6[1] ]; +mnepm = [ mnep0[0], mnep3[1] ]; + +mount_total_height = mnep2[1] - mnep6[1]; +mnep_z_offset = -mnep2[1]; +mnep_side_offset = [ mount_neck_width/2, mnep_z_offset ]; + +module MountNeckEdgePlan() { + polygon([ mnep0, + mnep1, + mnep2, + mnep3, + mnep4, + mnep5, + mnep6, + mnep7 ]); +} + +module MountNeckSquare() { + intersection_for (r=[0,90]) { + rotate([0,0,r]){ + linextr_y_xz(-100,100,convexity=10){ + for (m=[0,1]) { + mirror([m,0]) { + translate(mnep_side_offset) MountNeckEdgePlan(); + rectfromto([-0.1, -mount_total_height], + mnep_side_offset + mnepm); + } + } + } + } + } +} + +module MountDiagonal() { + rotate([0,0,45]){ + translate([0,0, -mount_total_height]){ + linextr(0, mount_lip_height) + square(center=true, mount_diag_outer); + linextr(0, mount_total_height) + square(center=true, mount_diag_inner); + linextr(mount_lip_height + mount_neck_length, + mount_total_height + 1) + square(center=true, 100); + } + } +} + +module MountDemoCeil() { + c = mount_demo_ceil + mount_extra_slope; + linextr(0, 0.8) { + square(mount_neck_width + 2*(mount_demo_ceil + mount_extra_slope), + center=true); + } +} + +module Mount(){ + intersection(){ + MountNeckSquare(); + MountDiagonal(); + } +} + +module MountDemo(){ ////toplevel + Mount(); + MountDemoCeil(); +} + +//MountNeckEdgePlan(); +//MountNeck(); +//MountDemoCeil(); +//MountDiagonal(); +//MountDemo(); diff --git a/bike-stalk-led-mount.scad b/bike-stalk-led-mount.scad new file mode 100644 index 0000000..a3c0c4e --- /dev/null +++ b/bike-stalk-led-mount.scad @@ -0,0 +1,71 @@ +// -*- C -*- + +include + +stalk_dia = 6.4 + 0.25; + +length = 50; +width = 12; + +strap_below = 2; +strap_thick = 2; +strap_width = 5; +strap_above = 0.25; + +arch_above = 2; + +inside_gap = 0.5; + +// calculated + +height_base = stalk_dia/2 - inside_gap/2; +above_height = height_base + arch_above; +below_height = height_base + max(arch_above, + strap_below + strap_thick + strap_above); + +module StalkCutout(){ + translate([-length,0,0]) + rotate([0,90,0]) + cylinder(r= stalk_dia/2, h=length*2, $fn=40); +} + +module SomeBlockBase(height){ + translate([0,0, height/2 + inside_gap/2]) { + difference(){ + cube([length, width, height], center=true); + translate([-length/2, 0, height/2]) + Commitid_BestCount([length*.66, width/2]); + } + } +} + +module BlockAbove(){ ////toplevel + difference(){ + SomeBlockBase(above_height); + StalkCutout(); + } +} + +module BlockBelow(){ ////toplevel + difference(){ + SomeBlockBase(below_height); + StalkCutout(); + translate([0,0, inside_gap/2 + strap_above + stalk_dia/2 + strap_thick/2]) + cube([strap_width, width*2, strap_thick], center=true); + } +} + +module BlockAbovePrint(){ ////toplevel + rotate([180,0,0]) BlockAbove(); +} + +module BlockBelowPrint(){ ////toplevel + rotate([180,0,0]) BlockBelow(); +} + +module Demo(){ ////toplevel + BlockAbove(); + rotate([180,0,0]) BlockBelow(); +} + +//Demo(); diff --git a/biscuits.scad b/biscuits.scad new file mode 100644 index 0000000..5f6a42a --- /dev/null +++ b/biscuits.scad @@ -0,0 +1,30 @@ + +scale=0.75; +rad=30*scale; +hbase=28.4*scale; +voff=10*scale; +height=70*scale; + +wallheight = 15; +wallthick=0.8; + +module flatsolid() { + circle(r=rad,$fn=50); + polygon(points=[[-hbase,voff],[hbase,voff],[0,height]]); +} + +module mink() { + minkowski() { + flatsolid(); + circle(r=wallthick/2); + } +} + +module hollow() { + difference() { + mink(); + flatsolid(); + } +} + +linear_extrude(height=wallheight) hollow(); diff --git a/brompton-computer-guard.scad b/brompton-computer-guard.scad new file mode 100644 index 0000000..f0154c0 --- /dev/null +++ b/brompton-computer-guard.scad @@ -0,0 +1,107 @@ +// -*- C -*- + +arch_height = 18; +arch_width = 75; +end_width = 25; + +arch_thick = 4; + +arch_breadth = 25; + +hole_dia = 4 + 0.5; + +pbase_tab = 12; +pbase_thick = 4; +inner_pbase_thick = 12; +inner_pbase_rad_mul = 3; + +// computed + +arch_alpha = atan(arch_height / (arch_width/2)); +arch_beta = 2*arch_alpha; +echo(arch_alpha,arch_beta); +arch_in_rad = arch_width/2 / sin(arch_beta); +arch_to_chord = arch_in_rad * cos(arch_beta); + +echo(inner_pbase_thick); + +inner_pbase_rad = arch_in_rad * inner_pbase_rad_mul; + +end_thick = arch_thick; + +holes = [[[ 5 , 5 ], [16 , 21]], // left + [[ 18.5, 4.5], [ 4.5, 21]]]; // right + +module ArchCircle(rad){ + translate([0,-arch_to_chord]) + circle(rad, $fa=0.1); +} + +module ArchProfile(pbase){ + intersection(){ + translate([-200,0]) + square([400,200]); + difference(){ + union(){ + ArchCircle(arch_in_rad + arch_thick); + for (m=[0,1]) + mirror([m,0]) + translate([arch_width/2,0]) + multmatrix([[1,pbase ? -0.75 : 0,0,0], + [0,1,0,0], + [0,0,1,0], + [0,0,0,1]]) + square([end_width, pbase ? pbase_tab : end_thick]); + } + } + } +} + +module Holes(){ + for (m=[0,1]) { + mirror([1-m,0]) + translate([arch_width/2, 50, 0]) + rotate([90,0,0]) + for (h=holes[m]) { + translate(h) + cylinder(r=hole_dia/2, h=100, $fn=20); + } + } +} + +module MainCutout(){ + ArchCircle(arch_in_rad); +} + +module Arch(){ + difference(){ + rotate([0,0,180]){ + linear_extrude(height=arch_breadth) { + difference(){ + ArchProfile(false); + MainCutout(); + } + } + difference(){ + translate([0,0, arch_breadth - pbase_thick]) + linear_extrude(height=pbase_thick){ + difference(){ + hull(){ + ArchProfile(true); + ArchProfile(false); + } + intersection(){ + MainCutout(); + translate([0, -inner_pbase_thick + - (inner_pbase_rad - arch_in_rad)]) + ArchCircle(inner_pbase_rad); + } + } + } + } + } + Holes(); + } +} + +rotate([0,0,45]) translate([0,0,arch_breadth]) rotate([0,180,0]) Arch(); diff --git a/cable-hole-trunking-cover.scad b/cable-hole-trunking-cover.scad new file mode 100644 index 0000000..ed2bc31 --- /dev/null +++ b/cable-hole-trunking-cover.scad @@ -0,0 +1,119 @@ +// -*- C -*- + +holedia = 25; +tapethick = 1.5; +cutoutsz= 15; +innerz = 11; + +sidesflatbase = 2; +endsflatbase = 8; + +basex = holedia + endsflatbase*2; +basey = holedia + sidesflatbase*2; + +bevely = 2.75; +bevelslope = 0.75; +bevelz = bevely / bevelslope;; +basebevelt = 3; + +sideslop = 0.5; + +basebaset = 2; +sidewallt = 2; + +lidt = 1.3; +endwallt = 2; +zslop = 0.75; +endslop = 0.75; + +module sheared_cube(sz, xperz, yperz) { + multmatrix([[1,0,xperz,0], + [0,1,yperz,0], + [0,0,1, 0], + [0,0,0, 1]]) + cube(sz); +} + +module Base(cutouty){ + echo(cutouty); + difference(){ + union(){ + for (mir=[0,1]) mirror([0,mir,0]) { + translate([0, basey/2 - basebevelt, 0]) + sheared_cube([basex, basebevelt, bevelz], 0, bevelslope); + cube([basex, basey/2, basebaset]); + } + } + translate([basex/2, 0, -1]) + cylinder(r=holedia/2, h=bevelz+2); + } + rotate([90, 0, 90]) { + linear_extrude(height=endwallt) { + difference(){ + for (mir=[0,1]) mirror([mir,0,0]) { + polygon([[-0.1, 0], + [basey/2, 0], + [basey/2 + bevely, bevelz], + [basey/2 + bevely, innerz], + [-0.1, innerz]]); + } + translate([cutouty, 0]) + square(size=[cutoutsz, 3*innerz], center=true); + } + } + } +} + +module Lid(){ + lidx = basex + endslop + endwallt; + for (mir=[0,1]) mirror([0,mir,0]) { + translate([0, basey/2 + sideslop + bevely, 0]) + rotate([90,0,90]) + linear_extrude(height = lidx) + polygon([[0, 0], + [-bevely, 0], + [0, bevelz], + [0, innerz + lidt + zslop], + [sidewallt, innerz + lidt + zslop], + [sidewallt, -tapethick], + [0, -tapethick]]); + translate([0, -1, innerz + zslop]) + cube([lidx, 1 + basey/2 + sideslop + bevely + sidewallt, lidt]); + translate([basex + endslop, -1, -tapethick]) + cube([endwallt, 1 + basey/2 + sideslop + bevely + sidewallt, + tapethick + innerz + zslop + 0.1]); + } +} + +module LidT(){ ////toplevel + rotate([180,0,0]) Lid(); +} + +module BaseCMid(){ ////toplevel + Base(0); +} + +module BaseCTop(){ ////toplevel + Base(basey/2 + bevely - cutoutsz/2); +} + +module BaseCBot(){ ////toplevel + Base(-(basey/2 + bevely - cutoutsz/2)); +} + +module BaseCNone(){ ////toplevel + Base(basey); +} + +module Demo(){ ////toplevel + BaseCTop(); + %Lid(); +} + +//BaseCTop(); +//BaseCMid(); +//BaseCBot(); +//BaseCNone(); +//Lid(); +//LidT(); +//Demo(); diff --git a/cable-splice-clamp.scad b/cable-splice-clamp.scad new file mode 100644 index 0000000..bd27d86 --- /dev/null +++ b/cable-splice-clamp.scad @@ -0,0 +1,138 @@ +// -*- C -*- + +include + +rnom = 3.5 / 2; + +// alpha is slope angle, which is half of inner concave angle that +// wire sits in +alpha = 40; // degrees + +// mu is minimum number of cable radii that cable tangent point (line) +// with splint ought to be away from edge of split +mu = 1/4; + +// wall thickness, and base width as fraction of cable size +wall_r = 3.5 / 6.5; +base_r = 0.75; + +total_len = 60; + +strap_width = 3.0 + 0.5; + +strap_count = 4; + +// for cross-section calculations: +// +// origin O is at intersection of straight line segments forming walls +// C is centre of circle (wire x-section) (of radius r or radius 1) +// which is tangent to lines +// T is said tangent points +// B is inner base point, which is extension of line from B by mu*r + +sina = sin(alpha); +cosa = cos(alpha); +tana = sina/cosa; + +// blah_r is blah where r=1 +// d_AB is distance AB +// dy_AB is " " " vertical component + +d_OT_r = tana; +d_OB_r = tana + mu; + +d_OC_r = 1/cosa; + +dy_OB_r = d_OB_r * sina; + +// *0 and *1 relate to smallest and largest wire +// r[01] is radius +// r10 is radius ratio + +r10 = d_OC_r / dy_OB_r; + +r0 = rnom / sqrt(r10); +r1 = rnom * sqrt(r10); + +x_B_r = d_OB_r * cosa; +y_B_r = -dy_OB_r; + +x_T_r = sina; +y_T_r = -tana * sina; + +wall_x_r = wall_r / tan(90-alpha); + +top = wall_r * r1 - (d_OC_r - 1) * r0; +basew = base_r * rnom; + +echo("dias", r0*2, r1*2, "ratio",r1/r0); + +module CrossSectionHalf(plus=0) { + difference(){ + polygon([[-0.1, y_T_r * r0], + [x_T_r * r0, y_T_r * r0], + [x_B_r * r1, y_B_r * r1], + [x_B_r * r1 + wall_x_r * rnom + plus, y_B_r * r1], + [basew + plus, top], + [-0.1, top]]); + translate([0, -d_OC_r * r0]) + circle(r = r0, $fn=20); + } +} + +module CrossSection(plus=0) { + for (m=[0,1]) { + mirror([m,0]) + CrossSectionHalf(plus); + } +} + +module CrossSectionDemo(){ ////toplevel + color("black") CrossSection(2); + CrossSection(); + for (rc=[["red", r1], + ["blue",r0]]) { + color(rc[0]) translate([0, -d_OC_r * rc[1]]) circle(r = rc[1]); + } +} + +strap_wall_h = 1.5; +strap_wall_l = 2.0; + +writing_dx = total_len / 3; +writing_dy = basew*2; + +module HalfClamp(){ ////toplevel + difference(){ + rotate([90,0,0])rotate([0,90,0]){ + linear_extrude(height=total_len) + CrossSection(); + + for (i=[0 : strap_count]){ + if (i*2 != strap_count) { + translate([0, 0, + total_len * (i + 0.5) / (strap_count + 1)]) + for (m=[0,1]){ + mirror([0,0,m]) + translate([0,0, strap_width/2]) + linear_extrude(height=strap_wall_l) + CrossSection(strap_wall_h); + } + } + } + } + + translate([0, -basew, top]) + Commitid_BestCount([writing_dx, writing_dy]); + } +} + +module HalfClampPrint(){ ////toplevel + rotate([180,0,0]) + HalfClamp(); +} + +//CrossSection(); +//CrossSectionDemo(); +//HalfClamp(); +HalfClampPrint(); diff --git a/calib-fit.scad b/calib-fit.scad new file mode 100644 index 0000000..dbee918 --- /dev/null +++ b/calib-fit.scad @@ -0,0 +1,20 @@ +th=3; +holesz=6; +outsz=15; + +module small(sz=holesz,dsz=0,dz=0) { + cube([sz+dsz,sz+dsz,th+dz], center=true); +} + +module osmall() { + translate([0,outsz/2 + 10,0]) small(); +} +module obig() { + difference() { + cube([outsz,outsz,th], center=true); + small(dz=1); + } +} + +osmall(); +obig(); diff --git a/camera-mount.scad b/camera-mount.scad new file mode 100644 index 0000000..fec9096 --- /dev/null +++ b/camera-mount.scad @@ -0,0 +1,28 @@ +// -*- C -*- + +include + +inch = 25.4; + +negative_dia = inch * 1/4. + 0.375; +negative_default_l = 10.0; + +negative_tpi = 20; +negative_pitch = inch/negative_tpi; +negative_chamfer = negative_pitch/2; + +module CameraMountThread(l){ + rotate([0,180,0]) + english_thread(diameter=negative_dia/inch, + threads_per_inch=negative_tpi, + leadin=0, internal=true, test=$test, + length= (l + inch/19) / inch); + hull(){ + translate([0,0, negative_chamfer]) + cylinder(r= negative_dia/2 + negative_chamfer*2, + h=1); + mirror([0,0,1]) + cylinder(r= negative_dia/2 - negative_chamfer*2, + h= negative_chamfer*3); + } +} diff --git a/chimney-cable-retainer.scad b/chimney-cable-retainer.scad new file mode 100644 index 0000000..2f783d0 --- /dev/null +++ b/chimney-cable-retainer.scad @@ -0,0 +1,71 @@ +// -*- C -*- + +include + +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); + } + } +} diff --git a/clip-spring-holder-clip.scad b/clip-spring-holder-clip.scad new file mode 100644 index 0000000..11f395f --- /dev/null +++ b/clip-spring-holder-clip.scad @@ -0,0 +1,48 @@ +// -*- C -*- +// +// For holding the spring while reassembling a candle holder. + +include + +spring_body_w = 5.0; +spring_body_l = 6.0; +axle_dia = 2.0; +recess_d = 13.0; +total_len = 45.0; + +th_y = 1.5; +th_x = 2; +handle_th = 2.5; + +// calculated + +outer_sz = [spring_body_l + th_x*2, spring_body_w + th_y*2]; +handle_sz = [outer_sz[0], handle_th]; +th_z = th_x; + +echo(outer_sz); + +module OuterElevation(){ + square(center=true, outer_sz); +} + +module Elevation(){ + difference(){ + OuterElevation(); + + square(center=true, [spring_body_l, spring_body_w]); + square(center=true, [outer_sz[0] + 10, axle_dia]); + } +} + +module Clip(){ + linextr(-th_z, recess_d) Elevation(); + linextr(-th_z, 0) OuterElevation(); + linextr(recess_d - total_len, 0) square(center=true, handle_sz); +} + +module Print(){ + rotate([0, 90, 0]) Clip(); +} + +Print(); diff --git a/cliphook.scad b/cliphook.scad new file mode 100644 index 0000000..22f08f5 --- /dev/null +++ b/cliphook.scad @@ -0,0 +1,103 @@ +// -*- C -*- +// +// cliphook.scad +// +// 3D design for a small clippy hook +// Copyright 2012,2016 Ian Jackson +// +// This work is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This work is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this work. If not, see . + +function ClipHook_r2(w,g,l,ye,k) = w/2 + g + w/2; +function ClipHook_r3(w,g,l,ye,k) = k * (ClipHook_r2(w,g,l,ye,k) + w + g); +function ClipHook_yd(w,g,l,ye,k) = g/2 + w + g + w/2 - ClipHook_r3(w,g,l,ye,k); +function ClipHook_xe(w,g,l,ye,k) = + l*1.5 + w + + sqrt(pow( ClipHook_r3(w,g,l,ye,k), 2) - + pow( ClipHook_yd(w,g,l,ye,k) - ye, 2)); + +module FlatArc(cx,cy,r1,r2,a1,a2=361,$fn=$fn) { + astep = (a2-a1)/6; + size = 5*(r2/2); + translate([cx,cy,0]) { + intersection() { + difference() { + circle(r=r2); + translate([0,0,-1]) + circle(r=r1); + } + scale(size) { + for (ai=[0:4]) { + //echo(" jarc ", a1,a2, astep, ai, a1 + astep*ai ); + rotate(a1 + astep*ai) { + polygon([ [0,0], [1,0], + [cos(astep*2),sin(astep*2)] ]); + } + } + } + } + } +} + +module ClipHook_2D(w,g,l,ye,k,h) { + r2 = ClipHook_r2(w,g,l,ye,k); + r3 = ClipHook_r3(w,g,l,ye,k); + yd = ClipHook_yd(w,g,l,ye,k); + xe = ClipHook_xe(w,g,l,ye,k); + + xd = l*1.5 + w; + xc = -l/2; + yc = g/2 + w/2; + + alpha = atan2((xe-xd)/r3, (ye-yd)/r3); + + echo("ClipHook(w g l ye k h) ", w, g, l, ye, k, h); + echo("ClipHook r2 r3 xd yd xe =", r2,r3, xd,yd, xe); + + $fn = 20; + + module jcirc(x,y) { translate([x,y,0]) circle(r=w/2); } + module jbox(y,x1,x2) { translate([x1,y-w/2,0]) square(size=[x2-x1, w]); } + module jarc(cx,cy,r,a1=0,a2=360) { FlatArc(cx,cy,r-w/2,r+w/2,a1,a2); } + + jcirc(-xc, -yc); + jbox(-yc, xc, -xc); + jarc(xc, yc, r2, 90, 270); + jbox(yc+r2, xc, xd); + jarc(xd, yd, r3, 90-alpha, 90); + jcirc(xe,ye); +} + +module ClipHook(w=1.2, g=0.2, l=0.0, ye=0, k=2.0, h=3.5, demo=false, + cupcaph=0, cupgapg=0) { + difference() { + linear_extrude(height=h) + ClipHook_2D(w,g,l,ye,k); + if (cupcapg != 0) { + translate([-g+0.01,-(w+g),h-cupcapg]) + cube([w,(w+g),cupcaph+1]); + } + } + if (cupcaph != 0) { + translate([-l/2, g/2+w/2, h-0.01]) + intersection() { + cylinder(r=ClipHook_r2(w,g,l,ye,k)+w*0.4, h=cupcaph, $fn=16); + translate([-50-g,-50,-1]) cube([50,100,h+2]); + } + } +} + +if (ClipHook_demo) { + ClipHook(l=0, k=1, cupcaph=1, cupcapg=0.4); + %translate([l+w,0,0]) rotate(180) ClipHook(l=0, k=1, cupcaph=1, cupcapg=0.4); +} diff --git a/commitid-2d-test.scad b/commitid-2d-test.scad new file mode 100644 index 0000000..a15de77 --- /dev/null +++ b/commitid-2d-test.scad @@ -0,0 +1,3 @@ +// -*- C -*- +include +Commitid_2DDemo(); diff --git a/commitid-best-test.scad.pl b/commitid-best-test.scad.pl new file mode 100755 index 0000000..b0ca1ba --- /dev/null +++ b/commitid-best-test.scad.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl -w +use strict; + +our @xm = qw(4.7 6.8 8.2 10 12 15 18 22 27 33 47 68 100); +our @ym = qw(3.9 5.6 8.2 12 18 27 39 56); + +sub p { print @_ or die $!; } + +p "include \n"; +p "p = Commitid_pixelsz();\n"; + +my $x = 0; +foreach my $xm (@xm) { + my $y = 0; + foreach my $ym (@ym) { + p " translate([$x,$y] * p) {\n"; + p " difference(){\n"; + p " translate(-0.5*p*[1,1]) square([$xm+1,$ym+1]*p);\n"; + p " square([$xm,$ym]*p);\n"; + p " }\n"; + p " Commitid_BestCount_2D([$xm,$ym] * p, margin=0.2);\n"; + p " }\n"; + $y += $ym + 2; + } + $x += $xm + 2; +} diff --git a/commitid-cube-test-X.scad b/commitid-cube-test-X.scad new file mode 100644 index 0000000..4f2f6fd --- /dev/null +++ b/commitid-cube-test-X.scad @@ -0,0 +1,76 @@ +// -*- C -*- + +include + +baseh= 1; + +sz = 20; + +fdo = [1, 3, 0]; + +$Commitid_depth = 1.0; + +module FD () { + translate(fdo) + Commitid_FontDemo(); +} + +module TC () { ////toplevel + difference(){ + cube([sz,sz,sz]); + translate([0,0, sz]) mirror([0,0,1]) FD(); + rotate([90,0,0]) translate([0,0,0]) FD(); + translate([sz,0,0]) mirror([1,0,0]) rotate([90,0,90]) FD(); + translate([sz,sz,0]) rotate([0,0,180]) FD(); + } + translate([sz,sz,0]) rotate([-90,0,0]) rotate([0,0,180]) FD(); + translate([0,sz,0]) rotate([-90,0,90]) rotate([0,0,180]) FD(); +} + +w = 3; +t = 4; + +fdsz = Commitid_FontDemo_sz(); +d = Commitid_depth(); +ru = Commitid_pixelsz(); + +module TTWall () { + difference(){ + translate([0, 0, -0.1]) + cube([w, sz, sz - 2 + 0.1]); + + translate([0,sz,0]) rotate([90,0,-90]) FD(); + translate([0, sz, 0]) + rotate([90, 0, -90]) + translate(fdo + [0, -ru*2, -d]) cube([fdsz[0], ru, d*2]); + } + translate([w,0,0]) rotate([90,0,90]) FD(); + + translate([0, sz+d, 0]) + rotate([90,0,0]) + translate([0, fdo[1], 0]) cube([d*2, fdsz[1], ru]); +} + +module TT () { ////toplevel + difference(){ + translate([-sz, 0, -t]) + cube([sz*2 + w, sz, t]); + + translate([0,0,-t]) rotate([0,180,0]) FD(); + translate([w,0,0]) rotate([0,0,0]) FD(); + + translate([(sz+w), 0, -t]) rotate([0,180,0]) + Commitid_BestCount([sz+w, sz]); + } + translate([-sz,0,0]) rotate([0,0,0]) FD(); + + TTWall(); + translate([0,0,-t]) rotate([90,0,0]) TTWall(); +} + +echo("pixelsz:", str(Commitid_pixelsz()), + "depth:", Commitid_depth(), + "sz:", Commitid_FontDemo_sz()); + +//TC(); +TT(); diff --git a/commitid-cube-test-Y.scad b/commitid-cube-test-Y.scad new file mode 100644 index 0000000..23aa076 --- /dev/null +++ b/commitid-cube-test-Y.scad @@ -0,0 +1,78 @@ +// -*- C -*- + +$Commitid_depth = 1.0; +$Commitid_pixelsz = 1.5; + +include + +baseh= 1; + +fdo = [1, 3, 0]; + +w = 3; +t = 4; + +fdsz = Commitid_FontDemo_sz(); +d = Commitid_depth(); +ru = Commitid_pixelsz(); +echo($Commitid_pixelsz, ru, fdsz); + +sz = max( fdsz[0], fdsz[1] ) + ru; + +module FD () { + translate(fdo) + Commitid_FontDemo(); +} + +module TC () { ////toplevel + difference(){ + cube([sz,sz,sz]); + translate([0,0, sz]) mirror([0,0,1]) FD(); + rotate([90,0,0]) translate([0,0,0]) FD(); + translate([sz,0,0]) mirror([1,0,0]) rotate([90,0,90]) FD(); + translate([sz,sz,0]) rotate([0,0,180]) FD(); + } + translate([sz,sz,0]) rotate([-90,0,0]) rotate([0,0,180]) FD(); + translate([0,sz,0]) rotate([-90,0,90]) rotate([0,0,180]) FD(); +} + +module TTWall () { + difference(){ + translate([0, 0, -0.1]) + cube([w, sz, sz - 2 + 0.1]); + + translate([0,sz,0]) rotate([90,0,-90]) FD(); + translate([0, sz, 0]) + rotate([90, 0, -90]) + translate(fdo + [0, -ru*2, -d]) cube([fdsz[0], ru, d*2]); + } + translate([w,0,0]) rotate([90,0,90]) FD(); + + translate([0, sz+d, 0]) + rotate([90,0,0]) + translate([0, fdo[1], 0]) cube([d*2, fdsz[1], ru]); +} + +module TT () { ////toplevel + difference(){ + translate([-sz, 0, -t]) + cube([sz*2 + w, sz, t]); + + translate([0,0,-t]) rotate([0,180,0]) FD(); + translate([w,0,0]) rotate([0,0,0]) FD(); + + translate([(sz+w), 0, -t]) rotate([0,180,0]) + Commitid_BestCount([sz+w, sz]); + } + translate([-sz,0,0]) rotate([0,0,0]) FD(); + + TTWall(); + translate([0,0,-t]) rotate([90,0,0]) TTWall(); +} + +echo("pixelsz:", str(Commitid_pixelsz()), + "depth:", Commitid_depth(), + "sz:", Commitid_FontDemo_sz()); + +//TC(); +TT(); diff --git a/commitid-cube-test.scad b/commitid-cube-test.scad new file mode 100644 index 0000000..0a5f21a --- /dev/null +++ b/commitid-cube-test.scad @@ -0,0 +1,74 @@ +// -*- C -*- + +include + +baseh= 1; + +sz = 20; + +fdo = [1, 3, 0]; + +module FD () { + translate(fdo) + Commitid_FontDemo(); +} + +module TC () { ////toplevel + difference(){ + cube([sz,sz,sz]); + translate([0,0, sz]) mirror([0,0,1]) FD(); + rotate([90,0,0]) translate([0,0,0]) FD(); + translate([sz,0,0]) mirror([1,0,0]) rotate([90,0,90]) FD(); + translate([sz,sz,0]) rotate([0,0,180]) FD(); + } + translate([sz,sz,0]) rotate([-90,0,0]) rotate([0,0,180]) FD(); + translate([0,sz,0]) rotate([-90,0,90]) rotate([0,0,180]) FD(); +} + +w = 3; +t = 2; + +fdsz = Commitid_FontDemo_sz(); +d = Commitid_depth(); +ru = Commitid_pixelsz(); + +module TTWall () { + difference(){ + translate([0, 0, -0.1]) + cube([w, sz, sz - t + 0.1]); + + translate([0,sz,0]) rotate([90,0,-90]) FD(); + translate([0, sz, 0]) + rotate([90, 0, -90]) + translate(fdo + [0, -ru*2, -d]) cube([fdsz[0], ru, d*2]); + } + translate([w,0,0]) rotate([90,0,90]) FD(); + + translate([0, sz+d, 0]) + rotate([90,0,0]) + translate([0, fdo[1], 0]) cube([d*2, fdsz[1], ru]); +} + +module TT () { ////toplevel + difference(){ + translate([-sz, 0, -t]) + cube([sz*2 + w, sz, t]); + + translate([0,0,-t]) rotate([0,180,0]) FD(); + translate([w,0,0]) rotate([0,0,0]) FD(); + + translate([(sz+w), 0, -t]) rotate([0,180,0]) + Commitid_BestCount([sz+w, sz]); + } + translate([-sz,0,0]) rotate([0,0,0]) FD(); + + TTWall(); + translate([0,0,-t]) rotate([90,0,0]) TTWall(); +} + +echo("pixelsz:", str(Commitid_pixelsz()), + "depth:", Commitid_depth(), + "sz:", Commitid_FontDemo_sz()); + +//TC(); +TT(); diff --git a/commitid-layering-test.scad b/commitid-layering-test.scad new file mode 100644 index 0000000..04d8c83 --- /dev/null +++ b/commitid-layering-test.scad @@ -0,0 +1,31 @@ +// -*- C -*- + +include + +baseh= 1; +basex = 31; +basey= 20; +basexpos = -12; +baseypos = -4; + +module Body(){ + mirror([0,0,1]) + translate([basexpos, baseypos]) + cube([basex, basey, baseh]); +} + +difference(){ + Body(); + translate([basexpos, baseypos, -baseh]) + Commitid_BestCount_M([basex,basey], margin=3); + + translate([-6, 6, -0.4]) + cylinder(r=5, h=3, $fn=50); +} + +translate([-6, 6, -0.5]) + cylinder(r=4, h=3.5, $fn=50); + +echo("pause height (mm)", baseh+0.01); + +Commitid_FontDemo(); diff --git a/crossbar-computer-led-mount.scad b/crossbar-computer-led-mount.scad new file mode 100644 index 0000000..535c680 --- /dev/null +++ b/crossbar-computer-led-mount.scad @@ -0,0 +1,340 @@ +// -*- C -*- + +led_dia = 5 + 0.6; +led_depth = 5; + +led_tip_height_above_crossbar = 70; +led_angle = -60; +crossbar_dia = 25; // fixme + +vert_space_inside = 8; +backfront_space_inside = 12; +width_space_inside = 10; + +backfront_mate_size = 25; +tower_frontheight = 10; +tower_base_height = 20; +tower_slot_width = 3; + +cableclamp_ctie_width = 4.0 + 1.0; +cableclamp_ctie_thick = 2.5 + 0.5; + +lidclamp_ctie_width = 4.0 + 1.0; +lidclamp_ctie_thick = 2.5 + 0.5; + +base_ctie_width = 4.0 + 1.0; +base_ctie_thick = 2.5 + 0.5; + +tube_ctie_width = 4.0 + 1.0; +tube_ctie_thick = 2.5 + 0.5; + +// tuning + +tower_over_angle = 45; +tower_wall_thick = 1.6; +tower_forehead_angle = 30; +lid_wall_thick = 1.6; +lid_slop = 0.75; +//cableclamp_ctie_anchor = 5; +lidclamp_cableclamp_ctie_between = 0; +base_ctie_anchor = 5; +tube_ctie_anchor = 5; +protrusion_size = 2; +protrusion_none_frontback = 10; +protrusion_slop = 0.25; +cableclamp_ctie_z = tower_frontheight/2; + +towerleg_backfront = 5; +towerleg_width = 3; +towerleg_foot_gap = 2; +towerleg_foot_backfront = 20; +towerleg_foot_width = 40; +towerleg_foot_height = 10; +towerleg_yslope = 0.7; +towerleg_xslope = 0.3; +echo(sqrt(towerleg_yslope*towerleg_yslope+towerleg_xslope*towerleg_xslope)); + +//--- tests --- + +test_width = 24; +test_height = 24; + +test_thicks = [9,14,21]; + +module Tests(){ ////toplevel + for (thicki=[0:len(test_thicks)-1]) { + translate([thicki*test_width-0.5, 0, 0]) { + difference(){ + cube([test_width, + test_thicks[thicki] + led_depth, + test_height]); + translate([test_width/2, -1, test_height/2]) + rotate([-90,0,0]) + cylinder(r=led_dia/2, h=led_depth+1, $fn=30); + } + } + } +} + +//Tests(); + +//--- real thing --- + +// calculated + +tower_overhang = led_dia * 2.5; +tower_width = width_space_inside + tower_wall_thick*2; + +tower_over_max_y = tower_overhang * sin(tower_over_angle); +tower_over_max_z = tower_frontheight + tower_overhang * cos(tower_over_angle); +tower_total_max_z = tower_over_max_z + vert_space_inside + led_depth; +tower_rearwall_y = -(backfront_space_inside + tower_wall_thick); +led_head_y = tower_over_max_y/2; +led_head_z = tower_frontheight + tower_overhang*sin(tower_over_angle)/2; +backfront_mate_extra = (backfront_mate_size - (-tower_rearwall_y)); + +tower_height_contribution = led_head_z + tower_base_height; + +base_ctie_anchor_eff = base_ctie_anchor+base_ctie_thick/2; +tube_ctie_anchor_eff = tube_ctie_anchor+tube_ctie_thick/2; + +base_width = 0.7 * crossbar_dia; +base_backfront = backfront_mate_extra - tower_rearwall_y; +base_height = led_tip_height_above_crossbar - tower_height_contribution; + +protrusion_frontback = base_backfront - protrusion_none_frontback; + +echo(tower_height_contribution, base_height); + +module TowerWallCrossSection(){ + // generates a 2D shape - a polygon + // x is what is going to be -y + // y is what is going to be z + polygon([[0, 0], + [0, tower_frontheight], + [-tower_over_max_y, tower_over_max_z], + [-tower_over_max_y + + tan(tower_forehead_angle) * (vert_space_inside + led_depth), + tower_total_max_z], + [-tower_rearwall_y, tower_total_max_z], + [-tower_rearwall_y, 0], + [-tower_rearwall_y, -tower_base_height], + [-backfront_mate_extra, -tower_base_height]], + convexity=5); +} + +module TowerWallSomeEdge(front){ + minkowski(){ + difference(){ + TowerWallCrossSection(); + translate([front ? 0.10 : -0.10, 0]) + TowerWallCrossSection(); + } + circle(r=tower_wall_thick, $fn=8); + } +} + +module TowerBulkCrossSection(){ + intersection(){ + TowerWallCrossSection(); + union(){ + translate([-led_head_y, led_head_z]) + circle(r = led_depth); + TowerWallSomeEdge(true); + translate([-50, -50]) + square([100, 50]); + } + } +} + +module TowerRearWallCrossSection(){ + intersection(){ + TowerWallCrossSection(); + union(){ + intersection(){ + translate([0,-10]) square([100, 10+led_head_z]); + TowerWallSomeEdge(false); + } + TowerBulkCrossSection(); + } + } +} + + +module TowerCrossSectionDemo(){ + %TowerWallCrossSection(); + //TowerBulkCrossSection(); + TowerRearWallCrossSection(); +} + +module TowerMain(){ + for (mir=[0,1]) + mirror([mir,0,0]) rotate([90,0,-90]) { + translate([0,0, tower_width/2-tower_wall_thick]) + linear_extrude(height=tower_wall_thick) { + TowerWallCrossSection(); + } + translate([0,0,-1]) + linear_extrude(height=tower_width/2+0.9) + union(){ + TowerBulkCrossSection(); + hull(){ + intersection(){ + TowerWallCrossSection(); + translate([-30, -30]) + square([30 + 0.1, 30 + tower_frontheight]); + } + } + } + translate([0,0, tower_slot_width/2]) + linear_extrude(height=(tower_width - tower_slot_width)/2 - 0.2) + TowerRearWallCrossSection(); + } +} + +module LedHole(){ + translate([0, led_head_y, led_head_z]) + rotate([90 + led_angle, 0, 0]) + translate([0,0,-10]) + cylinder(r=led_dia/2, h=led_depth+1+10, $fn=26, $fa=10); +} + +module TowerProper(){ + difference(){ + TowerMain(); + LedHole(); + // passages for cable ties + translate([0, + tower_rearwall_y/2, + cableclamp_ctie_z + + cableclamp_ctie_width/2 + lidclamp_ctie_thick/2 + + lidclamp_cableclamp_ctie_between]) + cube([50, lidclamp_ctie_width, lidclamp_ctie_thick], center=true); + translate([0, + (backfront_mate_extra+tower_rearwall_y)/2, + -tower_base_height + + max(protrusion_size + protrusion_slop + 0.1, + base_ctie_anchor_eff)]) + cube([50, base_ctie_width, base_ctie_thick], center=true); +// for (extra_y=[0, -(cableclamp_ctie_thick + cableclamp_ctie_anchor)]) { +// translate([-tower_width/2, +// -cableclamp_ctie_thick/2 - tower_wall_thick + extra_y, +// cableclamp_ctie_z]) +// cube([tower_wall_thick+2, +// cableclamp_ctie_thick, +// cableclamp_ctie_width], center=true); +// } + for (mir=[0,1]) + mirror([mir,0,0]) { + translate([tower_width/4, 20, cableclamp_ctie_z]) + cube([cableclamp_ctie_thick, + tower_wall_thick*2+1+40, + cableclamp_ctie_width], + center=true); + } + translate([0, tower_rearwall_y, -tower_base_height]) + BaseRegistrationProtrusion(protrusion_slop); + } +} + +module Tower(){ ////toplevel + TowerProper(); + for (mir=[0,1]) { + mirror([mir,0,0]){ + translate([0, + tower_rearwall_y + 0.1, + -1]) + mirror([0,0,1]) + multmatrix([[1,0, towerleg_xslope,0], + [0,1,-towerleg_yslope,0], + [0,0,1,0], + [0,0,0,1]]) + cube([towerleg_width, towerleg_backfront, tower_base_height-2]); + } + } + translate([-towerleg_foot_width/2, + tower_rearwall_y - towerleg_foot_gap, + -tower_base_height]) + mirror([0,1,0]) + cube([towerleg_foot_width, towerleg_foot_backfront, towerleg_foot_height]); +} + +module TowerMainHull(){ + hull(){ TowerMain(); } +} + +module Lid(){ + intersection(){ + difference(){ + minkowski(){ + TowerMainHull(); + sphere(r=lid_wall_thick+lid_slop, $fn=8); + } + minkowski(){ + TowerMainHull(); + sphere(r=lid_slop, $fn=6); + } + } + translate([-50,-50,led_head_z]) cube([100,100,100]); + } +} + +module LidT(){ ////toplevel + rotate([180,0,0]) Lid(); +} + +module BaseRegistrationProtrusion(extra){ + size = protrusion_size + extra; + translate([0, base_backfront/2, 0]){ + hull(){ + translate([0,0, -0.5]) + cube([protrusion_size*2, protrusion_frontback, 1.0], center=true); + translate([0, 0, protrusion_size-0.5]) + cube([0.05, protrusion_frontback-protrusion_size*2, 1.0], center=true); + } + } +} + +module Base(){ + difference(){ + mirror([0,0,1]){ + hull(){ + translate([-tower_width/2, 0, 0]) + cube([tower_width, base_backfront, 0.1]); + translate([-base_width/2, 0, base_height]) + cube([base_width, base_backfront, crossbar_dia/2]); + } + } + translate([0, base_backfront/2, -base_ctie_anchor_eff]) + cube([100, base_ctie_width, base_ctie_thick], center=true); + translate([0, base_backfront/2, -base_height + tube_ctie_anchor_eff]) + cube([100, tube_ctie_width, tube_ctie_thick], center=true); + translate([0, -1, -(base_height + crossbar_dia/2)]) + rotate([-90,0,0]) + cylinder(r=crossbar_dia/2, h=101); + } + BaseRegistrationProtrusion(0.0); +} + +module BaseT(){ ////toplevel + rotate([90,0,0]) Base(); +} + +module Demo(){ + Tower(); + %Lid(); + translate([0,0, 25]) Lid(); + translate([0, tower_rearwall_y, -(tower_base_height+5)]) Base(); +} + +//TowerCrossSectionDemo(); +//TowerWallSomeEdge(false); +//TowerWallFrontEdge(); +//TowerMainHull(); +//LidT(); +//Tower(); +//Lid(); +//BaseRegistrationProtrusion(); +//Base(); +//BaseT(); +//Demo(); diff --git a/dell-psu-glow-lampshade.scad b/dell-psu-glow-lampshade.scad new file mode 100644 index 0000000..8733948 --- /dev/null +++ b/dell-psu-glow-lampshade.scad @@ -0,0 +1,16 @@ +// -*- C -*- + +india = 11.01 + 0.50; +t = 0.9; +l = 7.5; + +$fa = 3; +$fs = 0.2; + +linear_extrude(height=l, convexity=10) { + difference(){ + circle(r = india/2 + t); + circle(r = india/2); + } +} + diff --git a/deore-crank-remover.scad b/deore-crank-remover.scad new file mode 100644 index 0000000..1b0a738 --- /dev/null +++ b/deore-crank-remover.scad @@ -0,0 +1,34 @@ +// -*- C -*- + +outdia=15.1; +india=13.0; +depth=10; + +eoutrad = outdia/2 + 1.0; +einrad = india/2 - 1.0; +edepth = depth + 3; + +handledepth = 5; +handlewidth = 20; +handlelength = 70; + +module FlatSplines(){ + for (rot=[0:7]) { + rotate([0,0, rot*360/8]) + for (m=[0,1]) { + mirror([m,0,0]) + polygon([[-0.1, 0], + [-0.01, eoutrad], + [einrad * sin(22.5), einrad * cos(22.5)], + [einrad * sin(22.5), einrad * cos(22.5) - 3], + [1, 0]]); + } + } +} + +translate([0,0,-1]) + linear_extrude(height=edepth+1) + FlatSplines(); + +translate([0,0,-handledepth/2]) + cube([handlelength,handlewidth,handledepth], center=true); diff --git a/digispark-with-cable.scad b/digispark-with-cable.scad new file mode 100644 index 0000000..1ad978c --- /dev/null +++ b/digispark-with-cable.scad @@ -0,0 +1,329 @@ +// -*- C -*- +// +// Print (fine detail settings): +// +// * Bottom +// * MiddlePrint +// * CoverPrint + +include + +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(); +} diff --git a/distort-stl b/distort-stl new file mode 100755 index 0000000..68290c4 --- /dev/null +++ b/distort-stl @@ -0,0 +1,296 @@ +#!/usr/bin/perl -w +# +# usage: +# ./distort-stl 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 () { + 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; + if 0; # suppresses Name "main::I" used only once + +while (@ARGV) { + my $op = shift_arg; + $op =~ y/-/_/; + &{ ${*::}{"op__$op"} }; +} + +select $otmp; + +print "solid distort-stl\n"; + +foreach my $t (@$triangles) { + print " facet normal 0 0 0\n"; + print " outer loop\n"; + die unless @$t==4; + foreach my $p (@$t[0..2]) { + die unless @$p==3; + print " vertex"; + printf " %.18g", $_ foreach @$p; + print "\n"; + } + print " endloop\n"; + print " endfacet\n"; +} + +print "endsolid distort-stl\n"; + +flush $otmp; + +if (!$raw) { + system "admesh --normal-values $admesh_stdout $otmp"; +} diff --git a/diziet-utils/COPYING b/diziet-utils/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/diziet-utils/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/diziet-utils/README.md b/diziet-utils/README.md new file mode 100644 index 0000000..ec4c50d --- /dev/null +++ b/diziet-utils/README.md @@ -0,0 +1,24 @@ +# 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). diff --git a/commitid.scad.pl b/diziet-utils/commitid.scad.pl similarity index 100% rename from commitid.scad.pl rename to diziet-utils/commitid.scad.pl diff --git a/funcs.scad.cpp b/diziet-utils/funcs.scad.cpp similarity index 100% rename from funcs.scad.cpp rename to diziet-utils/funcs.scad.cpp diff --git a/reprap-objects.make b/diziet-utils/reprap-objects.make similarity index 100% rename from reprap-objects.make rename to diziet-utils/reprap-objects.make diff --git a/diziet-utils/threads.scad b/diziet-utils/threads.scad new file mode 100644 index 0000000..b2eee23 --- /dev/null +++ b/diziet-utils/threads.scad @@ -0,0 +1,407 @@ +/* + * ISO-standard metric threads, following this specification: + * http://en.wikipedia.org/wiki/ISO_metric_screw_thread + * + * Copyright 2020 Dan Kirshner - dan_kirshner@yahoo.com + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 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. + * + * See . + * + * Version 2.5. 2020-04-11 Leadin option works for internal threads. + * Version 2.4. 2019-07-14 Add test option - do not render threads. + * Version 2.3. 2017-08-31 Default for leadin: 0 (best for internal threads). + * Version 2.2. 2017-01-01 Correction for angle; leadfac option. (Thanks to + * Andrew Allen .) + * Version 2.1. 2016-12-04 Chamfer bottom end (low-z); leadin option. + * Version 2.0. 2016-11-05 Backwards compatibility (earlier OpenSCAD) fixes. + * Version 1.9. 2016-07-03 Option: tapered. + * Version 1.8. 2016-01-08 Option: (non-standard) angle. + * Version 1.7. 2015-11-28 Larger x-increment - for small-diameters. + * Version 1.6. 2015-09-01 Options: square threads, rectangular threads. + * Version 1.5. 2015-06-12 Options: thread_size, groove. + * Version 1.4. 2014-10-17 Use "faces" instead of "triangles" for polyhedron + * Version 1.3. 2013-12-01 Correct loop over turns -- don't have early cut-off + * Version 1.2. 2012-09-09 Use discrete polyhedra rather than linear_extrude () + * Version 1.1. 2012-09-07 Corrected to right-hand threads! + */ + +// Examples. +// +// Standard M8 x 1. +// metric_thread (diameter=8, pitch=1, length=4); + +// Square thread. +// metric_thread (diameter=8, pitch=1, length=4, square=true); + +// Non-standard: long pitch, same thread size. +//metric_thread (diameter=8, pitch=4, length=4, thread_size=1, groove=true); + +// Non-standard: 20 mm diameter, long pitch, square "trough" width 3 mm, +// depth 1 mm. +//metric_thread (diameter=20, pitch=8, length=16, square=true, thread_size=6, +// groove=true, rectangle=0.333); + +// English: 1/4 x 20. +//english_thread (diameter=1/4, threads_per_inch=20, length=1); + +// Tapered. Example -- pipe size 3/4" -- per: +// http://www.engineeringtoolbox.com/npt-national-pipe-taper-threads-d_750.html +// english_thread (diameter=1.05, threads_per_inch=14, length=3/4, taper=1/16); + +// Thread for mounting on Rohloff hub. +//difference () { +// cylinder (r=20, h=10, $fn=100); +// +// metric_thread (diameter=34, pitch=1, length=10, internal=true, n_starts=6); +//} + + +// ---------------------------------------------------------------------------- +function segments (diameter) = min (50, max (ceil (diameter*6), 25)); + + +// ---------------------------------------------------------------------------- +// diameter - outside diameter of threads in mm. Default: 8. +// pitch - thread axial "travel" per turn in mm. Default: 1. +// length - overall axial length of thread in mm. Default: 1. +// internal - true = clearances for internal thread (e.g., a nut). +// false = clearances for external thread (e.g., a bolt). +// (Internal threads should be "cut out" from a solid using +// difference ()). Default: false. +// n_starts - Number of thread starts (e.g., DNA, a "double helix," has +// n_starts=2). See wikipedia Screw_thread. Default: 1. +// thread_size - (non-standard) axial width of a single thread "V" - independent +// of pitch. Default: same as pitch. +// groove - (non-standard) true = subtract inverted "V" from cylinder +// (rather thanadd protruding "V" to cylinder). Default: false. +// square - true = square threads (per +// https://en.wikipedia.org/wiki/Square_thread_form). Default: +// false. +// rectangle - (non-standard) "Rectangular" thread - ratio depth/(axial) width +// Default: 0 (standard "v" thread). +// angle - (non-standard) angle (deg) of thread side from perpendicular to +// axis (default = standard = 30 degrees). +// taper - diameter change per length (National Pipe Thread/ANSI B1.20.1 +// is 1" diameter per 16" length). Taper decreases from 'diameter' +// as z increases. Default: 0 (no taper). +// leadin - 0 (default): no chamfer; 1: chamfer (45 degree) at max-z end; +// 2: chamfer at both ends, 3: chamfer at z=0 end. +// leadfac - scale of leadin chamfer length (default: 1.0 = 1/2 thread). +// test - true = do not render threads (just draw "blank" cylinder). +// Default: false (draw threads). +module metric_thread (diameter=8, pitch=1, length=1, internal=false, n_starts=1, + thread_size=-1, groove=false, square=false, rectangle=0, + angle=30, taper=0, leadin=0, leadfac=1.0, test=false) +{ + // thread_size: size of thread "V" different than travel per turn (pitch). + // Default: same as pitch. + local_thread_size = thread_size == -1 ? pitch : thread_size; + local_rectangle = rectangle ? rectangle : 1; + + n_segments = segments (diameter); + h = (test && ! internal) ? 0 : (square || rectangle) ? local_thread_size*local_rectangle/2 : local_thread_size / (2 * tan(angle)); + + h_fac1 = (square || rectangle) ? 0.90 : 0.625; + + // External thread includes additional relief. + h_fac2 = (square || rectangle) ? 0.95 : 5.3/8; + + tapered_diameter = diameter - length*taper; + + difference () { + union () { + if (! groove) { + if (! test) { + metric_thread_turns (diameter, pitch, length, internal, n_starts, + local_thread_size, groove, square, rectangle, angle, + taper); + } + } + + difference () { + + // Solid center, including Dmin truncation. + if (groove) { + cylinder (r1=diameter/2, r2=tapered_diameter/2, + h=length, $fn=n_segments); + } else if (internal) { + cylinder (r1=diameter/2 - h*h_fac1, r2=tapered_diameter/2 - h*h_fac1, + h=length, $fn=n_segments); + } else { + + // External thread. + cylinder (r1=diameter/2 - h*h_fac2, r2=tapered_diameter/2 - h*h_fac2, + h=length, $fn=n_segments); + } + + if (groove) { + if (! test) { + metric_thread_turns (diameter, pitch, length, internal, n_starts, + local_thread_size, groove, square, rectangle, + angle, taper); + } + } + } + + // Internal thread lead-in: take away from external solid. + if (internal) { + + // "Negative chamfer" z=0 end if leadin is 2 or 3. + if (leadin == 2 || leadin == 3) { + cylinder (r1=diameter/2, r2=diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac, + $fn=n_segments); + } + + // "Negative chamfer" z-max end if leadin is 1 or 2. + if (leadin == 1 || leadin == 2) { + translate ([0, 0, length + 0.05 - h*h_fac1*leadfac]) { + cylinder (r1=tapered_diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac, + r2=tapered_diameter/2, + $fn=n_segments); + } + } + } + } + + if (! internal) { + + // Chamfer z=0 end if leadin is 2 or 3. + if (leadin == 2 || leadin == 3) { + difference () { + cylinder (r=diameter/2 + 1, h=h*h_fac1*leadfac, $fn=n_segments); + + cylinder (r2=diameter/2, r1=diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac, + $fn=n_segments); + } + } + + // Chamfer z-max end if leadin is 1 or 2. + if (leadin == 1 || leadin == 2) { + translate ([0, 0, length + 0.05 - h*h_fac1*leadfac]) { + difference () { + cylinder (r=diameter/2 + 1, h=h*h_fac1*leadfac, $fn=n_segments); + + cylinder (r1=tapered_diameter/2, r2=tapered_diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac, + $fn=n_segments); + } + } + } + } + } +} + + +// ---------------------------------------------------------------------------- +// Input units in inches. +// Note: units of measure in drawing are mm! +module english_thread (diameter=0.25, threads_per_inch=20, length=1, + internal=false, n_starts=1, thread_size=-1, groove=false, + square=false, rectangle=0, angle=30, taper=0, leadin=0, + leadfac=1.0, test=false) +{ + // Convert to mm. + mm_diameter = diameter*25.4; + mm_pitch = (1.0/threads_per_inch)*25.4; + mm_length = length*25.4; + + echo (str ("mm_diameter: ", mm_diameter)); + echo (str ("mm_pitch: ", mm_pitch)); + echo (str ("mm_length: ", mm_length)); + metric_thread (mm_diameter, mm_pitch, mm_length, internal, n_starts, + thread_size, groove, square, rectangle, angle, taper, leadin, + leadfac, test); +} + +// ---------------------------------------------------------------------------- +module metric_thread_turns (diameter, pitch, length, internal, n_starts, + thread_size, groove, square, rectangle, angle, + taper) +{ + // Number of turns needed. + n_turns = floor (length/pitch); + + intersection () { + + // Start one below z = 0. Gives an extra turn at each end. + for (i=[-1*n_starts : n_turns+1]) { + translate ([0, 0, i*pitch]) { + metric_thread_turn (diameter, pitch, internal, n_starts, + thread_size, groove, square, rectangle, angle, + taper, i*pitch); + } + } + + // Cut to length. + translate ([0, 0, length/2]) { + cube ([diameter*3, diameter*3, length], center=true); + } + } +} + + +// ---------------------------------------------------------------------------- +module metric_thread_turn (diameter, pitch, internal, n_starts, thread_size, + groove, square, rectangle, angle, taper, z) +{ + n_segments = segments (diameter); + fraction_circle = 1.0/n_segments; + for (i=[0 : n_segments-1]) { + rotate ([0, 0, i*360*fraction_circle]) { + translate ([0, 0, i*n_starts*pitch*fraction_circle]) { + //current_diameter = diameter - taper*(z + i*n_starts*pitch*fraction_circle); + thread_polyhedron ((diameter - taper*(z + i*n_starts*pitch*fraction_circle))/2, + pitch, internal, n_starts, thread_size, groove, + square, rectangle, angle); + } + } + } +} + + +// ---------------------------------------------------------------------------- +module thread_polyhedron (radius, pitch, internal, n_starts, thread_size, + groove, square, rectangle, angle) +{ + n_segments = segments (radius*2); + fraction_circle = 1.0/n_segments; + + local_rectangle = rectangle ? rectangle : 1; + + h = (square || rectangle) ? thread_size*local_rectangle/2 : thread_size / (2 * tan(angle)); + outer_r = radius + (internal ? h/20 : 0); // Adds internal relief. + //echo (str ("outer_r: ", outer_r)); + + // A little extra on square thread -- make sure overlaps cylinder. + h_fac1 = (square || rectangle) ? 1.1 : 0.875; + inner_r = radius - h*h_fac1; // Does NOT do Dmin_truncation - do later with + // cylinder. + + translate_y = groove ? outer_r + inner_r : 0; + reflect_x = groove ? 1 : 0; + + // Make these just slightly bigger (keep in proportion) so polyhedra will + // overlap. + x_incr_outer = (! groove ? outer_r : inner_r) * fraction_circle * 2 * PI * 1.02; + x_incr_inner = (! groove ? inner_r : outer_r) * fraction_circle * 2 * PI * 1.02; + z_incr = n_starts * pitch * fraction_circle * 1.005; + + /* + (angles x0 and x3 inner are actually 60 deg) + + /\ (x2_inner, z2_inner) [2] + / \ + (x3_inner, z3_inner) / \ + [3] \ \ + |\ \ (x2_outer, z2_outer) [6] + | \ / + | \ /| + z |[7]\/ / (x1_outer, z1_outer) [5] + | | | / + | x | |/ + | / | / (x0_outer, z0_outer) [4] + | / | / (behind: (x1_inner, z1_inner) [1] + |/ | / + y________| |/ + (r) / (x0_inner, z0_inner) [0] + + */ + + x1_outer = outer_r * fraction_circle * 2 * PI; + + z0_outer = (outer_r - inner_r) * tan(angle); + //echo (str ("z0_outer: ", z0_outer)); + + //polygon ([[inner_r, 0], [outer_r, z0_outer], + // [outer_r, 0.5*pitch], [inner_r, 0.5*pitch]]); + z1_outer = z0_outer + z_incr; + + // Give internal square threads some clearance in the z direction, too. + bottom = internal ? 0.235 : 0.25; + top = internal ? 0.765 : 0.75; + + translate ([0, translate_y, 0]) { + mirror ([reflect_x, 0, 0]) { + + if (square || rectangle) { + + // Rule for face ordering: look at polyhedron from outside: points must + // be in clockwise order. + polyhedron ( + points = [ + [-x_incr_inner/2, -inner_r, bottom*thread_size], // [0] + [x_incr_inner/2, -inner_r, bottom*thread_size + z_incr], // [1] + [x_incr_inner/2, -inner_r, top*thread_size + z_incr], // [2] + [-x_incr_inner/2, -inner_r, top*thread_size], // [3] + + [-x_incr_outer/2, -outer_r, bottom*thread_size], // [4] + [x_incr_outer/2, -outer_r, bottom*thread_size + z_incr], // [5] + [x_incr_outer/2, -outer_r, top*thread_size + z_incr], // [6] + [-x_incr_outer/2, -outer_r, top*thread_size] // [7] + ], + + faces = [ + [0, 3, 7, 4], // This-side trapezoid + + [1, 5, 6, 2], // Back-side trapezoid + + [0, 1, 2, 3], // Inner rectangle + + [4, 7, 6, 5], // Outer rectangle + + // These are not planar, so do with separate triangles. + [7, 2, 6], // Upper rectangle, bottom + [7, 3, 2], // Upper rectangle, top + + [0, 5, 1], // Lower rectangle, bottom + [0, 4, 5] // Lower rectangle, top + ] + ); + } else { + + // Rule for face ordering: look at polyhedron from outside: points must + // be in clockwise order. + polyhedron ( + points = [ + [-x_incr_inner/2, -inner_r, 0], // [0] + [x_incr_inner/2, -inner_r, z_incr], // [1] + [x_incr_inner/2, -inner_r, thread_size + z_incr], // [2] + [-x_incr_inner/2, -inner_r, thread_size], // [3] + + [-x_incr_outer/2, -outer_r, z0_outer], // [4] + [x_incr_outer/2, -outer_r, z0_outer + z_incr], // [5] + [x_incr_outer/2, -outer_r, thread_size - z0_outer + z_incr], // [6] + [-x_incr_outer/2, -outer_r, thread_size - z0_outer] // [7] + ], + + faces = [ + [0, 3, 7, 4], // This-side trapezoid + + [1, 5, 6, 2], // Back-side trapezoid + + [0, 1, 2, 3], // Inner rectangle + + [4, 7, 6, 5], // Outer rectangle + + // These are not planar, so do with separate triangles. + [7, 2, 6], // Upper rectangle, bottom + [7, 3, 2], // Upper rectangle, top + + [0, 5, 1], // Lower rectangle, bottom + [0, 4, 5] // Lower rectangle, top + ] + ); + } + } + } +} + + + diff --git a/toplevel-find b/diziet-utils/toplevel-find similarity index 100% rename from toplevel-find rename to diziet-utils/toplevel-find diff --git a/toplevel-make b/diziet-utils/toplevel-make similarity index 100% rename from toplevel-make rename to diziet-utils/toplevel-make diff --git a/diziet-utils/utils.scad b/diziet-utils/utils.scad new file mode 100644 index 0000000..e440b5f --- /dev/null +++ b/diziet-utils/utils.scad @@ -0,0 +1,51 @@ +// -*- C -*- + +// suitable for masking things within radius sqrt(2) only +module FArcSegment_mask(beta) { + for (i=[0 : 0.75 : 3]) { + rotate(i*beta/4) + polygon([[0, 0], + [1, 0], + [cos(beta/4), sin(beta/4)]]); + } +} + +module FArcSegment(xc,yc,inrad,outrad,alpha,delta) { + translate([xc,yc]) { + intersection() { + difference() { + circle(r=outrad, $fn=70); + circle(r=inrad, $fn=70); + } + rotate(alpha) scale(outrad*2) { + FArcSegment_mask(delta); + } + } + } +} + +module rectfromto(a,b) { + ab = b - a; + translate([min(a[0], b[0]), min(a[1], b[1])]) + square([abs(ab[0]), abs(ab[1])]); +} +module circleat(c,r) { translate(c) circle(r); } +module linextr(z0,z1, convexity=20) { + translate([0,0,z0]) + linear_extrude(height=z1-z0, convexity=convexity) + children(); +} + +module linextr_x_yz(x0,x1, convexity=20) { // XY turn into YZ + rotate([90,0,0]) + rotate([0,90,0]) + linextr(x0,x1, convexity=convexity) + children(); +} + +module linextr_y_xz(y0,y1, convexity=20) { // XY turn into YZ + rotate([0,0,180]) + rotate([90,0,0]) + linextr(y0,y1, convexity=convexity) + children(); +} diff --git a/doveclip.scad b/doveclip.scad new file mode 100644 index 0000000..0536c1c --- /dev/null +++ b/doveclip.scad @@ -0,0 +1,161 @@ +// -*- C -*- +// +// doveclip.scad +// +// 3D design for a fastener suitable for Reprapss +// Copyright 2012,2016 Ian Jackson +// +// This work is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This work is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this work. If not, see . + + +toothheight = 1.2; +webthick = 1.8; +height = 7; +pinlengthfact = 1.2; +nomrad = height/2 - toothheight; +minrad = nomrad - 0.75; +maxrad = nomrad + 0.25; +jawthick = 1.5; + +webgap = 0.4; +basepinclear = 1.0; + +toothgap = webthick + webgap*2; +basethick = toothheight; + +module DoveClipPin(h=height) { + pinh = h * pinlengthfact; + pinheight = nomrad*2 + jawthick*2; + translate([0,0, pinheight/2]) intersection(){ + union(){ + for (m=[0,1]) { + mirror([0,0,m]) translate([0,0,pinheight/2]) rotate([90,0,0]) + cylinder($fn=20, r1=minrad, r2=maxrad, h=pinh); + } + translate([-webthick/2, -pinh, -pinheight/2-1]) + cube([webthick, pinh, pinheight+2]); + } + translate([-maxrad-1, -pinh-1, -pinheight/2]) + cube([maxrad*2+2, pinh+2, pinheight]); + } +} + +function DoveClip_depth() = + basethick + nomrad*2 + toothheight; + +module DoveClipEnd(baseextend=1, h=7) { + cubex = nomrad*2 + jawthick*2; + cube0y = -basethick-nomrad*2-toothheight; + centrey = -basethick-nomrad; + difference(){ + translate([-cubex/2, cube0y, 0]) + cube([cubex, -cube0y+baseextend, h]); + translate([0, centrey, -1]) + cylinder($fn=20, r=nomrad, h=h+2); + translate([-toothgap/2, cube0y-1, -1]) + cube([toothgap, toothheight+nomrad+1, h+2]); + } +} + +module DoveClipPair(baseextend=1, h=7) { + delta = nomrad*2 + jawthick*2 + toothgap; + for (x=[-delta/2,delta/2]) + translate([x,0,0]) + DoveClipEnd(baseextend=baseextend, h=h); +} + +module DoveClipPairBase(baseextend=0.1, h=7, count=2) { + delta = nomrad*2 + jawthick; + intrude = nomrad + basethick - basepinclear; + for (i=[0:count-1]) { + translate([(i - (count-1)/2) * delta, 0, 0]) + DoveClipEnd(baseextend=baseextend, h=h); + } + translate([-delta * count/2, -intrude, 0]) + cube([delta * count, intrude+0.1, h]); +} + +module DoveClipPairSane(baseextend=0, h=7, count=2) { + rotate([0,0,90]) + translate([0, DoveClip_depth(), 0]) + DoveClipPairBase(baseextend=baseextend, h=h, count=count); +} + +function DoveClipPairSane_width(count=2) = + 2 * (nomrad + jawthick + ((nomrad*2 + jawthick) * (count-1)/2)); + +module ExtenderPillar(length, height, + pillarw=3.5, pillarslope=0.75, webthick=1) { + pillarr=pillarw/2; + d = 0.25; + + intangle = atan(pillarslope); + polyjx = sin(intangle)*pillarr; + polyjy = cos(intangle)*pillarr; + polyex = -tan(intangle+90)*pillarr; + webmidy = height/2+d; + + for (xmir=[0,1]) + translate([0,0,height/2]) mirror([0,0,xmir]) + translate([0,0,-height/2]) { + intersection() { + translate([-1, -pillarr-5, 0.01]) + cube([length+2, height+pillarr*2+10, height]); + mirror([1,0,0]) rotate([0,-90,0]) + linear_extrude(height=length) union(){ + circle(r=pillarr, $fn=20); + polygon([[polyjx,polyjy-0.1], [polyex, 0], + [polyjx,-(polyjy-0.1)]]); + polygon([[0,-webthick/2], [0,webthick/2], + [webmidy,webthick/2], [webmidy,-webthick/2]]); + } + } + } +} + +module ExtenderPillars(length, width, height, + pillarw=3.5, pillarslope=0.75, webthick=1, + baseweb=false, basewebthick=1) { + pilesw = width - pillarw; + + for (ymir=[0,1]) mirror([0,ymir,0]) translate([0,-pilesw/2,0]) { + ExtenderPillar(length, height, pillarw, pillarslope, webthick); + } + + if (baseweb) { + translate([0, -pilesw/2, 0]) + cube([length, pilesw, basewebthick]); + } +} + +module DoveClipExtender(length, ha=7, hb=7, counta=2, countb=2, + pillarw=3.5, pillarslope=0.75, webthick=1) { + + mirror([1,0,0]) + DoveClipPairSane(h=ha, count=counta); + translate([length,0,0]) + DoveClipPairSane(h=hb, count=countb); + pillarlen = length - DoveClip_depth() * 2 + 2; + + pilesw = min(DoveClipPairSane_width(counta), DoveClipPairSane_width(countb)) + - 0.5; + pilesh = min(ha, hb) - 0.5; + + translate([DoveClip_depth() - 1, 0, 0]) + ExtenderPillars(pillarlen, pilesw, pilesh, + pillarw=pillarw, pillarslope=pillarslope, + webthick=webthick); +} + +//DoveClipExtender(length=100, ha=16, hb=20, counta=3, countb=4); diff --git a/dovecliptest.scad b/dovecliptest.scad new file mode 100644 index 0000000..e7e075c --- /dev/null +++ b/dovecliptest.scad @@ -0,0 +1,15 @@ +// -*- C -*- + +include + +for (y=[0,-15]) translate([0,y,0]) { + DoveClipPair(); + + translate([-8,0,0]) + cube([16,5,7]); + translate([15,0,0]) + DoveClipPin(); +} + +translate([0,20,0]) + DoveClipPairBase(); diff --git a/dungeonquest-cone.scad b/dungeonquest-cone.scad new file mode 100644 index 0000000..b56ff91 --- /dev/null +++ b/dungeonquest-cone.scad @@ -0,0 +1,19 @@ +// -*- C -*- + +basesz=12; +height=14.7; +topsz=0.5; +dsz=0; + +module One(){ ////toplevel + cylinder(h=height, r1=basesz/2-dsz, r2=topsz/2-dsz, $fn=50); +} + +module Four(){ ////toplevel + for (x=[0,1]) { + for (y=[0,1]) { + translate([x*(basesz+3), y*(basesz+3), 0]) + One(); + } + } +} diff --git a/dungeonquest-cone.slic3r b/dungeonquest-cone.slic3r new file mode 100644 index 0000000..b7d882f --- /dev/null +++ b/dungeonquest-cone.slic3r @@ -0,0 +1,2 @@ +bed_temperature = 80 +temperature = 205 diff --git a/earring-stand.scad b/earring-stand.scad new file mode 100644 index 0000000..85c28a8 --- /dev/null +++ b/earring-stand.scad @@ -0,0 +1,345 @@ +// -*- C -*- + +include + +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 + +eclip_inner_strt = eclip_outer_strt + eclip_inner_xstrt; + +r0 = eclip_inner_rad; +r1 = r0 + eclip_gap_rad; +r2 = r1 + eclip_prong_th; +r2e = r1 + eclip_base_epsilon; + +ppxl = -(r0 / sqrt(2)) + (eclip_inner_strt / sqrt(2)); + +rgap = eclip_gap_rad; + +eclip_base_offset = r1; +eclip_wall_offset = -ppxl; + +eclip_ra_offset = r2 - 0.1; + +eclip_recept_height = r2; + +eclip_rhs_offset = ppxl + rgap + eclip_prong_th; +// does not include main_th + +$fn=70; + +module EclipLPlanCore(alpha){ + FArcSegment(0,0, r1,r2, + 180-eclip_ult_angle, eclip_ult_angle-alpha +1); + + difference(){ + hull(){ + intersection(){ + circle(r2); + rotate(-alpha) mirror([1,1]) square([r2e, 50]); + } + rotate(-alpha) mirror([1,1]) square([r2e, r2]); + } + circle(r1); + } +} + +module EclipRPlan(alpha, main_th){ + intersection(){ + rotate(alpha) + translate([ppxl + main_th + rgap, -r2*2]) + square([eclip_prong_th, r2*(2 + 1/sqrt(2))]); + translate([-r2, -r2e]) + square([r2*3, eclip_base_epsilon + r2*4]); + } +} + +module EclipLPlan(alpha){ + rotate(alpha) EclipLPlanCore(alpha); +} + +module EclipPPlan(main_th){ + intersection(){ + hull(){ + circle(r0); + rotate(90-eclip_ult_angle) square([r0,r0]); + } + translate([-(r0+.1), -(r0+.1)]) + square([(r0+.1) + main_th + ppxl, r2*2]); + } + translate([ppxl, 0]) square([main_th, r2]); +} + +module TestBase(){ ////toplevel + translate([0,0, eclip_base_offset]){ + for (i=[1 : 2: test_eclips-2]) { + translate([0, i*eclip_each_every]) + rotate([90,0,0]) + linear_extrude(height=eclip_each_len) + EclipLPlan(test_alpha); + } + for (j=[0 : 2: test_eclips-1]) { + translate([0, j*eclip_each_every]) + rotate([90,0,0]) + linear_extrude(height=eclip_each_len) + EclipRPlan(test_alpha, test_main_th); + } + } + translate([-r2, -eclip_each_len, -test_base_th]){ + difference(){ + cube([r2*2, + test_len, + test_base_th]); + mirror([0,0,1]) Commitid_BestCount_M([r2*2, test_len]); + } + } +} + +module TestProtr(){ ////toplevel + difference(){ + translate([0,0, test_main_th - eclip_wall_offset]) + rotate([0,90,0]) + linear_extrude(height=test_len) + EclipPPlan(test_main_th); + mirror([0,0,1]) Commitid_BestCount_M([test_len, r2]); + } +} + +module TestRAProtr(){ ////toplevel + rotate([-90,0,0]) TestProtr(); + mirror([1,0,0]) + translate([-test_len, + -r2, + -(eclip_ra_offset + test_base_th)]) + cube([test_len, + r2*2, + test_base_th]); +} + +module TestPlanDemo(){ + color("red") EclipLPlan(test_alpha); + color("blue") rotate(test_alpha) EclipPPlan(test_main_th); + color("green") EclipRPlan(test_alpha, test_main_th); +} + +beta = asin(front_setback / front_height); + +uf = [-sin(beta), cos(beta)]; +ur = [ -uf[1], uf[0]]; + +pp = [0, 0]; +pq = pp + uf*front_height + ur*eclip_ra_offset; +pr = [ pq[0] - eclip_base_offset - eclip_wall_offset, + 0 ]; + +echo("uf ur P Q R", uf, ur, pp, pq, pr); + +module Sketch(){ + polygon([pq, pp, pr]); +} + +thicks = [ base_thick, front_thick, back_thick ]; + +module Joins(alpha, objnum, objnum_f, objnum_m) { + pitch = (front_width - eclip_each_len) / (num_eclips-1); + + thm = thicks[objnum_m]; + stride = (front_width - eclip_each_len) / (num_eclips-1); + + if (objnum==objnum_f) { + for (i=[ 1 : 2 : num_eclips-1 ]) { + translate([0, i*stride + eclip_each_len, 0]) { + rotate([90,0,0]) + linear_extrude(height=eclip_each_len) + EclipLPlan(alpha); + } + } + for (i=[ 0 : 2 : num_eclips-1 ]) { + translate([0, i*stride + eclip_each_len, 0]) { + rotate([90,0,0]) + linear_extrude(height=eclip_each_len) + EclipRPlan(alpha, thm); + } + } + } + if (objnum==objnum_m) + mirror([0,1,0]) + rotate([90,0,0]) + linear_extrude(height=front_width) + rotate(alpha) + EclipPPlan(thm); +} + +function r3(pc) = [ pc[0], 0, pc[1] ]; + +module ObjectJoins(objnum){ + translate(r3(pp)) Joins(beta, objnum, 0,1); + translate(r3(pr)) mirror([1,0,0]) Joins(0, objnum, 0,2); + translate(r3(pq)) rotate([0,90,0]) mirror([1,0,0]) Joins(-beta, objnum, 2,1); +} + +module Base(){ + xmin = pr[0] - eclip_rhs_offset - thicks[2]; + xmax = pp[0] + eclip_rhs_offset + thicks[1] + + eclip_prong_th * (1/cos(beta) - 1) + + eclip_base_offset * tan(beta); + intersection(){ + ObjectJoins(0); + translate([xmin, + -1, + -50]) + cube([xmax - xmin, + front_width + 2, + 300]); + } + translate([xmin, + 0, + -eclip_base_offset - thicks[0]]){ + difference(){ + cube([xmax - xmin, + front_width, + thicks[0]]); + translate([xmax-xmin, front_width]/2) + rotate([0,0,270]) + Commitid_Full16_M(); + } + } +} + +module FrontPattern(){ + totalh = front_height - eclip_wall_offset + thicks[1]; + + ystride = front_hex_stride; + xstride = front_hex_stride * cos(30) * 2; + + difference(){ + square([front_width, totalh]); + translate([ front_surround_lr, + eclip_recept_height ]) + square([ front_width - front_surround_lr*2, + totalh - eclip_recept_height*2 + ]); + } + + difference(){ + square([front_width, totalh]); + for (xi=[ -5 : 5 ]) { + translate([front_width/2 + + xi * xstride, + 0]) { + for (yi=[ 0 : 10 ]) { + //echo(yi); + translate([0, yi * ystride + + front_hex_dia*front_hex_y_fudge]) { + for (dv=[ [0,0], + [-xstride/2, -ystride/2] + ]) + translate(dv) + circle(r= front_hex_dia/2, $fn=6); + } + } + } + } + } +} + +module Front(){ + ObjectJoins(1); + rotate([0, 90-beta, 0]) + translate([0, 0, ppxl]) + rotate([0,0,90]) { + linear_extrude(height=thicks[1]) + FrontPattern(); + } +} + +module Back(){ + ObjectJoins(2); + + zmin = pr[1]; + zmax = pq[1] + eclip_prong_th; + height = zmax - zmin; + + translate([pr[0] + eclip_wall_offset - thicks[2], + 0, 0]) + rotate([0,90,0]) + rotate([0,0,90]) { + difference(){ + cube([front_width, + height, + thicks[2]]); + translate([back_pillarw, + eclip_recept_height, + -10]) + cube([front_width - back_pillarw*2, + height - eclip_recept_height*2 - eclip_prong_th, + 20]); + } + } +} + +module BackPrint(){ ////toplevel + rotate([0,-90,0]) Back(); +} + +module FrontPrint(){ ////toplevel + rotate([0, 90+beta, 0]) Front(); +} + +module BasePrint(){ ////toplevel + Base(); +} + +module Demo(){ ////toplevel + color("red") Base(); + color("blue") Front(); + color("black") Back(); +} + +//PlanDemo(); +//TestBase(); +//TestProtr(); +//TestRAProtr(); +//Sketch(); +//Demo(); +//BackPrint(); +//FrontPrint(); +//BasePrint(); diff --git a/electron-token.scad.pl b/electron-token.scad.pl new file mode 100755 index 0000000..dc24c5a --- /dev/null +++ b/electron-token.scad.pl @@ -0,0 +1,140 @@ +#!/usr/bin/perl -w + +use strict; +use Math::Trig; +use Math::Vector::Real; +use IO::File; +use Data::Dumper; +use constant tau => pi*2; + +my $ellipse = 60 / 2; +my $circle = 3.5 / 2; +my $xscale = 33 / 100; +my $N = 180; # around ellipse +my $M = 80; # around each circle + +my $NMdiv = $ENV{'ELECTRONTOKEN_COARSE'} || 1; + +$M /= $NMdiv; +$N /= $NMdiv; + +print <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 () { print }; + +STDOUT->error and die $!; +STDOUT->flush or die $!; + +__DATA__ +module Token(){ + difference(){ + for (rot=[ 0,120,240 ]) + rotate([0,0, rot]) + translate([0,0,torusyup]) + Torusy(); + translate([-200,-200,-50]) + cube([400,400,50]); + } +} +Token(); diff --git a/fairphone-battery-case.scad b/fairphone-battery-case.scad new file mode 100644 index 0000000..557271d --- /dev/null +++ b/fairphone-battery-case.scad @@ -0,0 +1,190 @@ +// -*- C -*- + +include + +mainwall_th = 3.0; +smallwall_th = 2.0; + +seal_th = 0.3 + 0.6 + 0.6 - 0.4 - 0.4 + 0.2; // total gap for seal etc. +behind_recess = 1.5; + +recess_gap_end = 0.4; + +lid_edge_th = 0.5; + +battery_len = 66.55 + 1.25 -.55; +battery_th = 6.55 + 0.75 - .60; +battery_wdth = 44.38 + 0.75 -.55; + +battery_base_indent = 0.94 + 0.50; +battery_base_indent_fromside_outside = 4; +battery_base_indent_fromside_inside = 10; + +handle_height = 3.5; +handle_inward = 10; +handle_len = 5; + +pushhole_ell_sz = 4.75; +pushhole_ell_th = 1.75; +pushhole_circle_dia = 4.0; + +// for testing: +//battery_len = 3; +//battery_wdth = 15; +//battery_base_indent_fromside_inside = 6; + +// calculated + +bpp0 = [0,0]; +bpp1 = bpp0 + [ 0, mainwall_th - behind_recess ]; +lppA = bpp1 + [ seal_th, -recess_gap_end ]; +lppB = lppA + [ lid_edge_th, 0 ]; +bpp2 = [ lppB[0], bpp1[1] ]; +bpp3 = [ bpp2[0] + (bpp1 - bpp0)[1], bpp0[1] ]; +bpp4 = [ bpp3[0], bpp0[1] + mainwall_th ]; +lppC = bpp3 + [ 0, -recess_gap_end ]; + +lppF = lppC + [ handle_height, 0 ]; + +s0 = battery_wdth/2; +s0i = s0 - battery_th/2; +s1 = s0 + smallwall_th; + +l1 = s1 - handle_inward; +l0 = l1 - handle_len; + +echo( + bpp0, + bpp1, + bpp2, + bpp3, + bpp4, + bpp5, + bpp6, + bpp7, + bpp8 +); + +echo( + lppA, + lppB, + lppC, + lppD, + lppE +); + +bpp8 = bpp0 + [ -battery_len,0 ]; +bpp5 = [ bpp8[0] - smallwall_th, bpp4[1] ]; +bpp9 = [ bpp0[0], bpp0[1] - battery_th/2 - 1.0 ]; +bpp7 = [ bpp8[0], bpp9[1] ]; +bpp6 = [ bpp5[0], bpp9[1] ]; +lppE = [ lppA[0], bpp9[1] ]; +lppD = [ lppC[0], bpp9[1] ]; + +module BaseHalfPlan(indent=0){ + polygon([ bpp0, + bpp1, + bpp2, + bpp3, + bpp4, + bpp5, + bpp6, + bpp7 + indent * [1,0], + bpp8 + indent * [1,0] + ]); +} + +module SideHalfPlan(){ + polygon([ bpp5, + bpp6, + bpp9, + bpp1 + ]); +} + +module LidHalfPlan(){ + polygon([ lppA, + lppE, + lppD, + lppC, + lppB + ]); +} + +module HandleHalfPlan(){ + translate(lppE) + square(lppF - lppE); +} + +module ExtrudePlan(from,to){ + rotate([0,-90,0]) + for (mj=[0,1]) { + mirror([0,0,mj]) translate([0,0,from]){ + linear_extrude(height= to-from, convexity=5){ + for (mi=[0,1]) { + mirror([0,mi]) + translate([0, battery_th/2]) + children(0); + } + } + } + } +} + +module PushHolePlan(){ ////toplevel + translate(-(pushhole_ell_th * 0.10 + + pushhole_ell_sz * 0.10) * [1,1]) { + for (r=[0,90]) + rotate(r) + translate(-pushhole_ell_th * 0.5 * [1,1]) + square([ pushhole_ell_sz, pushhole_ell_th ]); + } + circle(pushhole_circle_dia/2, $fn=40); +} + +module PlanDemo(){ ////toplevel + color("blue") BaseHalfPlan(); + color("red") LidHalfPlan(); + translate([0,0,-1]) color("lightblue") SideHalfPlan(); +} + +module Base(){ ////toplevel + difference(){ + ExtrudePlan(0,s1) BaseHalfPlan(); + linextr(-(10+battery_len), battery_len+10, convexity=5) PushHolePlan(); + } + difference(){ + union(){ + ExtrudePlan(s0i, s1) SideHalfPlan(); + ExtrudePlan(s0 - battery_base_indent_fromside_inside, + s0 - battery_base_indent_fromside_outside + ) BaseHalfPlan(indent = battery_base_indent); + } + for (m=[0,1]) + mirror([m,0,0]) + translate([s0i, 0, bpp7[0] - 0.1]) + cylinder(r= battery_th/2, h=100, $fs=0.5); + } +} + +module BaseHalfTest(){ ////toplevel + intersection(){ + Base(); + translate([-100,0,-100]) + cube([200,200,200]); + } +} + +module Lid(){ ////toplevel + ExtrudePlan(0,s1) LidHalfPlan(); + ExtrudePlan(l0,l1) HandleHalfPlan(); +} + +module Demo(){ ////toplevel + %Base(); + Lid(); +} + +//PlanDemo(); +//Demo(); +//Base(); diff --git a/fairphone-case-mounted.scad b/fairphone-case-mounted.scad new file mode 100644 index 0000000..8999348 --- /dev/null +++ b/fairphone-case-mounted.scad @@ -0,0 +1,14 @@ +// -*- C -*- + +include + +module CaseMounted(){ ////toplevel + Case(); + translate([ phone_width/2, + -phone_height/2, epp3[1] - case_th_bottom ]) + Mount(); +} + +//// toplevels-from: +include +$suppress_forward_holes = true; diff --git a/fairphone-case.scad b/fairphone-case.scad new file mode 100644 index 0000000..2998b54 --- /dev/null +++ b/fairphone-case.scad @@ -0,0 +1,1755 @@ +// -*- C -*- + +// Hard case for Fairphone 2 +// +// Copyright 2018 Ian Jackson. There is NO WARRANTY. +// See below for full licensing and disclaimer. +// +// Instructions +// +// 1. You will want to git clone this repository. +// +// 2. Decide about the notification LED aperture. See the variable +// led_window_style, below. The default here is "ad-hoc +// multi-colour", which can produces a translucent (clear-ish) +// window set into the lid, even on a single-nozzle printer. +// See "Ad-hoc multi-colour", below. +// +// 3. use "make" to generate the necessary files: +// +// make -j8 fairphone-case.auto.scads `for f in \ +// HingePrint \ +// LidWindowPrint \ +// LidPrint \ +// OneKeeperPrint \ +// Case \ +// ; do echo fairphone-case,$f.auto.stl; done` +// +// 4. Print them. Case and OneKeeperPrint should probably be +// the same colour. +// +// For Lid and LidWindowPrint, if you are doing ad-hoc +// multi-colour: +// i. Set up for clear filament +// ii. Print LidWindowPrint. Wait for it to finish. +// It won't take long. As soon as it finishes, tell +// your printer to warm up (so that in fact it does +// not cool down). +// iii. Leaving the output so far on the printbed, reload +// your printer with the main lid colour. +// iv. Print LidPrint. You can let this go unattended. +// +// 5. Assemble the hinge. You will need 4x M2 12mm machine screws +// and 8x M2 full nuts. +// +// Make sure you get the hinge the right way round. If you're not +// sure, run +// openscad fairphone-case,DemoHinge.auto.scad +// to see an assembly diagram. +// +// The nuts recess into the hinge. You will want very fine +// pliers. As you screw each screw in, add the second nut when +// the screw thread emerges from the first - this will be a +// locknut. Screw each screw to an appropriate tightness for the +// hinge stiffness. You want the lid-side hinge to be stiffer as +// that makes closing the case work better. +// +// When you have the stiffness right, tighten the locknuts onto +// each first nut. +// +// 6. In use: +// +// - To put the phone in, drop its RH side into the RH side of +// the case. Then feed the keeper through the small hole. +// Feed it right through. +// +// - The optional prop can be used to prop the phone up (in +// portrait orientation only right now). See +// openscad fairphone-case,DemoPropAngles.auto.scad +// +// Ad-hoc multi-colour +// +// This file is set up to let you make a translucent window using a +// single-extruder printer, using a "two print run" technique. This +// works well with our Lulzbot TAZ 5 and Aleph Objects' version of +// Cura. If you are using a different printer, you may need to +// adjust the parameters or try a different technique. In particular, +// initial_layer_thick +// set so that the window is one layer thick +// initial_layer_width +// set so that the slicer draws a rectangle around the whole +// object, rather than putting a "skirt" or anything inside +// +// If you have a dual-extruder printer, you can set led_window_style +// to 2 and do a single print of LidPrint and LidWindowPrint. +// +// Alternatively you can set it to 1 (just a hole) or 0 (no hole). +// +// Thanks to Clare Boothby for the ad-hoc multi-colour technique (and +// the parameters for our Lulzbot TAZ 5 and Aleph Objects's Cura). +// +// Other phones +// +// It might well be possible to adapt this file for other phones. +// If you do, let me know how you get on. +// +// +// AUTHORSHIP, COPYRIGHT, LICENCE, AND LACK OF WARRANTY +// +// Copyright (C)2018 Ian Jackson. +// +// This program for generating a 3D model is free software: you can +// redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program. If not, see +// . +// +// 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 +include + +phone = [ 75.0, 145.0 ]; + +prop_buildout_less = 3; + +prop_angles = [ 15, 30, 45, 60 ]; + +bumper = [ 0.250, -0.025 ]; +// ^ One side. Overall size is increased by twice this. +// If no bumpers, is the gap around the phone. + +enable_support = 1; + +led_window_style = 3; +// 0: no window +// 1: simply an opening +// 2: opening with separate cover model, for printing in clear (two colour) +// 3: like 2 but one-layer window for ad-hoc multi-colour + +initial_layer_thick = 0.400; // ^ needed for mode 3 only +initial_layer_width = 0.750; // ^ needed for mode 3 only +multicolour_gap = 0.15; // each side + +phone_cnr_rad = 6.0; +phone_rim_depth = 0.80; // includes allowance for a screen protector + +button_cutout_depth = 9; + +phone_edge_thick = 9.0; +phone_total_thick = 12.0; +phone_backside_slope_inner = 1.5; // larger means shallower +phone_backside_slope_outer = 1.0; // larger means shallower + +camera_pos_tl = [ 6.450, 12.750 ]; // measured from tl corner +camera_pos_br = [ 22.300, 37.600 ]; // tl/br as seen from back + +jack_pos = [ 13.83, 8.485 ]; +jack_dia = 10.64 + .5; // some jack I had lying around + +led_pos = [ 13.98, 10.00 ]; +led_aperture = 9; +led_window_ledge = 0.75; // each side + +noisecancelmic_pos = [ 19.54, 7.37 ]; // from rhs +noisecancelmic_dia = 4.00; + +//fingerpushhole_dias = [ 15, 18 ]; +fingerpushhole_dias = []; + +lanyard_half_dia = 1.15; +lanyard_entry_rel_breadth = 2; +lanyard_channel_len = 15; +rearspeaker_pos_bl = [ 12.64, 18.72 ]; +rearspeaker_size = [ 3.76, 7.36 ]; + +microusb_above = 3.27 - 0.25; +microusb_below = 0.0; +microusb_width = 16.12 + 1.25; + +case_th_bottom = 2.5; +case_th_lid = 3.0; +case_th_side = 2.3; +case_th_lip = 1.2; + +lid_screen_gap_extra = .66; + +case_struts_count = 6; +case_struts_solid_below = 1.00; +case_struts_solid_above = 0.75; +case_struts_width = 0.10; + +keeper_th_z = 0.75; +keeper_th_x = 0.75; +keeper_inner_width = 2.75; +keeper_inner_height = 2.75; +keeper_slant_slope = 2; // larger means steeper + +keeper_gap_z_top = 0.25; +keeper_gap_z_bot = 0.75; +keeper_gap_x = 0.25; +keeper_gap_x_holes = 0.75; + +keeper_side = 0; // 0 = lhs; 1 = rhs + +case_lip = 1.25; + +lid_gap_x = 0.25; +lid_gap_z = 0.25; +lid_lip = 1.75; +lid_edgepart_width = 5.0; +lid_buttoncover_thick = 1.3; +lid_buttoncover_reinf = 0.65; + +foldover_gap = 0.50; +foldover_lever_gap = 0.50; + +// properties of the hinge fasteners +hingescrew_shaft_dia = 2.0 + 0.25; // M2 x 12mm machine screw +hingescrew_shaft_len = 12; +hingescrew_fasteners_extra_thick = 0.40; +// ^ amount of thread protruding if everything was completely nominal +// and we are using two nuts +hingescrew_nut_access_dia = 4.72 + 0.50; +// ^ washer is 4.72 dia +// also, want to get pliers or tiny spanner in to do up locknut +hingescrew_nut_across = 3.92 + 0.25; // incl. slop around recess slop +hingescrew_nut_thick = 1.93; +hingescrew_head_th = 1.38 + 0.75; +hingescrew_head_dia = 3.92; + +hingescrew_nut_recess_portion = 2/3; // portion of nut in recess + +lever_cover_th = 0.75; +hingemount_th = 2.5; +hingemount_wd = 4.8725; + +$fa = 5; +$fs = 0.1; + +button_l_fudge = 4.4; +buttonishleg_default_l_is_fudge = 10; + +hinge_base_slope = 1.5; // bigger is steeper + +strut_min_at_end = 1.5; + +hinge_x_gap = 0.125; +hinge_x_postscrew_gap = 0.75; +hinge_x_arms_gap = 0.35; +hinge_r_arms_gap = 0.55; + +rearspeaker_gap = [ 2.0, 2.0 ]; // each side + +thumbrecess_depth = 1.3; +thumbrecess_width = 16.5; +thumbrecess_topcurve_r = 5.0; + +prop_recess_under = 0.50; +prop_recess_slop = 0.200; // each side +prop_end_dia = 0.5; +prop_main_th = 3; +prop_taper_len = 6; +prop_main_width = 4; +prop_side_gap = 0.75; // each side +prop_lidrecess_behind = 0.75; +prop_caserecess_behind = 0.75; +prop_caserecess_taper = 0.45; // one side only +prop_prop_gap = 0.5; +prop_prong_heel_slope = 0.5; + +lid_fold_clearance_antislop = 0.5; + +$button_leg_only = false; +$suppress_forward_holes = false; + +// ---------- calculated ---------- + +phone_width = (phone + bumper*2)[0]; +phone_height = (phone + bumper*2)[1]; + +inside_br = [phone_width, -phone_height]; + +prop_prong_h = prop_main_th; + +//echo(camera_pos_tl + bumper, +// camera_pos_br + bumper); + +// ----- could be changed ----- +lid_buttoncover_gap = lid_gap_x; +lid_buttoncover_overlap = case_th_lip + keeper_gap_z_top; + +phone_backside_slope_thick = phone_total_thick - phone_edge_thick; + +//prop_lidrecess_depth = case_th_lid - prop_recess_under; + +//prop_nose_len = case_th_lid - prop_recess_under; +//prop_recess_slope = tan(prop_max_angle); // bigger means steeper +//prop_recess_width = prop_main_th / cos(prop_max_angle) + prop_backfwd_gap; + + +//lid_lip_overlap_width xxx bad name = ; +//lid_lip_inner_slope = [ 5, 5 ]; // xxx + +epp0 = [0,0]; +epp1 = [0, -phone_edge_thick]; +epp2i = epp1 + phone_backside_slope_thick * [ phone_backside_slope_inner, -1 ]; +epp2o = epp1 + phone_backside_slope_thick * [ phone_backside_slope_outer, -1 ]; +epp3 = epp2i + [10, 0]; +epp5 = epp0 + [0,1] * (keeper_th_z + keeper_gap_z_top + case_lip); +epp4 = epp5 + [-1,0] * case_th_side; + +kppe = [0,0]; +kppd = kppe + [1,0] * keeper_inner_width; +kppc = kppd + [0,1] * keeper_th_z; +kppb = [ kppe[0] - keeper_th_x, kppc[1] ]; +kppf = kppe - [0,1] * keeper_inner_height; +kppa = [ kppb[0], kppf[1] ]; + +lpp10 = [ epp5[0] + lid_gap_x, kppc[1] + lid_gap_z ]; +lpp11 = [ lpp10[0], epp5[1] + lid_gap_z ]; + +lpp14 = lpp10 + [1,0] * max(keeper_inner_width, lid_edgepart_width); +// exact x posn not very important; must extend past end of keeper + +lpp15 = [ lpp14[0], + epp0[1] - phone_rim_depth + 1/2.5 * case_th_lid + + lid_screen_gap_extra ]; +// ^ beam theory says to maximise force before contact, +// the gap below the `beam' (the lid) must be 1/3 +// the thickness (ie the lid thickness) if the beam +// is solid, or 1/2 if it has a top and bottom only. +// ours is mostly solid. + +lp_r12 = max(case_th_lid - (lpp11[1] - lpp15[1]), + case_th_lip); + +lpp12 = [ epp4[0] + lp_r12, lpp11[1] ]; +lpp13 = [ lpp12[0], lpp12[1] + lp_r12 ]; + +case_bottom_z = epp2o[1] - case_th_bottom; + +// button profile +bppM = epp4 + [0,5]; +bppN = [ bppM[0] + lid_buttoncover_thick, bppM[1] ]; +bppR = [ bppN[0] + lid_buttoncover_gap, -button_cutout_depth ]; +bppS = [ epp1[0], bppR[1] ]; +bppQ = [ bppM[0], bppR[1] - lid_buttoncover_overlap ]; +bppP = bppQ + [0,1] * lid_buttoncover_gap; +bppO = [ bppN[0], bppP[1] ]; +bppL = lpp10 + [5,0]; +bppK = [ bppL[0], bppN[1] ]; +bppJ = [ bppN[0], bppL[1] ]; +bppU = [ bppJ[0], lpp12[1] ]; +bppV = lpp11; +bppW = lpp10; + +echo("BUTTON COVER TH", bppO[0] - bppP[0]); + +// notification led aperture + +nla_r0 = led_aperture/2; +nla_r1 = nla_r0 + led_window_ledge; +nla_r2 = nla_r1 + multicolour_gap; +nla_t = + led_window_style >= 3 ? initial_layer_thick : + led_window_style >= 2 ? led_window_ledge : 0; + + +// hinge plan +hp_rn = hingescrew_nut_access_dia/2; +hp_r2_min = hp_rn + lever_cover_th; +hp_rs = hingescrew_shaft_dia/2; +hp_r1_min = hp_rs + hingemount_th; + +hp_r1 = max(hp_r1_min, hp_r2_min); +hp_r2 = hp_r1; + +hppU = lpp13; +hppS = epp2o + [0,-1] * case_th_bottom; +hp_k = 0.5 * (hppU[1] - hppS[1] + foldover_gap); + +hppM = [ epp4[0] - foldover_lever_gap - hp_r2, + 0.5 * (hppU + hppS)[1] ]; +hppT = [ hppM[0], hppU[1] - hp_r1 ]; +hppB = hppT + [0,-1] * hp_k; + +hppE_y = epp2o[1] - case_th_bottom + hp_r1; +hppE_x = hppB[0] + (hppB[1] - hppE_y) * hinge_base_slope; +hppE = [ hppE_x, hppE_y ]; + +// hinge elevation x coords + +hex20 = max(epp2o[0], + phone_cnr_rad, + kppd[0] + hingescrew_head_th + keeper_gap_x_holes); +hex21 = hex20 + hingemount_wd; +hex22 = hex21 + hinge_x_gap; +hex27 = hex20 + hingescrew_shaft_len; +hex24 = hex27 + hinge_x_postscrew_gap; +hex23 = hex27 - (hingescrew_nut_thick*2 + + hingescrew_fasteners_extra_thick); +hex26 = hex23 + hingescrew_nut_thick * 2/3; + +echo(hex20, hex21, hex22, hex23, hex24); +// 6, 10.8725, 10.9975, 13.74, 18.75 +module chk(act,exp) { + if (abs(act-exp) > 1e-9) echo("WRONG", act, exp); + else echo("ok", act); +} +chk(hex20, 6); +chk(hex21, 10.8725); +chk(hex22, 10.9975); +chk(hex23, 13.74); +chk(hex24, 18.75); + +lid_fold_clearance_skew = + (lpp10[1] - hppB[1]) / + (lpp10[0] - hppB[0]); + +echo("SK",lid_fold_clearance_skew); + +// thumb recess (used to be "catch" hence cpp* + +cppA = epp4 + [thumbrecess_depth, 0]; +cppB = [ cppA[0], epp1[1] ]; + +// lanyard + +ly_r = lanyard_half_dia / 2; +ly_rc = ly_r * 2; + +ly_theta = -atan2vector(epp2i - epp1); +ly_o = epp2i + 3 * ly_r * unitvector2d(epp1 - epp2i); + +max_case_bottom_edge_thickness = + case_th_bottom + + sin(ly_theta) * (epp2i-epp2o)[0]; + +ly_q_z = -(ly_rc + ly_r); +ly_re = max_case_bottom_edge_thickness - (-ly_q_z); + +ly_oec_y = lanyard_entry_rel_breadth * ly_r; + +// prop recess in case + +prop_x_pos = phone_width/2; + +prop_recess_hw = 0.5 * prop_main_width + prop_side_gap; + +prc_r1 = prop_end_dia/2; +prc_r3 = prc_r1 + prop_recess_slop; + +prcp2 = [ epp4[0] + prop_buildout_less, + case_bottom_z ]; + +prop_caserecess_buildout_r = -1; // prcp2[0] - epp2o[0]; + +prcp1 = [ epp2o[0] + prc_r3 + prop_caserecess_behind, + epp2i[1] - prc_r3 - prop_recess_under]; + +// prop recess in lid + +prl_r10 = prop_end_dia/2; +prl_r10o = prl_r10 + prop_recess_slop; + +prlp10 = lpp10 + [1,1] * prl_r10o + + [1,0] * prop_lidrecess_behind + + [0,1] * prop_recess_under; + +// prop + +$prpp10 = [0,0]; +$prpp11 = [0, prop_taper_len]; + +$prp_r10 = prl_r10; + +// ---------- modules ---------- + +module AdhocMultiprintFrame(phase, z0, zs) { + // from z0 to z0 + zs*layer + extra = phase * (initial_layer_width + multicolour_gap) + 5; + xextra = extra + -epp4[0]; + xrange = [ 0, phone_width ] + [-1,+1] * xextra; + yextra = extra + -epp4[0]; + yrange = [ -phone_height + +hppB[0] - hp_r2, 0 ] + [-1,+1] * yextra; + p0 = [ xrange[0], yrange[0] ]; + p1 = [ xrange[1], yrange[1] ]; + echo(p0, p1); + translate([0,0, z0]) + mirror([0,0, zs<0 ? 1 : 0]) + linear_extrude(height= initial_layer_thick) + difference(){ + rectfromto(p0 - [1,1] * initial_layer_width, + p1 + [1,1] * initial_layer_width); + rectfromto(p0, p1); + } +} + +module KeeperProfile(slant=0){ + use_e = kppe + [0,-1] * slant * keeper_inner_width / keeper_slant_slope; + polygon([use_e, kppd, kppc, kppb, kppa, kppf]); +} + +module EdgeProfile(){ + difference(){ + hull(){ + translate(epp3) square(case_th_bottom*2, center=true); + circleat(epp2o, r=case_th_bottom); + circleat(epp1, r=case_th_side); + rectfromto(epp0, epp4); + } + polygon([ epp5 + [0,10], + epp1, + epp2i, + epp3 + [10,0] ]); + } +} + +module LanyardLanyardProfile(entry=false){ + hull(){ + for (xs=[-1,+1] * (entry ? lanyard_entry_rel_breadth : 1)) + translate(xs * 0.5 * lanyard_half_dia * [1,0]) + circle(r= lanyard_half_dia/2); + } +} + +module LanyardCurveChannelProfile(){ + translate([0, -ly_r]) + LanyardLanyardProfile(); +} + +module LanyardEntryChannelProfile(){ + LanyardLanyardProfile(true); +} + +module LanyardMainChannelProfile(){ + LanyardCurveChannelProfile(); + difference(){ + square(center=true, ly_r * [6, 2]); + for (xs=[-1,+1]) + translate(ly_r * [3 * xs, -1]) + circle(r = ly_r); + } +} + +module LanyardEntryOuterProfile(){ + circleat([ly_re + ly_r, 0], ly_re); +} + +module LanyardEntry(){ + q_z = ly_q_z; + oec_y = ly_oec_y; + + d_x = -ly_rc; + + translate([d_x, 0, q_z]) { + intersection(){ + rotate([90,0,0]) + rotate_extrude(convexity=10) + rotate(90) + translate([0, -q_z]) + LanyardCurveChannelProfile(); + translate([0,-10,0]) + cube([20,20,20]); + } + } + + mirror([0,0,1]) + translate([0,0,-1]) + linear_extrude(height=20) + rotate(-90) + LanyardEntryChannelProfile(); + + translate([0, ly_r*2, 0]) + rotate([90,0,0]) + linear_extrude(height = ly_r*4){ + difference(){ + rectfromto([d_x, q_z], [ly_r, 0]); + circleat([d_x, q_z], ly_rc); + } + } + + translate([0,0,q_z]){ + for (my=[0,1]) + mirror([0,my,0]){ + translate([0, oec_y, 0]){ + difference(){ + translate(ly_re * [-1,0,-2]) + cube(ly_re * [2,1,2]); + rotate_extrude(convexity=10) + LanyardEntryOuterProfile(); + } + } + } + difference(){ + translate([-ly_re, -(oec_y + 0.01), -2*ly_re]) + cube([ly_re*2, 2*(oec_y + 0.01), 2*ly_re]); + for (mx=[0,1]) + mirror([mx,0,0]) + rotate([90,0,0]) + translate([0,0,-10]) + linear_extrude(height=20) + LanyardEntryOuterProfile(); + } + } +} + +module LanyardCutout(l){ + rotate([0,-90,0]) + linear_extrude(height=l) + rotate(-90) + LanyardMainChannelProfile(); + + for (ee=[0,1]){ + translate(ee * l * [-1,0]) + mirror([ee,0,0]) + LanyardEntry(); + } +} + +module LidEdgeProfile(){ + polygon([ lpp10, + lpp11, + lpp12, + lpp13, + lpp13 + [10, 0], + lpp15 + [10, 0], + lpp15, + lpp14, + ]); + intersection(){ + circleat(lpp12, r=lp_r12); + rectfromto( lpp12 + [-10, 0], + lpp12 + [+10, +10] ); + } +} + +module LidEdgeFoldClearanceProfile(){ + translate([-lid_fold_clearance_antislop, 0]) + polygon([ lpp10, + lpp11, + lpp11 + [-20, 0], + lpp11 + [-20, 20], + lpp11 + [+20, 20], + lpp10 + [+20, 0] ]); +} + +module ButtonCoverProfile(){ + intersection(){ + polygon(concat([ bppM, bppP, bppO, bppJ ], + (enable_support && !$button_suppress_over_keeper + ? [ bppU, bppV, bppW ] : []), + [ bppL, bppK ])); + hull(){ + EdgeProfile(); + LidEdgeProfile(); + } + } +} + +module ButtonPlan(l, deep, cut){ + epsilon = + (cut ? 0 : lid_buttoncover_gap); + + delta = + (deep ? lid_buttoncover_overlap : 0); + + C = [0,0]; // by definition + T = [ 0, epp4[1] ]; + G = T + [0,10]; + + B0 = C + [0,-1] * button_cutout_depth; + B1 = B0 + [0,1] * epsilon; + + r0 = 0.5 * (T[1] - B0[1]); + A = [ -(l + button_l_fudge)/2 + r0, 0.5 * (T[1] + B0[1]) ]; + H = A + [0,-1] * delta; + + D = A + [-2,0] * r0; + F = D + [0,10]; + + E0 = 0.5 * (D + A); + E1 = E0 + [1,0] * epsilon; + + I0 = [ E0[0], H[1] ]; + I1 = [ E1[0], H[1] ]; + + hull(){ + for (m=[0,1]) mirror([m,0]) + circleat(H, r0 - epsilon); + } + for (m=[0,1]) mirror([m,0]) { + difference(){ + polygon([ E1, + I1, + H, + B1, + G, + F, + D + ]); + circleat(D, r0 + epsilon); + } + } +} + +module ButtonCoverReinf(){ ////toplevel + minkowski(){ + rotate([90,0,0]) + linear_extrude(height=0.01) + intersection(){ + ButtonCoverProfile(); + translate([bppJ[0] + 0.1, -50]) mirror([1,0]) + square([100,100]); + } + mirror([0,0,1]) linear_extrude(height=0.01) intersection(){ + circle(r= lid_buttoncover_reinf); + translate([-20,0]) square(40, center=true); + } + } +} + +module ThumbRecessCutProfile(){ + difference(){ + polygon([ cppA + [-10,0], + cppB + [-10,0], + cppB, + cppA ]); + circleat(epp1, r=case_th_side); + } +} + +module Flip_rhs(yn=[0,1]) { + for ($rhsflip=yn) { + translate([phone_width/2, 0, 0]) + mirror([$rhsflip,0,0]) + translate([-phone_width/2, 0, 0]) + children(); + } +} + +module Flip_bot(yn=[0,1]) { + for ($botflip=yn) { + translate([0, -phone_height/2, 0]) + mirror([0, $botflip, 0]) + translate([0, phone_height/2, 0]) + children(); + } +} + +module AroundEdges(fill_zstart, fill_th, fill_downwards=0){ + // sides + Flip_rhs(){ + translate([0, -phone_cnr_rad, 0]) + rotate([90,0,0]) + linear_extrude(height = phone_height - phone_cnr_rad*2) + children(0); + } + // corners + Flip_rhs() Flip_bot() { + translate([+1,-1] * phone_cnr_rad) + intersection(){ + rotate_extrude() + intersection(){ + mirror([1,0,0]) + translate([-1,0] * phone_cnr_rad) + children(0); + rectfromto([0,-20],[10,20]); + } + translate([-10, 0, -20] + 0.01 * [+1,-1, 0] ) + cube([10,10,40]); + } + } + // top and bottom + Flip_bot(){ + translate([ phone_width - phone_cnr_rad, 0,0 ]) + rotate([90,0,-90]) + linear_extrude(height = phone_width - phone_cnr_rad*2) + children(0); + } + // fill + translate([0,0, fill_zstart]) + mirror([0,0, fill_downwards]) + linear_extrude(height = fill_th) + rectfromto([+1,-1] * phone_cnr_rad, + [phone_width, -phone_height] + [-1,+1] * phone_cnr_rad); +} + +module CaseAperture(pos, dia, $fn) { + theta = 180/$fn; + translate([ pos[0] + bumper[0], + -epp2i[0], + -pos[1] ]) + rotate([-90, theta, 0]) + cylinder(r = dia/2 / cos(theta), + h = 60); +} + +module SideButton(y, y_ref_sign, l, suppress_over_keeper=0){ + // y_ref_sign: + // +1 measured from top of actual phone to top of button + // -1 measured from bottom of actual phone to bottom of button + // 0 y is centre of button in coordinate system + $button_l= l; + $button_suppress_over_keeper= suppress_over_keeper; + eff_y = y_ref_sign > 0 ? -bumper [1] -y -l/2 : + y_ref_sign < 0 ? (-phone -bumper)[1] +y +l/2 : + y; + //echo(eff_y); + translate([0, eff_y, 0]) + children(); +} + +module LidButtonishLeg(y, y_ref_sign, l=buttonishleg_default_l_is_fudge) { + $button_leg_only = true; + SideButton(y, y_ref_sign, l) children(); +} + +module Buttons(){ + Flip_rhs(1) SideButton(15.580, +1, 8.9 ) children(); // power + Flip_rhs(1) SideButton(48.700, -1, 8.920 ) children(); // camera + Flip_rhs(0) SideButton(30.800, +1, 21.96, 1) children(); // volume + Flip_rhs( ) LidButtonishLeg(14, -1) children(); +// Flip_rhs(0) LidButtonishLeg(20, +1, 20) children(); +} + +module Struts(x_start, z_min, th){ + // if th is negative, starts at z_min and works towards -ve z + // and object should then be printed other way up + for (i= [1 : 1 : case_struts_count]) { + translate([0, + 0, + z_min]) + mirror([0,0, th<0 ? 1 : 0]) + translate([0, + -phone_height * i / (case_struts_count+1), + case_struts_solid_below]) + linear_extrude(height= abs(th) + -(case_struts_solid_below+case_struts_solid_above)) + rectfromto([ x_start, -0.5 * case_struts_width ], + [ phone_width - x_start, +0.5 * case_struts_width ]); + } +} + +module OrdinaryRearAperture(rhs,bot, pos){ + Flip_rhs(rhs) Flip_bot(bot) + linextr(-20, 20) + mirror([0,1]) + translate(pos + bumper) + children(); +} + +module MicroUSB(){ + Flip_bot(1){ + rotate([90,0,0]) + mirror([0,0,1]) + linextr(-epp2i[0], 60) + translate([0.5 * phone_width, 0, 0]) + rectfromto([-microusb_width/2, epp2i[1] + microusb_below], + [+microusb_width/2, epp0[1] + -microusb_above]); + } +} + +module OrdinaryRearApertures(){ + // rear speaker + OrdinaryRearAperture(1,1, rearspeaker_pos_bl) + rectfromto(-rearspeaker_gap, + rearspeaker_size + rearspeaker_gap); + + // finger hole to remove phone + if (len(fingerpushhole_dias)) + OrdinaryRearAperture(1,0, [ fingerpushhole_dias[0]/2 + epp2i[0], + phone[1]/2 ]) + scale(fingerpushhole_dias) + circle(r= 0.5 ); +} + +module RearCameraAperture(){ + Flip_rhs(1) + mirror([0, 0, 1]) + linear_extrude(height = 20) + mirror([0, 1, 0]) + translate(bumper) + rectfromto(camera_pos_tl, camera_pos_br); +} + +module HingeLidProfile(){ + hull(){ + circleat(hppT, hp_r1); + circleat(lpp12, lp_r12); + polygon([lpp10, + lpp13 + [2,0], + lpp12, + hppT]); + } +} + +module HingeBaseProfile(){ + difference(){ + hull(){ + circleat(hppB, hp_r1); + circleat(hppE, hp_r1); + circleat(epp2o, case_th_bottom); + circleat(hppB + [10,0], hp_r1); + } + polygon([epp5, epp1, epp2i, epp3, bppL]); + } +} + +module HingeLeverOuterProfile(){ + hull(){ + circleat(hppT, hp_r2); + circleat(hppB, hp_r2); + } +} + +module HingeLeverInnerProfile(){ + for (s = [-1,+1]) { + c = s > 0 ? hppT : hppB; + translate(c) + mirror([0,0,s>0]) + rotate(s<0 ? -40 : 0) + hull() + for (x=[-20,20]) + for (y=[0, s * 10]) + translate([x,y]) + circle(hp_rn); + } +} + +module HingeLeverNutProfile(){ + for (c= [hppB, hppT]) { + translate(c) + circle($fn=6, r= 0.5 * hingescrew_nut_across / cos(30)); + } +} + +module Flip_hinge(doflip=1){ + hinge_origin = [0, -(phone_height - hppB[0]), hppB[1]]; + translate(hinge_origin) + rotate([doflip*180,0,0]) + translate(-hinge_origin) + children(); +} + +module HingePortion(x0,x1){ + Flip_rhs() Flip_bot(1) + translate([x0,0,0]) + mirror([1,0,0]) + rotate([90,0,-90]) + linear_extrude(height=x1-x0) + children(); +} + +module ThumbRecessApply(ztop){ + width = thumbrecess_width; + w = width + thumbrecess_topcurve_r*2 + 1; + translate([phone_width/2, 0,0]){ + difference(){ + rotate([90,0,-90]) + linextr(-w/2, w/2) + children(0); + translate([0, 50, 0]) + rotate([90,0,0]) + linear_extrude(height=100){ + for (m=[0,1]) mirror([m,0,0]) { + hull(){ + translate([w/2, ztop - thumbrecess_topcurve_r]) + circle(thumbrecess_topcurve_r); + translate([w/2, -50]) + square(thumbrecess_topcurve_r*2, center=true); + } + } + } + } + } +} + +module CaseBase(){ + AroundEdges(epp3[1], case_th_bottom, 1) + EdgeProfile(); +} + +function prop_x(gamma) = hp_k / (2 * sin(gamma/2)) - hppT[0]; + +module PropProfileAssignments(gamma){ + // https://en.wikipedia.org/wiki/Solution_of_triangles#Two_sides_and_the_included_angle_given_(SAS) + x = prop_x(gamma); + p = phone_height + prlp10[0] - hppB[0]; + b = p + x; + + q = phone_height - hppT[0] - prcp1[0]; // $prpp7[0] is 0 by definition + a = q + x; + c = sqrt(a*a + b*b - 2*a*b*cos(gamma)); + $prp_alpha = acos( (b*b + c*c - a*a) / (2*b*c) ); + + $prp_theta = 90 - $prp_alpha; + beta = 180 - $prp_alpha - gamma; + psi = 90 - beta; + + //echo("abc", a,b,c); + + v1 = [ [ cos(psi), -sin(psi) ], // x + [ sin(psi), cos(psi) ] ]; // y + + $prpp7 = [0, c + (lpp13[1] - $prpp10[1] - hp_k) ]; + + $prp_r1 = prc_r1; + $prp_r11 = prop_main_th/2; + + $prpp1 = $prpp7 + [1,0] * + // this is approximate, but will do + (prop_main_th/2 + prop_prop_gap + prcp1[0] - cppA[0]); + $prpp3 = $prpp1 + + v1[0] * -$prp_r1 + + v1[1] * ((prcp2[1] - prcp1[1]) - prop_prop_gap); + $prpp12 = $prpp3 + v1[0] * + (prop_end_dia + prop_caserecess_taper * ($prpp1[1] - $prpp3[1])); + $prp_r8 = prop_main_th; + $prpp4 = [ prop_main_th/2, $prpp3[1] ]; + $prp_r5 = $prp_r8; + $prpp5 = [ $prpp12[0] - $prp_r5, + $prpp3[1] - prop_prong_h + $prp_r5 ]; + $prpp6 = $prpp4 + [0,-1] * (prop_prong_h + + prop_prong_heel_slope * ($prpp5[0] - $prpp4[0])); + $prpp8 = $prpp4 + [0,-1] * $prp_r8; + $prpp9 = $prpp8 + [-1,0] * $prp_r8; + + children(); +} + +module PropProfile(gamma, cut=0, rot=0){ + PropProfileAssignments(gamma){ + + //#circleat($prpp3,1); + //#circleat($prpp12,1); + + if (!cut) { + hull(){ + translate($prpp8) + intersection(){ + circle($prp_r8); + polygon([[-20,-0], [20,20], [0,0]]); + } + rectfromto($prpp6, $prpp9); + translate($prpp5) intersection(){ + circle($prp_r5); + polygon([[-10,-10], [0,0], [10,0]]); + } + rectfromto($prpp12 + [0,-0.1], $prpp3); + } + hull(){ + circleat($prpp1, $prp_r1); + rectfromto($prpp12 + [0,-0.1], $prpp3); + } + } + // main shaft + rotate([0,0, rot*-$prp_theta]){ + hull(){ + extra = cut ? prop_recess_slop : 0; + rectfromto($prpp6, $prpp9); + circleat($prpp11, $prp_r11 + extra); + circleat($prpp10, $prp_r10 + extra); + } + } + } +} + +module PropAggregateProfile(){ + for (angle = prop_angles) + PropProfile(angle, 0,0); +} + +module Prop(){ ////toplevel + hw = prop_main_width/2; + linextr(-hw, +hw) + PropAggregateProfile(); +} + +module Case(){ ////toplevel + difference(){ + union(){ + CaseBase(); + + // ledge (fixed keeper) + Flip_rhs(1-keeper_side) intersection(){ + rotate([90, 0, 0]) + linear_extrude(height = phone_height + phone_cnr_rad * 2) + KeeperProfile(1); + + // outline of the whole case, to stop it protruding + translate([0,0, -25]) + linear_extrude(height = 50) + hull() + Flip_bot() + circleat([+1,-1] * phone_cnr_rad, phone_cnr_rad + case_th_side/2); + } + + // hinge + HingePortion(hex20, hex21) HingeBaseProfile(); + + // buildout for prop recess + if (prop_caserecess_buildout_r > 0) Flip_rhs(1) + linextr(case_bottom_z, epp2i[1]) + hull() { + for (dxs = [-1,+1]) + circleat([ prop_x_pos + dxs * prop_caserecess_buildout_r, + -epp2o[0] ], + r = epp2o[0] - prcp2[0]); + } + } + + // slot for keeper + Flip_rhs(keeper_side) + translate([0, -phone_cnr_rad, 0]) + rotate([90, 0, 0]) + linear_extrude(height = phone_height + phone_cnr_rad * 2) + minkowski(){ + KeeperProfile(); + rectfromto([ -keeper_gap_x, -keeper_gap_z_bot ], + [ keeper_gap_x_holes, +keeper_gap_z_top ]); + } + + // front camera + RearCameraAperture(); + + // struts (invisible, because they're buried in the case) + Struts(epp2i[0], epp2i[1] - case_th_bottom, case_th_bottom); + + Buttons(){ + mirror([1,0,0]) + rotate([90,0,90]) { + if (!($button_leg_only && enable_support)) + intersection(){ + translate([0,0,-10]) + linear_extrude(height= 20) + ButtonPlan($button_l, 0,1); + if ($button_leg_only) + rotate([-90,90,0]) + translate([phone_width/2, -400, kppe[1]]) + mirror([1-abs($rhsflip - keeper_side),0,0]) + cube([400, 800, 50]); + if (enable_support && !$button_suppress_over_keeper) + rotate([-90,90,0]) + translate([-400, -400, kppd[1]]) + mirror([0,0,1]) + cube([800,800,100]); + } + translate([0,0, -bppR[0]]) + linear_extrude(height= 20) + ButtonPlan($button_l, 1,1); + } + + } + + // apertures along top edge + if (!$suppress_forward_holes) { + CaseAperture(jack_pos, jack_dia, 8); + Flip_rhs(1) + CaseAperture(noisecancelmic_pos, noisecancelmic_dia, 8); + } + + OrdinaryRearApertures(); + + MicroUSB(); + + // gaps for the lid's hinge arms + HingePortion(hex20 - hinge_x_arms_gap, + hex21 + hinge_x_arms_gap) + minkowski(){ + HingeLidProfile(); + circle(r= hinge_r_arms_gap, $fn= 8); + } + + // screw holes in the hinge arms + HingeScrews(); + + // thumb recess + ThumbRecessApply(epp4[1]) + ThumbRecessCutProfile(); + + // lanyard + Flip_bot(1) + translate([ly_o[0], -(phone_cnr_rad + ly_re), ly_o[1]]) + rotate([0, ly_theta, 0]) + rotate([0,0,90]) + LanyardCutout(lanyard_channel_len); + + // prop recess + Flip_rhs(1) + translate([prop_x_pos,0,0]) + mirror([0,1,0]) + rotate([90,0,90]) + linextr(-prop_recess_hw, +prop_recess_hw) + hull(){ + for (d=[ [0,0], [0,-1], [+1,-1/prop_caserecess_taper] ]) + circleat(prcp1 + 20*d, + prc_r3); + } + } +} + +module LidAdhocMultiprintFrame(phase){ + if (led_window_style >= 3) { + AdhocMultiprintFrame(phase, lpp13[1], -1); + } +} + +module LidAroundEdges(){ + AroundEdges(lpp15[1], lpp13[1] - lpp15[1], 0) + children(); +} + +module Lid(){ ////toplevel + skew_centre = [0, lpp11[0], lpp11[1]]; + difference(){ + union(){ + intersection(){ + LidAroundEdges() + LidEdgeProfile(); + + translate(skew_centre) + multmatrix([[ 1, 0, 0, 0 ], + [ 0, 1, -lid_fold_clearance_skew, 0 ], + [ 0, 0, 1, 0 ], + [ 0, 0, 0, 1 ]]) + translate(-skew_centre) + LidAroundEdges() + LidEdgeFoldClearanceProfile(); + } + + // button covers + Buttons(){ + intersection(){ + rotate([90,0,90]) + translate([0,0,-10]) + linear_extrude(height= 20) + ButtonPlan($button_l, 1,0); + union(){ + rotate([90,0,0]) + translate([0,0,-100]) + linear_extrude(height= 200) + ButtonCoverProfile(); + hull() + for (y= [-1,+1] * (($button_l + button_l_fudge)/2 + - lid_buttoncover_reinf)) + translate([0,y,0]) + ButtonCoverReinf(); + } + } + } + + // hinge arms + HingePortion(hex20, hex21) { + LidEdgeProfile(); + HingeLidProfile(); + } + } + Struts(lpp10[0] + strut_min_at_end, lpp13[1], -case_th_lid); + + // screw holes in the hinge arms + HingeScrews(); + + // prop recess + translate([prop_x_pos, -prlp10[0], prlp10[1]]) + mirror([0,1,0]) + rotate([90,0,90]) + linextr(-prop_recess_hw, +prop_recess_hw) + hull() + for (pa = prop_angles) + PropProfile(pa, 1,1); + + // notification led aperture + if (led_window_style) + translate([led_pos[0], -led_pos[1], lpp13[1]]) { + translate([0,0,-10]) + cylinder(r=nla_r0, h=20); + if (led_window_style >= 2) + translate([0,0, -nla_t]) + cylinder(r=nla_r2, height=20); + } + + } + + LidAdhocMultiprintFrame(1); +} + +module HingeLever(){ ////toplevel + difference() { + // outer body, positive + HingePortion(hex22, hex22 + phone_width/2) + HingeLeverOuterProfile(); + + // space for the screws + HingePortion(hex26, hex24) + HingeLeverInnerProfile(); + + // recesses for the nuts + HingePortion(hex23, hex26+1) + HingeLeverNutProfile(); + + // bores for the screws + HingeScrews(); + + // space for the charging cable + MicroUSB(); + Flip_hinge() MicroUSB(); + } +} + +module LidWindow(){ ////toplevel + translate([led_pos[0], -led_pos[1], lpp13[1]]) + mirror([0,0,1]) + cylinder(r= nla_r1, h=nla_t); + LidAdhocMultiprintFrame(0); +} + +module LidWindowPrint(){ ////toplevel + rotate([0,180,0]) + LidWindow(); +} + +module DemoLidWindowSelect(){ + translate([led_pos[0], led_pos[1], -100]) { + translate([0, -30, 0]) cube([400, 400, 200]); + } +} + +module DemoLidWindow(){ ////toplevel + %Lid(); + LidWindow(); + translate([0,40,0]){ + color("blue") intersection(){ Lid(); DemoLidWindowSelect(); } + color("red") intersection(){ LidWindow(); DemoLidWindowSelect(); } + } +} + +module HingeLeverPrint(){ ////toplevel + rotate([-90,0,0]) + translate([-phone_width/2, phone_height, 0]) + HingeLever(); +} + +module TestSelectLength(){ + translate([-30, -200, -20]) + cube([30 + 15, 250, 40]); +} + +module TestLength(){ ////toplevel + intersection(){ + Case(); + TestSelectLength(); + } +} + +module TestLengthRight(){ ////toplevel + intersection(){ + Case(); + Flip_rhs(1) + TestSelectLength(); + } +} + +module TestSelectWidth(){ + translate([-30, -(phone_height - 25), -20]) + mirror([0, 1, 0]) + cube([200, 50, 40]); +} + +module TestWidth(){ ////toplevel + intersection(){ + Case(); + TestSelectWidth(); + } +} + +module TestLidWidthPrint(){ ////toplevel + rotate([0,180.0]) intersection(){ + Lid(); + TestSelectWidth(); + } +} + +module TestSelectRearAperture(){ + minkowski(){ + union() children(); + translate([20, 0,0]) + cube([42, 2, 1], center=true); + } +} + +module TestSelectCamera(){ + minkowski(){ + TestSelectRearAperture() + RearCameraAperture(); + cube([0.1, 50, 0.1]); + } +} + +module TestSelectOrdinaryRearApertures(){ + TestSelectRearAperture() + OrdinaryRearApertures(); +} + +module TestCamera(){ ////toplevel + intersection(){ + Case(); + TestSelectCamera(); + } +} + +module TestLidByCamera(){ ////toplevel + intersection(){ + Lid(); + TestSelectCamera(); + } +} + +module TestLidByCameraPrint(){ ////toplevel + rotate([180,0,0]) TestLidByCamera(); +} + +module DemoByCamera(){ ////toplevel + color("blue") TestLidByCamera(); + color("red") TestCamera(); +} + +module OneKeeper(){ ////toplevel + translate([0, -phone_cnr_rad, 0]) + rotate([90, 0, 0]) + linear_extrude(height = phone_height - phone_cnr_rad * 2) + KeeperProfile(); +} + +module OneKeeperPrint(){ ////toplevel + rotate([0,180,0]) + OneKeeper(); +} + +module LidPrint(){ ////toplevel + rotate([0,180,0]) + Lid(); +} + +module TestSelectFrame(){ + include = [1,-1] * (epp2i[0] + 4); + + difference(){ + cube(1000, center=true); + translate([0,0, -100]) + linear_extrude(height=200) + rectfromto(include, inside_br - include); + } +} + +module TestSelectLidFrame(){ + TestSelectFrame(); + translate([led_pos[0], -led_pos[1], -50]) + cylinder(r= nla_r2+3, h=100); +} + +module TestFrameCase(){ ////toplevel + intersection(){ + Case(); + union(){ + TestSelectFrame(); + TestSelectCamera(); + TestSelectOrdinaryRearApertures(); + } + } +} + +module TestSelectTopApertures(){ + translate([-100, -35, -100]) + cube([400, 100, 200]); + LidAdhocMultiprintFrame(0); + LidAdhocMultiprintFrame(1); +} + +module TestTopApertures(){ ////toplevel + intersection(){ + Case(); + TestSelectFrame(); + TestSelectTopApertures(); + } +} + +module TestLidTopAperturesPrint(){ ////toplevel + rotate([0,180,0]) intersection(){ + Lid(); + TestSelectLidFrame(); + TestSelectTopApertures(); + } +} + +module TestLidWindowTopAperturesPrint(){ ////toplevel + rotate([0,180,0]) intersection(){ + LidWindow(); + TestSelectTopApertures(); + } +} + +module TestFrameLidPrint(){ ////toplevel + rotate([0,180,0]) intersection(){ + Lid(); + TestSelectLidFrame(); + } +} + +module ButtonPlanForDemo(z, deep, cut){ + translate([0,0,z]) + ButtonPlan(8, deep, cut); +} + +module HingeScrews(){ + Flip_rhs() Flip_bot(1){ + for (c= [ hppT, hppB ]) + translate([ hex20, + -c[0], + c[1] ]){ + rotate([0,90,0]) + translate([0,0,-.2]) + cylinder( r= hingescrew_shaft_dia/2, + h = hingescrew_shaft_len+0.2 ); + rotate([0,-90,0]) + translate([0,0,+.1]) + cylinder( r= hingescrew_head_dia/2, h = hingescrew_head_th ); + } + } +} + +module DemoPropAngleSelect(c){ + color(c) difference(){ + union(){ children(); } + translate([ prop_x_pos, -400, -200 ]) + cube([ 400,800,400 ]); + } +} + +module DemoPropAngle(ang){ + hL = [0, -(phone_height - hppT[0]), hppT[1] - hp_k*2]; + hC = [0, -(phone_height - hppB[0]), hppB[1]]; + + translate(hL) + rotate([ang/2,0,0]) + translate(-hL) + translate(hC) + rotate([ang/2,0,0]) + translate(-hC) { + DemoPropAngleSelect("red") Case(); + + color("orange") + translate([prop_x_pos, -prcp1[0], prcp1[1]]) + PropProfileAssignments(ang) { + echo($prpp1); + rotate([-$prp_theta, 0, 0]) + translate([0, $prpp1[0], -$prpp1[1]]) + rotate([90,0,-90]) + Prop(); + } + } + + translate([0,0, -hp_k*2]) + DemoPropAngleSelect("blue") + Lid(); +} + +module DemoPropAngles(){ ////toplevel + for (i=[0 : len(prop_angles)-1]) + translate(i * [0, -100, 100]) + DemoPropAngle(prop_angles[i]); +} + +module DemoHingeAngle(ang1,ang2){ + hL = [0, -(phone_height - hppT[0]), hppT[1]]; + hC = [0, -(phone_height - hppB[0]), hppB[1]]; + + translate(hL) + rotate([ang2,0,0]) + translate(-hL) + translate(hC) + rotate([ang1,0,0]) + translate(-hC) { + color("red") Lid(); + } + + color("blue") intersection(){ + Case(); + union(){ + translate([bppJ[0], -400, -200]) + mirror([1,0,0]) + cube([400, 800, 400]); + translate([10, -400, -200]) + cube([10, 800, 400]); + } + } +} + +module DemoHingeAngles(){ ////toplevel + angles = [ 0, 4, 8, 12 ]; + echo("angles",angles); + for (i=[0 : len(angles)-1]) { + translate(i * [0, 0, 30]) { + DemoHingeAngle(0,angles[i]); + translate([0, 200, 0]) + DemoHingeAngle(angles[i],0); + } + } +} + +module DemoSelectAdhocLeftRight(right=0) { + translate([phone_width/2, -400, -100]) // , -15, -100 to cross-section + mirror([1-right, 0,0]) + cube([400, 800, 200]); +} + +module DemoLeft(){ ////toplevel + color("red") intersection(){ Case(); DemoSelectAdhocLeftRight(); } + color("blue") intersection(){ Lid(); DemoSelectAdhocLeftRight(); } +} + +module DemoFrame(){ ////toplevel + color("red") TestFrameCase(); + color("blue") intersection(){ Lid(); TestSelectLidFrame(); } + color("black") HingeScrews(); + %HingeLever(); +} + +module DemoLanyardCutout(){ ////toplevel + LanyardCutout(25); +} + +module DemoHingedFrame(){ ///toplevel + color("red") TestFrameCase(); + translate([0,0, -2*hp_k]) + color("blue") intersection(){ Lid(); TestSelectLidFrame(); } + + Flip_hinge(){ + color("orange") HingeLever(); + color("black") HingeScrews(); + } +} + +module DemoHinge(){ ////toplevel + translate([ -0.5*phone_width, phone_height, hp_k*3 ]) { + DemoFrame(); + translate([0,0, -hp_k*3]) + DemoHingedFrame(); + } +} + +module DemoProfiles(){ ////toplevel + LidEdgeProfile(); + %EdgeProfile(); + KeeperProfile(); + translate([0,0,-1]) color("black") KeeperProfile(1); + translate(ly_o){ + rotate(-ly_theta){ + translate([0,0,+1]) color("purple") LanyardMainChannelProfile(); + translate([0,0,+2]) color("red") LanyardCurveChannelProfile(); + translate([0, ly_q_z]){ + translate([0,0,-1]) color("blue") LanyardEntryChannelProfile(); + translate([ly_oec_y,0,-2]) color("black") LanyardEntryOuterProfile(); + } + } + } + translate([0,0,-5]) color("white") translate(epp2i) + rotate(-ly_theta) + rectfromto([-15, 0], + [+15, -max_case_bottom_edge_thickness]); + + translate([0,20]) { + LanyardMainChannelProfile(); + translate([0,0,1]) color("purple") LanyardCurveChannelProfile(); + translate([0,0,-1]) color("red") LanyardEntryChannelProfile(); + } + + translate([20,0]) { + LidEdgeProfile(); + %EdgeProfile(); + + demopoint_QR = [ bppS[0], bppQ[1] - 0.1]; + + color("blue") ButtonCoverProfile(); + color("red") { + rectfromto(bppQ, demopoint_QR); + rectfromto(bppR, demopoint_QR); + } + } + + translate([-20,0]) { + color("black") ButtonPlanForDemo(-2, 0,1); + color("red" ) ButtonPlanForDemo(-4, 1,1); + color("blue") ButtonPlanForDemo(-6, 1,0); + } + + translate([0, -30]) { + %LidEdgeProfile(); + %EdgeProfile(); + color("blue") HingeLidProfile(); + color("red") HingeBaseProfile(); + color("black") translate([0,0,-2]) HingeLeverOuterProfile(); + } + + for (f=[0,1]) { + translate([-30, -60 + 30*f]) { + translate([0,0,-4]) EdgeProfile(); + %translate([0,0,-10]) HingeBaseProfile(); + translate([0,-2] * f * hp_k) { + translate([0,0,-4]) LidEdgeProfile(); + %translate([0,0,-10]) %HingeLidProfile(); + } + translate(+hppB) rotate([0,0,180*f]) translate(-hppB) { + translate([0,0,-2]) color("black") HingeLeverOuterProfile(); + translate([0,0,0]) color("red") difference(){ + HingeLeverOuterProfile(); + HingeLeverInnerProfile(); + } + translate([0,0,3]) color("yellow") HingeLeverNutProfile(); + } + } + } + + translate([20,-30]) { + %EdgeProfile(); + %LidEdgeProfile(); + //translate([0,0,1]) ThumbRecessCutProfile(); + translate([0,0,+1]) color("red") + difference(){ EdgeProfile(); ThumbRecessCutProfile(); } + } + + translate([40,-30]) { + difference(){ + LidEdgeProfile(); + translate(prlp10) + PropProfile(10, 1, 0); + } + translate(prlp10) + PropProfile(15, 0); + } + translate([60,-30]) { + PropAggregateProfile(); + } +} + +//EdgeProfile(); +//KeeperProfile(); +//CaseBase(); +//%Case(); +//Keeper(); +//LidEdgeProfile(); +//KeeperProfile(); +//DemoProfiles(); +//PropRecess(); diff --git a/fairphone4-case-coarse.scad b/fairphone4-case-coarse.scad new file mode 100644 index 0000000..e5ea9a1 --- /dev/null +++ b/fairphone4-case-coarse.scad @@ -0,0 +1,7 @@ +// -*- C -*- + +//// toplevels-from: +include + +$fa = 20; +$fs = 2; diff --git a/fairphone4-case-mounted.scad b/fairphone4-case-mounted.scad new file mode 100644 index 0000000..fecff5a --- /dev/null +++ b/fairphone4-case-mounted.scad @@ -0,0 +1,15 @@ +// -*- C -*- + +include + +module CaseMounted(){ ////toplevel + Case(); + translate([ phone_width/2, + -phone_height/2, epp3[1] - case_th_bottom ]) + Mount(); +} + +//// toplevels-from: +include +$suppress_forward_holes = true; +$suppress_hinge = true; diff --git a/fairphone4-case-tripod.scad b/fairphone4-case-tripod.scad new file mode 100644 index 0000000..b244673 --- /dev/null +++ b/fairphone4-case-tripod.scad @@ -0,0 +1,37 @@ +// -*- C -*- + +include + +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 +$suppress_hinge = true; diff --git a/fairphone4-case.scad b/fairphone4-case.scad new file mode 100644 index 0000000..0a4b63d --- /dev/null +++ b/fairphone4-case.scad @@ -0,0 +1,1801 @@ +// -*- C -*- + +// Hard case for Fairphone 2 +// +// Copyright 2018 Ian Jackson. There is NO WARRANTY. +// See below for full licensing and disclaimer. +// +// Instructions +// +// 1. You will want to git clone this repository. +// +// 2. +// +// 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 +// . +// +// 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 +include + +phone = [ 75.86, 162.0 ]; + +prop_buildout_less = 3; + +prop_angles = [ 15, 30, 45, 60 ]; + +bumper = [ 0.250, -0.025 ]; +// ^ One side. Overall size is increased by twice this. +// If no bumpers, is the gap around the phone. + +enable_support = 1; + +led_window_style = 0; +// 0: no window +// 1: simply an opening +// 2: opening with separate cover model, for printing in clear (two colour) +// 3: like 2 but one-layer window for ad-hoc multi-colour + +initial_layer_thick = 0.400; // ^ needed for mode 3 only +initial_layer_width = 0.750; // ^ needed for mode 3 only +multicolour_gap = 0.15; // each side + +phone_cnr_rad = 7.0; // actuall 8.mumble, but smaller is fine +phone_rim_depth = 0.01; // includes allowance for a screen protector + +button_cutout_depth = 9; + +phone_edge_thick = 11.25; + +camera_pos_tl = [ 5.600, 5.750 ]; // from tl corner (as seen from back) +camera_edge_rad = 9.750 + 0.700; +camera_sz = 32.920 + .750 + 1.000; + +// this is disabled, FP4 doesn't have one +jack_pos = [ 13.83, 8.485 ]; +jack_dia = 10.64 + .5; // some jack I had lying around + +// this led stuff, is irrelevant, we have disabled it as it doesn't have one +led_pos = []; // [ 13.98, 10.00 ]; +led_aperture = 9; +led_window_ledge = 0.75; // each side + +noisecancelmic_pos = [ 15.08 + .720, 4.35 ]; // from rhs, from top edge +noisecancelmic_dia = 4.00; + +mainmic_pos = [ 21.0, 4.65 ]; // from lhs, from top edge +mainmic_dia = 4.00; + +lhshole_pos = [ phone[1]/2 + 0.40, 4.35 ]; + +fingerpushhole_dias = []; +//fingerpushhole_dias = [ 15, 18 ]; // this is for testing + +lanyard_half_dia = 1.15; +lanyard_entry_rel_breadth = 2; +lanyard_channel_len = 8; +//rearspeaker_pos_bl = [ 12.64, 18.72 ]; +//rearspeaker_size = [ 3.76, 7.36 ]; + +bottomspeaker_size = [ 11.35, 1.90 ] + [1,1] * 0.5; +bottomspeaker_pos = [ 17.55, 5.17 ]; // from rhs, from top + +microusb_above = 1.64 - 0.25; +microusb_below = 2.42; +microusb_width = 12.16 + 2.0 + 1.25; + +case_th_bottom = 2.5; +case_th_lid = 3.0; +case_th_side = 2.6; +case_th_lip = 1.2; + +lid_screen_gap_extra = .66; + +case_struts_count = 6; +case_struts_solid_below = 1.00; +case_struts_solid_above = 0.75; +case_struts_width = 0.10; + +keeper_th_z = 0.75; +keeper_th_x = 0.75; +keeper_inner_width = 2.75; +keeper_inner_height = 2.75; +keeper_slant_slope = 2; // larger means steeper + +keeper_gap_z_top = 0.25; +keeper_gap_z_bot = 0.75; +keeper_gap_x = 0.25; +keeper_gap_x_holes = 0.75; +keeper_fatter = 0.45; +keeper_fatter_hole = 1.20; +keeper_stubbier = 0.0; + +keeper_side = 0; // 0 = lhs; 1 = rhs + +case_lip = 1.25; + +lid_gap_x = 0.25; +lid_gap_z = 0.25; +lid_lip = 1.75; +lid_edgepart_width = 5.0; +lid_buttoncover_thick = 1.3; +lid_buttoncover_reinf = 0.95; + +foldover_gap = 0.50; +foldover_lever_gap = 0.50; + +// properties of the hinge fasteners +hingescrew_shaft_dia = 1.600 + 0.45; // beading wire +hingescrew_shaft_len = 10; +hingescrew_fasteners_extra_thick = 0.40; +// ^ amount of thread protruding if everything was completely nominal +// and we are using two nuts +hingescrew_nut_access_dia = 4.72 + 0.50; +// ^ washer is 4.72 dia +// also, want to get pliers or tiny spanner in to do up locknut +hingescrew_nut_across = 3.92 + 0.25; // incl. slop around recess slop +hingescrew_nut_thick = 1.93; +hingescrew_head_th = 1.38 + 0.75; +hingescrew_head_dia = 3.92; + +hingescrew_nut_recess_portion = 2/3; // portion of nut in recess + +lever_cover_th = 0.75; +hingemount_th = 2.5; +hingemount_wd = 4.8725; + +$fa = 5; +$fs = 0.1; + +button_l_fudge = 4.4; +buttonishleg_default_l_is_fudge = 10; + +hinge_base_slope = 1.5; // bigger is steeper + +strut_min_at_end = 1.5; + +hinge_x_gap = 0.125; +hinge_x_postscrew_gap = 0.75; +hinge_x_arms_gap = 0.35; +hinge_r_arms_gap = 0.55; +hinge_over_nut_plate = -0.50; // bodge apropos slope + +// there isn't one of these, speaker is by hinge +// rearspeaker_gap = [ 2.0, 2.0 ]; // each side + +thumbrecess_depth = 1.3; +thumbrecess_width = 16.5; +thumbrecess_topcurve_r = 5.0; + +prop_recess_under = 0.50; +prop_recess_slop = 0.200; // each side +prop_end_dia = 0.5; +prop_main_th = 3; +prop_taper_len = 6; +prop_main_width = 4; +prop_side_gap = 0.75; // each side +prop_lidrecess_behind = 0.75; +prop_caserecess_behind = 0.75; +prop_caserecess_taper = 0.45; // one side only +prop_prop_gap = 0.5; +prop_prong_heel_slope = 0.5; + +lid_fold_clearance_antislop = 0.5; + +$button_leg_only = false; +$suppress_forward_holes = false; +$suppress_hinge = false; + +// ---------- calculated ---------- + +phone_total_thick = phone_edge_thick; + +phone_width = (phone + bumper*2)[0]; +phone_height = (phone + bumper*2)[1]; + +inside_br = [phone_width, -phone_height]; + +prop_prong_h = prop_main_th; + +//echo(camera_pos_tl + bumper, +// camera_pos_br + bumper); + +// ----- could be changed ----- +lid_buttoncover_gap = lid_gap_x; +lid_buttoncover_overlap = case_th_lip + keeper_gap_z_top; + +//prop_lidrecess_depth = case_th_lid - prop_recess_under; + +//prop_nose_len = case_th_lid - prop_recess_under; +//prop_recess_slope = tan(prop_max_angle); // bigger means steeper +//prop_recess_width = prop_main_th / cos(prop_max_angle) + prop_backfwd_gap; + + +epp0 = [0,0]; +epp1 = [0, -phone_edge_thick]; +epp2i = epp1; // conflated for FP4 +epp2o = epp2i; +epp3 = epp2i + [10, 0]; +epp5 = epp0 + [0,1] * (keeper_th_z + keeper_gap_z_top + case_lip); +epp4 = epp5 + [-1,0] * case_th_side; + +kppe = [0,0]; +kppd = kppe + [1,0] * keeper_inner_width; +kppc = kppd + [0,1] * keeper_th_z; +kppb = [ kppe[0] - keeper_th_x, kppc[1] ]; +kppf = kppe - [0,1] * keeper_inner_height; +kppa = [ kppb[0], kppf[1] ]; + +lpp10 = [ epp5[0] + lid_gap_x, kppc[1] + lid_gap_z ]; +lpp11 = [ lpp10[0], epp5[1] + lid_gap_z ]; + +lpp14 = lpp10 + [1,0] * max(keeper_inner_width, lid_edgepart_width); +// exact x posn not very important; must extend past end of keeper + +lpp15 = [ lpp14[0], + epp0[1] - phone_rim_depth + 1/2.5 * case_th_lid + + lid_screen_gap_extra ]; +// ^ beam theory says to maximise force before contact, +// the gap below the `beam' (the lid) must be 1/3 +// the thickness (ie the lid thickness) if the beam +// is solid, or 1/2 if it has a top and bottom only. +// ours is mostly solid. + +lp_r12 = max(case_th_lid - (lpp11[1] - lpp15[1]), + case_th_lip); + +lpp12 = [ epp4[0] + lp_r12, lpp11[1] ]; +lpp13 = [ lpp12[0], lpp12[1] + lp_r12 ]; + +case_bottom_z = epp2o[1] - case_th_bottom; + +// button profile +bppM = epp4 + [0,5]; +bppN = [ bppM[0] + lid_buttoncover_thick, bppM[1] ]; +bppR = [ bppN[0] + lid_buttoncover_gap, -button_cutout_depth ]; +bppS = [ epp1[0], bppR[1] ]; +bppQ = [ bppM[0], bppR[1] - lid_buttoncover_overlap ]; +bppP = bppQ + [0,1] * lid_buttoncover_gap; +bppO = [ bppN[0], bppP[1] ]; +bppL = lpp10 + [5,0]; +bppK = [ bppL[0], bppN[1] ]; +bppJ = [ bppN[0], bppL[1] ]; +bppU = [ bppJ[0], lpp12[1] ]; +bppV = lpp11; +bppW = lpp10; + +echo("BUTTON COVER TH", bppO[0] - bppP[0]); + +// notification led aperture + +nla_r0 = led_aperture/2; +nla_r1 = nla_r0 + led_window_ledge; +nla_r2 = nla_r1 + multicolour_gap; +nla_t = + led_window_style >= 3 ? initial_layer_thick : + led_window_style >= 2 ? led_window_ledge : 0; + + +// hinge plan +hp_rn = hingescrew_nut_access_dia/2; +hp_r2_min = hp_rn + lever_cover_th; +hp_rs = hingescrew_shaft_dia/2; +hp_r1_min = hp_rs + hingemount_th; + +hp_r1 = max(hp_r1_min, hp_r2_min); +hp_r2 = hp_r1; + +hppU = lpp13; +hppS = epp2o + [0,-1] * case_th_bottom; +hp_k = 0.5 * (hppU[1] - hppS[1] + foldover_gap); + +hppM = [ epp4[0] - foldover_lever_gap - hp_r2, + 0.5 * (hppU + hppS)[1] ]; +hppT = [ hppM[0], hppU[1] - hp_r1 ]; +hppB = hppT + [0,-1] * hp_k; + +hppE_y = epp2o[1] - case_th_bottom + hp_r1; +hppE_x = hppB[0] + (hppB[1] - hppE_y) * hinge_base_slope; +hppE = [ hppE_x, hppE_y ]; + +// hinge elevation x coords + +hex20 = max(epp2o[0], + phone_cnr_rad, + kppd[0] + hingescrew_head_th + keeper_gap_x_holes); +hex21 = hex20 + hingemount_wd; +hex22 = hex21 + hinge_x_gap; +hex27 = hex20 + hingescrew_shaft_len; +hex24 = hex27 + hinge_x_postscrew_gap; +hex23 = hex27 - (hingescrew_nut_thick*2 + + hingescrew_fasteners_extra_thick); +hex26 = hex23 + hingescrew_nut_thick * 2/3; + +//echo(hex20, hex21, hex22, hex23, hex24); +//// 6, 10.8725, 10.9975, 13.74, 18.75 +//module chk(act,exp) { +// if (abs(act-exp) > 1e-9) echo("WRONG", act, exp); +// else echo("ok", act); +//} +//chk(hex20, 6); +//chk(hex21, 10.8725); +//chk(hex22, 10.9975); +//chk(hex23, 13.74); +//chk(hex24, 18.75); + +lid_fold_clearance_skew = + (lpp10[1] - hppB[1]) / + (lpp10[0] - hppB[0]); + +echo("SK",lid_fold_clearance_skew); + +// thumb recess (used to be "catch" hence cpp* + +cppA = epp4 + [thumbrecess_depth, 0]; +cppB = [ cppA[0], epp1[1] ]; + +// lanyard + +ly_r = lanyard_half_dia / 2; +ly_rc = ly_r * 2; + +ly_theta = 90; +ly_o = epp2i + 3 * ly_r * [0,1]; + +max_case_bottom_edge_thickness = + case_th_bottom; + +ly_q_z = -(ly_rc + ly_r); +ly_re = max_case_bottom_edge_thickness - (-ly_q_z); + +ly_oec_y = lanyard_entry_rel_breadth * ly_r; + +// prop recess in case + +prop_x_pos = phone_width/2; + +prop_recess_hw = 0.5 * prop_main_width + prop_side_gap; + +prc_r1 = prop_end_dia/2; +prc_r3 = prc_r1 + prop_recess_slop; + +prcp2 = [ epp4[0] + prop_buildout_less, + case_bottom_z ]; + +prop_caserecess_buildout_r = -1; // prcp2[0] - epp2o[0]; + +prcp1 = [ epp2o[0] + prc_r3 + prop_caserecess_behind, + epp2i[1] - prc_r3 - prop_recess_under]; + +// prop recess in lid + +prl_r10 = prop_end_dia/2; +prl_r10o = prl_r10 + prop_recess_slop; + +prlp10 = lpp10 + [1,1] * prl_r10o + + [1,0] * prop_lidrecess_behind + + [0,1] * prop_recess_under; + +// prop + +$prpp10 = [0,0]; +$prpp11 = [0, prop_taper_len]; + +$prp_r10 = prl_r10; + +// ---------- modules ---------- + +module AdhocMultiprintFrame(phase, z0, zs) { + // from z0 to z0 + zs*layer + extra = phase * (initial_layer_width + multicolour_gap) + 5; + xextra = extra + -epp4[0]; + xrange = [ 0, phone_width ] + [-1,+1] * xextra; + yextra = extra + -epp4[0]; + yrange = [ -phone_height + +hppB[0] - hp_r2, 0 ] + [-1,+1] * yextra; + p0 = [ xrange[0], yrange[0] ]; + p1 = [ xrange[1], yrange[1] ]; + echo(p0, p1); + translate([0,0, z0]) + mirror([0,0, zs<0 ? 1 : 0]) + linear_extrude(height= initial_layer_thick) + difference(){ + rectfromto(p0 - [1,1] * initial_layer_width, + p1 + [1,1] * initial_layer_width); + rectfromto(p0, p1); + } +} + +module KeeperProfile(fatter=0, slant=0, stubbier=0){ + use_e = kppe + [0,-1] * slant * keeper_inner_width / keeper_slant_slope; + polygon([use_e + [+1,-1] * fatter, + kppd + [ 0,-1] * fatter - stubbier * [1,0], + kppc - stubbier * [1,0], + kppb, + kppa + stubbier * [0,1], + kppf + [+1, 0] * fatter + stubbier * [0,1] + ]); +} + +module EdgeProfile(){ + difference(){ + hull(){ + translate(epp3) square(case_th_bottom*2, center=true); + circleat(epp2o, r=case_th_bottom); + circleat(epp1, r=case_th_side); + rectfromto(epp0, epp4); + } + polygon([ epp5 + [0,10], + epp1, + epp3 + [10,0] ]); + } +} + +module LanyardLanyardProfile(entry=false){ + hull(){ + for (xs=[-1,+1] * (entry ? lanyard_entry_rel_breadth : 1)) + translate(xs * 0.5 * lanyard_half_dia * [1,0]) + circle(r= lanyard_half_dia/2); + } +} + +module LanyardCurveChannelProfile(){ + translate([0, -ly_r]) + LanyardLanyardProfile(); +} + +module LanyardEntryChannelProfile(){ + LanyardLanyardProfile(true); +} + +module LanyardMainChannelProfile(){ + LanyardCurveChannelProfile(); + difference(){ + square(center=true, ly_r * [6, 2]); + for (xs=[-1,+1]) + translate(ly_r * [3 * xs, -1]) + circle(r = ly_r); + } +} + +module LanyardEntryOuterProfile(){ + circleat([ly_re + ly_r, 0], ly_re); +} + +module LanyardEntry(){ + q_z = ly_q_z; + oec_y = ly_oec_y; + + d_x = -ly_rc; + + translate([d_x, 0, q_z]) { + intersection(){ + rotate([90,0,0]) + rotate_extrude(convexity=10) + rotate(90) + translate([0, -q_z]) + LanyardCurveChannelProfile(); + translate([0,-10,0]) + cube([20,20,20]); + } + } + + mirror([0,0,1]) + translate([0,0,-1]) + linear_extrude(height=20) + rotate(-90) + LanyardEntryChannelProfile(); + + translate([0, ly_r*2, 0]) + rotate([90,0,0]) + linear_extrude(height = ly_r*4){ + difference(){ + rectfromto([d_x, q_z], [ly_r, 0]); + circleat([d_x, q_z], ly_rc); + } + } + + translate([0,0,q_z]){ + for (my=[0,1]) + mirror([0,my,0]){ + translate([0, oec_y, 0]){ + difference(){ + translate(ly_re * [-1,0,-2]) + cube(ly_re * [2,1,2]); + rotate_extrude(convexity=10) + LanyardEntryOuterProfile(); + } + } + } + difference(){ + translate([-ly_re, -(oec_y + 0.01), -2*ly_re]) + cube([ly_re*2, 2*(oec_y + 0.01), 2*ly_re]); + for (mx=[0,1]) + mirror([mx,0,0]) + rotate([90,0,0]) + translate([0,0,-10]) + linear_extrude(height=20) + LanyardEntryOuterProfile(); + } + } +} + +module LanyardCutout(l){ + rotate([0,-90,0]) + linear_extrude(height=l) + rotate(-90) + LanyardMainChannelProfile(); + + for (ee=[0,1]){ + translate(ee * l * [-1,0]) + mirror([ee,0,0]) + LanyardEntry(); + } +} + +module LidEdgeProfile(){ + polygon([ lpp10, + lpp11, + lpp12, + lpp13, + lpp13 + [10, 0], + lpp15 + [10, 0], + lpp15, + lpp14, + ]); + intersection(){ + circleat(lpp12, r=lp_r12); + rectfromto( lpp12 + [-10, 0], + lpp12 + [+10, +10] ); + } +} + +module LidEdgeFoldClearanceProfile(){ + translate([-lid_fold_clearance_antislop, 0]) + polygon([ lpp10, + lpp11, + lpp11 + [-20, 0], + lpp11 + [-20, 20], + lpp11 + [+20, 20], + lpp10 + [+20, 0] ]); +} + +module ButtonCoverProfile(){ + intersection(){ + polygon(concat([ bppM, bppP, bppO, bppJ ], + (enable_support && !$button_suppress_over_keeper + ? [ bppU, bppV, bppW ] : []), + [ bppL, bppK ])); + hull(){ + EdgeProfile(); + LidEdgeProfile(); + } + } +} + +module ButtonPlan(l, deep, cut){ + epsilon = + (cut ? 0 : lid_buttoncover_gap); + + delta = + (deep ? lid_buttoncover_overlap : 0); + + C = [0,0]; // by definition + T = [ 0, epp4[1] ]; + G = T + [0,10]; + + B0 = C + [0,-1] * button_cutout_depth; + B1 = B0 + [0,1] * epsilon; + + r0 = 0.5 * (T[1] - B0[1]); + A = [ -(l + button_l_fudge)/2 + r0, 0.5 * (T[1] + B0[1]) ]; + H = A + [0,-1] * delta; + + D = A + [-2,0] * r0; + F = D + [0,10]; + + E0 = 0.5 * (D + A); + E1 = E0 + [1,0] * epsilon; + + I0 = [ E0[0], H[1] ]; + I1 = [ E1[0], H[1] ]; + + hull(){ + for (m=[0,1]) mirror([m,0]) + circleat(H, r0 - epsilon); + } + for (m=[0,1]) mirror([m,0]) { + difference(){ + polygon([ E1, + I1, + H, + B1, + G, + F, + D + ]); + circleat(D, r0 + epsilon); + } + } +} + +module ButtonCoverReinf(){ ////toplevel + minkowski(){ + rotate([90,0,0]) + linear_extrude(height=0.01) + intersection(){ + ButtonCoverProfile(); + translate([bppJ[0] + 0.1, -50]) mirror([1,0]) + square([100,100]); + } + mirror([0,0,1]) linear_extrude(height=0.01) intersection(){ + circle(r= lid_buttoncover_reinf); + translate([-20,0]) square(40, center=true); + } + } +} + +module ThumbRecessCutProfile(){ + difference(){ + polygon([ cppA + [-10,0], + cppB + [-10,0], + cppB, + cppA ]); + circleat(epp1, r=case_th_side); + } +} + +module Flip_rhs(yn=[0,1]) { + for ($rhsflip=yn) { + translate([phone_width/2, 0, 0]) + mirror([$rhsflip,0,0]) + translate([-phone_width/2, 0, 0]) + children(); + } +} + +module Flip_bot(yn=[0,1]) { + for ($botflip=yn) { + translate([0, -phone_height/2, 0]) + mirror([0, $botflip, 0]) + translate([0, phone_height/2, 0]) + children(); + } +} + +module AroundEdges(fill_zstart, fill_th, fill_downwards=0){ + // sides + Flip_rhs(){ + translate([0, -phone_cnr_rad, 0]) + rotate([90,0,0]) + linear_extrude(height = phone_height - phone_cnr_rad*2) + children(0); + } + // corners + Flip_rhs() Flip_bot() { + translate([+1,-1] * phone_cnr_rad) + intersection(){ + rotate_extrude() + intersection(){ + mirror([1,0,0]) + translate([-1,0] * phone_cnr_rad) + children(0); + rectfromto([0,-20],[10,20]); + } + translate([-10, 0, -20] + 0.01 * [+1,-1, 0] ) + cube([10,10,40]); + } + } + // top and bottom + Flip_bot(){ + translate([ phone_width - phone_cnr_rad, 0,0 ]) + rotate([90,0,-90]) + linear_extrude(height = phone_width - phone_cnr_rad*2) + children(0); + } + // fill + translate([0,0, fill_zstart]) + mirror([0,0, fill_downwards]) + linear_extrude(height = fill_th) + rectfromto([+1,-1] * phone_cnr_rad, + [phone_width, -phone_height] + [-1,+1] * phone_cnr_rad); +} + +module CaseAperture(pos, dia, $fn, topbottom=0) { + theta = 180/$fn; + translate([ bumper[0], + -epp2i[0], + 0 ]) + rotate([0,0, 90*topbottom]) + translate([ pos[0] * (topbottom>0 ? -1 : +1), 0, -pos[1] ]) + rotate([-90, theta, 0]) + cylinder(r = dia/2 / cos(theta), + h = 60); +} + +module SideButton(y, y_ref_sign, l, suppress_over_keeper=0){ + // y_ref_sign: + // +1 measured from top of actual phone to top of button + // -1 measured from bottom of actual phone to bottom of button + // 0 y is centre of button in coordinate system + $button_l= l; + $button_suppress_over_keeper= suppress_over_keeper; + eff_y = y_ref_sign > 0 ? -bumper [1] -y -l/2 : + y_ref_sign < 0 ? (-phone -bumper)[1] +y +l/2 : + y; + //echo(eff_y); + translate([0, eff_y, 0]) + children(); +} + +module LidButtonishLeg(y, y_ref_sign, l=buttonishleg_default_l_is_fudge) { + $button_leg_only = true; + SideButton(y, y_ref_sign, l) children(); +} + +module Buttons(){ + Flip_rhs(1) SideButton(30.320, +1, 22.960 ) children(); // volume + Flip_rhs(1) SideButton(64.220, +1, 14.500 ) children(); // power + Flip_rhs(1) LidButtonishLeg(14, -1) children(); + Flip_rhs(0) LidButtonishLeg(21, -1) children(); + Flip_rhs(0) LidButtonishLeg(38, +1) children(); + Flip_rhs(0) LidButtonishLeg(14, +1) children(); +} + +module Struts(x_start, z_min, th){ + // if th is negative, starts at z_min and works towards -ve z + // and object should then be printed other way up + for (i= [1 : 1 : case_struts_count]) { + translate([0, + 0, + z_min]) + mirror([0,0, th<0 ? 1 : 0]) + translate([0, + -phone_height * i / (case_struts_count+1), + case_struts_solid_below]) + linear_extrude(height= abs(th) + -(case_struts_solid_below+case_struts_solid_above)) + rectfromto([ x_start, -0.5 * case_struts_width ], + [ phone_width - x_start, +0.5 * case_struts_width ]); + } +} + +module OrdinaryRearAperture(rhs,bot, pos){ + Flip_rhs(rhs) Flip_bot(bot) + linextr(-20, 20) + mirror([0,1]) + translate(pos + bumper) + children(); +} + +module MicroUSBEtc(){ + Flip_bot(1){ + rotate([90,0,0]) + mirror([0,0,1]) + linextr(-epp2i[0], 60) + translate([0.5 * phone_width, 0, 0]) + rectfromto([-microusb_width/2, epp2i[1] + microusb_below], + [+microusb_width/2, epp0[1] + -microusb_above]); + } +} + +module OrdinaryBottomEdgeApertures(){ + Flip_bot(1) + CaseAperture(mainmic_pos, mainmic_dia, 8); + + Flip_bot(1) Flip_rhs(1) { + linextr_y_xz(-epp2i[0], 60) + hull() + for (x= [-1,+1]) { + translate([ -bottomspeaker_pos[0], -bottomspeaker_pos[1] ] + + [ 0.5 * x * bottomspeaker_size[0] - bottomspeaker_size[1], + 0 ]) + rotate(360/16) + circle(r = bottomspeaker_size[1], $fn = 8); + } + } +} + +module OrdinaryRearApertures(){ + // rear speaker + // OrdinaryRearAperture(1,1, rearspeaker_pos_bl) + // rectfromto(-rearspeaker_gap, + // rearspeaker_size + rearspeaker_gap); +} + +module NotInTestFrameRearApertures(){ + // finger hole to remove phone + if (len(fingerpushhole_dias)) + OrdinaryRearAperture(0,0, [ fingerpushhole_dias[0] + epp2i[0], + phone[1]/2 ]) + scale(fingerpushhole_dias) + circle(r= 0.5 ); +} + +module RearCameraAperture(){ + Flip_rhs(1) + mirror([0, 0, 1]) + translate([0,0,0]) + hull() // there is some kind of bug if hull() is done in 2D here! + linear_extrude(height = 20) + mirror([0, 1, 0]) + translate(bumper) + translate(camera_pos_tl) + for (xy = [ [0,0], [0,1], [1,0] ]) { + translate( + camera_edge_rad * [1,1] + + xy * (camera_sz - camera_edge_rad * 2) + ) + circle(r = camera_edge_rad); + } +} + +module HingeLidProfile(){ + hull(){ + circleat(hppT, hp_r1); + circleat(lpp12, lp_r12); + polygon([lpp10, + lpp13 + [2,0], + lpp12, + hppT]); + } +} + +module HingeBaseProfile(){ + difference(){ + hull(){ + circleat(hppB, hp_r1); + circleat(hppE, hp_r1); + circleat(epp2o, case_th_bottom); + circleat(hppB + [10,0], hp_r1); + } + polygon([epp5, epp1, epp3, bppL]); + } +} + +module HingeLeverOuterProfile(){ + hull(){ + circleat(hppT, hp_r2); + circleat(hppB, hp_r2); + } +} + +module HingeLeverInnerProfile(){ + for (s = [-1,+1]) { + c = s > 0 ? hppT : hppB; + translate(c) + mirror([0,0, s>0 ? 1 : 0]) + rotate(s<0 ? -40 : 0) + hull() + for (x=[-20,20]) + for (y=[0, s * 10]) + translate([x,y]) + circle(hp_rn); + } +} + +module HingeLeverNutProfile(){ + for (c= [hppB, hppT]) { + translate(c) + circle($fn=6, r= 0.5 * hingescrew_nut_across / cos(30)); + } +} + +module Flip_hinge(doflip=1){ + hinge_origin = [0, -(phone_height - hppB[0]), hppB[1]]; + translate(hinge_origin) + rotate([doflip*180,0,0]) + translate(-hinge_origin) + children(); +} + +module HingePortion(x0,x1){ + Flip_rhs() Flip_bot(1) + translate([x0,0,0]) + mirror([1,0,0]) + rotate([90,0,-90]) + linear_extrude(height=x1-x0) + children(); +} + +module ThumbRecessApply(ztop){ + width = thumbrecess_width; + w = width + thumbrecess_topcurve_r*2 + 1; + translate([phone_width/2, 0,0]){ + difference(){ + rotate([90,0,-90]) + linextr(-w/2, w/2) + children(0); + translate([0, 50, 0]) + rotate([90,0,0]) + linear_extrude(height=100){ + for (m=[0,1]) mirror([m,0,0]) { + hull(){ + translate([w/2, ztop - thumbrecess_topcurve_r]) + circle(thumbrecess_topcurve_r); + translate([w/2, -50]) + square(thumbrecess_topcurve_r*2, center=true); + } + } + } + } + } +} + +module CaseBase(){ + AroundEdges(epp3[1], case_th_bottom, 1) + EdgeProfile(); +} + +function prop_x(gamma) = hp_k / (2 * sin(gamma/2)) - hppT[0]; + +module PropProfileAssignments(gamma){ + // https://en.wikipedia.org/wiki/Solution_of_triangles#Two_sides_and_the_included_angle_given_(SAS) + x = prop_x(gamma); + p = phone_height + prlp10[0] - hppB[0]; + b = p + x; + + q = phone_height - hppT[0] - prcp1[0]; // $prpp7[0] is 0 by definition + a = q + x; + c = sqrt(a*a + b*b - 2*a*b*cos(gamma)); + $prp_alpha = acos( (b*b + c*c - a*a) / (2*b*c) ); + + $prp_theta = 90 - $prp_alpha; + beta = 180 - $prp_alpha - gamma; + psi = 90 - beta; + + //echo("abc", a,b,c); + + v1 = [ [ cos(psi), -sin(psi) ], // x + [ sin(psi), cos(psi) ] ]; // y + + $prpp7 = [0, c + (lpp13[1] - $prpp10[1] - hp_k) ]; + + $prp_r1 = prc_r1; + $prp_r11 = prop_main_th/2; + + $prpp1 = $prpp7 + [1,0] * + // this is approximate, but will do + (prop_main_th/2 + prop_prop_gap + prcp1[0] - cppA[0]); + $prpp3 = $prpp1 + + v1[0] * -$prp_r1 + + v1[1] * ((prcp2[1] - prcp1[1]) - prop_prop_gap); + $prpp12 = $prpp3 + v1[0] * + (prop_end_dia + prop_caserecess_taper * ($prpp1[1] - $prpp3[1])); + $prp_r8 = prop_main_th; + $prpp4 = [ prop_main_th/2, $prpp3[1] ]; + $prp_r5 = $prp_r8; + $prpp5 = [ $prpp12[0] - $prp_r5, + $prpp3[1] - prop_prong_h + $prp_r5 ]; + $prpp6 = $prpp4 + [0,-1] * (prop_prong_h + + prop_prong_heel_slope * ($prpp5[0] - $prpp4[0])); + $prpp8 = $prpp4 + [0,-1] * $prp_r8; + $prpp9 = $prpp8 + [-1,0] * $prp_r8; + + children(); +} + +module PropProfile(gamma, cut=0, rot=0){ + PropProfileAssignments(gamma){ + + //#circleat($prpp3,1); + //#circleat($prpp12,1); + + if (!cut) { + hull(){ + translate($prpp8) + intersection(){ + circle($prp_r8); + polygon([[-20,-0], [20,20], [0,0]]); + } + rectfromto($prpp6, $prpp9); + translate($prpp5) intersection(){ + circle($prp_r5); + polygon([[-10,-10], [0,0], [10,0]]); + } + rectfromto($prpp12 + [0,-0.1], $prpp3); + } + hull(){ + circleat($prpp1, $prp_r1); + rectfromto($prpp12 + [0,-0.1], $prpp3); + } + } + // main shaft + rotate([0,0, rot*-$prp_theta]){ + hull(){ + extra = cut ? prop_recess_slop : 0; + rectfromto($prpp6, $prpp9); + circleat($prpp11, $prp_r11 + extra); + circleat($prpp10, $prp_r10 + extra); + } + } + } +} + +module PropAggregateProfile(){ + for (angle = prop_angles) + PropProfile(angle, 0,0); +} + +module Prop(){ ////toplevel + hw = prop_main_width/2; + linextr(-hw, +hw) + PropAggregateProfile(); +} + +module Case(){ ////toplevel + difference(){ + union(){ + CaseBase(); + + // ledge (fixed keeper) + Flip_rhs(1-keeper_side) intersection(){ + rotate([90, 0, 0]) + linear_extrude(height = phone_height + phone_cnr_rad * 2) + KeeperProfile(fatter=0, slant=1); + + // outline of the whole case, to stop it protruding + translate([0,0, -25]) + linear_extrude(height = 50) + hull() + Flip_bot() + circleat([+1,-1] * phone_cnr_rad, phone_cnr_rad + case_th_side/2); + } + + // hinge + if (!$suppress_hinge) + HingePortion(hex20, hex21) HingeBaseProfile(); + + // buildout for prop recess + if (prop_caserecess_buildout_r > 0) Flip_rhs(1) + linextr(case_bottom_z, epp2i[1]) + hull() { + for (dxs = [-1,+1]) + circleat([ prop_x_pos + dxs * prop_caserecess_buildout_r, + -epp2o[0] ], + r = epp2o[0] - prcp2[0]); + } + } + + // slot for keeper + Flip_rhs(keeper_side) + translate([0, -phone_cnr_rad, 0]) + rotate([90, 0, 0]) + linear_extrude(height = phone_height + phone_cnr_rad * 2) + minkowski(){ + KeeperProfile(fatter=keeper_fatter_hole); + rectfromto([ -keeper_gap_x, -keeper_gap_z_bot ], + [ keeper_gap_x_holes, +keeper_gap_z_top ]); + } + + // front camera + RearCameraAperture(); + + // struts (invisible, because they're buried in the case) + Struts(epp2i[0], epp2i[1] - case_th_bottom, case_th_bottom); + + Buttons(){ + mirror([1,0,0]) + rotate([90,0,90]) { + if (!($button_leg_only && enable_support)) + intersection(){ + translate([0,0,-10]) + linear_extrude(height= 20) + ButtonPlan($button_l, 0,1); + if ($button_leg_only) + rotate([-90,90,0]) + translate([phone_width/2, -400, kppe[1]]) + mirror([1-abs($rhsflip - keeper_side),0,0]) + cube([400, 800, 50]); + if (enable_support && !$button_suppress_over_keeper) + rotate([-90,90,0]) + translate([-400, -400, kppd[1]]) + mirror([0,0,1]) + cube([800,800,100]); + } + translate([0,0, -bppR[0]]) + linear_extrude(height= 20) + ButtonPlan($button_l, 1,1); + } + + } + + // apertures along top edge + if (!$suppress_forward_holes) { + // CaseAperture(jack_pos, jack_dia, 8); + Flip_rhs(1) + CaseAperture(noisecancelmic_pos, noisecancelmic_dia, 8); + } + CaseAperture(lhshole_pos, noisecancelmic_dia, 8, 1); + + OrdinaryBottomEdgeApertures(); + + OrdinaryRearApertures(); + NotInTestFrameRearApertures(); + + MicroUSBEtc(); + + // gaps for the lid's hinge arms + if (!$suppress_hinge) { + HingePortion(hex20 - hinge_x_arms_gap, + hex21 + hinge_x_arms_gap) + minkowski(){ + HingeLidProfile(); + circle(r= hinge_r_arms_gap, $fn= 8); + } + + // screw holes in the hinge arms + HingeScrews(); + } + + // thumb recess + ThumbRecessApply(epp4[1]) + ThumbRecessCutProfile(); + + // lanyard + Flip_bot(1) + translate([ly_o[0], -(phone_cnr_rad + ly_re), ly_o[1]]) + rotate([0, ly_theta, 0]) + rotate([0,0,90]) + LanyardCutout(lanyard_channel_len); + + // prop recess + Flip_rhs(1) + translate([prop_x_pos,0,0]) + mirror([0,1,0]) + rotate([90,0,90]) + linextr(-prop_recess_hw, +prop_recess_hw) + hull(){ + for (d=[ [0,0], [0,-1], [+1,-1/prop_caserecess_taper] ]) + circleat(prcp1 + 20*d, + prc_r3); + } + } +} + +module LidAdhocMultiprintFrame(phase){ + if (led_window_style >= 3) { + AdhocMultiprintFrame(phase, lpp13[1], -1); + } +} + +module LidAroundEdges(){ + AroundEdges(lpp15[1], lpp13[1] - lpp15[1], 0) + children(); +} + +module Lid(){ ////toplevel + skew_centre = [0, lpp11[0], lpp11[1]]; + difference(){ + union(){ + intersection(){ + LidAroundEdges() + LidEdgeProfile(); + + translate(skew_centre) + multmatrix([[ 1, 0, 0, 0 ], + [ 0, 1, -lid_fold_clearance_skew, 0 ], + [ 0, 0, 1, 0 ], + [ 0, 0, 0, 1 ]]) + translate(-skew_centre) + LidAroundEdges() + LidEdgeFoldClearanceProfile(); + } + + // button covers + Buttons(){ + intersection(){ + rotate([90,0,90]) + translate([0,0,-10]) + linear_extrude(height= 20) + ButtonPlan($button_l, 1,0); + union(){ + rotate([90,0,0]) + translate([0,0,-100]) + linear_extrude(height= 200) + ButtonCoverProfile(); + hull() + for (y= [-1,+1] * (($button_l + button_l_fudge)/2 + - lid_buttoncover_reinf)) + translate([0,y,0]) + ButtonCoverReinf(); + } + } + } + + // hinge arms + HingePortion(hex20, hex21) { + LidEdgeProfile(); + HingeLidProfile(); + } + } + Struts(lpp10[0] + strut_min_at_end, lpp13[1], -case_th_lid); + + // screw holes in the hinge arms + HingeScrews(); + + // prop recess + translate([prop_x_pos, -prlp10[0], prlp10[1]]) + mirror([0,1,0]) + rotate([90,0,90]) + linextr(-prop_recess_hw, +prop_recess_hw) + hull() + for (pa = prop_angles) + PropProfile(pa, 1,1); + + // notification led aperture + if (led_window_style) + translate([led_pos[0], -led_pos[1], lpp13[1]]) { + translate([0,0,-10]) + cylinder(r=nla_r0, h=20); + if (led_window_style >= 2) + translate([0,0, -nla_t]) + cylinder(r=nla_r2, height=20); + } + + } + + LidAdhocMultiprintFrame(1); +} + +module HingeLever(){ ////toplevel + difference() { + // outer body, positive + HingePortion(hex22, hex22 + phone_width/2) + HingeLeverOuterProfile(); + + // space for the screws +// HingePortion(hex26, hex24) +// HingeLeverInnerProfile(); + + // recesses for the nuts +// HingePortion(hex23, hex26+1) +// HingeLeverNutProfile(); + + // bores for the screws + HingeScrews(); + + // space for the charging cable and speaker and micc apertures + hull() { + for (x = [-1,+1]) { + multmatrix([[ 1,0, + + x + * ( (hex24 + hinge_over_nut_plate) - + (phone_width/2 - microusb_width/2) + ) + / ( (epp0[1] - microusb_above) + - + (hppB[1] - hp_r2) ), + + x * (epp0[1] - microusb_above) + + ], + [ 0,1,0, 0 ], + [ 0,0,1, 0 ]]) { + union(){ + MicroUSBEtc(); + Flip_hinge() MicroUSBEtc(); + } + } + } + } + } +} + +module LidWindow(){ ////toplevel + translate([led_pos[0], -led_pos[1], lpp13[1]]) + mirror([0,0,1]) + cylinder(r= nla_r1, h=nla_t); + LidAdhocMultiprintFrame(0); +} + +module LidWindowPrint(){ ////toplevel + rotate([0,180,0]) + LidWindow(); +} + +module DemoLidWindowSelect(){ + translate([led_pos[0], led_pos[1], -100]) { + translate([0, -30, 0]) cube([400, 400, 200]); + } +} + +module DemoLidWindow(){ ////toplevel + %Lid(); + LidWindow(); + translate([0,40,0]){ + color("blue") intersection(){ Lid(); DemoLidWindowSelect(); } + color("red") intersection(){ LidWindow(); DemoLidWindowSelect(); } + } +} + +module HingeLeverPrint(){ ////toplevel + rotate([-90,0,0]) + translate([-phone_width/2, phone_height, 0]) + HingeLever(); +} + +module TestSelectLength(){ + translate([-30, -200, -20]) + cube([30 + 15, 250, 40]); +} + +module TestLength(){ ////toplevel + intersection(){ + Case(); + TestSelectLength(); + } +} + +module TestLengthRight(){ ////toplevel + intersection(){ + Case(); + Flip_rhs(1) + TestSelectLength(); + } +} + +module TestSelectWidth(){ + translate([-30, -(phone_height - 25), -20]) + mirror([0, 1, 0]) + cube([200, 50, 40]); +} + +module TestWidth(){ ////toplevel + intersection(){ + Case(); + TestSelectWidth(); + } +} + +module TestLidWidthPrint(){ ////toplevel + rotate([0,180.0]) intersection(){ + Lid(); + TestSelectWidth(); + } +} + +module TestSelectRearAperture(){ + minkowski(){ + union() children(); + translate([20, 0,0]) + cube([42, 2, 1], center=true); + } +} + +module TestSelectCamera(){ + minkowski(){ + TestSelectRearAperture() + RearCameraAperture(); + cube([0.1, 50, 0.1]); + } +} + +module TestSelectOrdinaryRearApertures(){ + TestSelectRearAperture() + OrdinaryRearApertures(); +} + +module TestCamera(){ ////toplevel + intersection(){ + Case(); + TestSelectCamera(); + } +} + +module TestLidByCamera(){ ////toplevel + intersection(){ + Lid(); + TestSelectCamera(); + } +} + +module TestLidByCameraPrint(){ ////toplevel + rotate([180,0,0]) TestLidByCamera(); +} + +module DemoByCamera(){ ////toplevel + color("blue") TestLidByCamera(); + color("red") TestCamera(); +} + +module OneKeeper(){ ////toplevel + translate([0, -phone_cnr_rad, 0]) + rotate([90, 0, 0]) + linear_extrude(height = phone_height - phone_cnr_rad * 2) + KeeperProfile(fatter=keeper_fatter, stubbier=keeper_stubbier); +} + +module OneKeeperPrint(){ ////toplevel + rotate([0,180,0]) + OneKeeper(); +} + +module LidPrint(){ ////toplevel + rotate([0,180,0]) + Lid(); +} + +module TestSelectFrame(){ + include = [1,-1] * (epp2i[0] + 4); + + difference(){ + cube(1000, center=true); + translate([0,0, -100]) + linear_extrude(height=200) + rectfromto(include, inside_br - include); + } + + for (i= [1,2]) { + translate([ 0, -phone[1] * i/3, 0 ]) + cube(center=true, [1000, 4, 100]); + } +} + +module TestSelectLidFrame(){ + TestSelectFrame(); + if (len(led_pos)) + translate([led_pos[0], -led_pos[1], -50]) + cylinder(r= nla_r2+3, h=100); +} + +module TestFrameCase(){ ////toplevel + intersection(){ + Case(); + union(){ + TestSelectFrame(); + TestSelectCamera(); + TestSelectOrdinaryRearApertures(); + } + } +} + +module TestSelectTopApertures(){ + translate([-100, -35, -100]) + cube([400, 100, 200]); + LidAdhocMultiprintFrame(0); + LidAdhocMultiprintFrame(1); +} + +module TestTopApertures(){ ////toplevel + intersection(){ + Case(); + TestSelectFrame(); + TestSelectTopApertures(); + } +} + +module TestLidTopAperturesPrint(){ ////toplevel + rotate([0,180,0]) intersection(){ + Lid(); + TestSelectLidFrame(); + TestSelectTopApertures(); + } +} + +module TestLidWindowTopAperturesPrint(){ ////toplevel + rotate([0,180,0]) intersection(){ + LidWindow(); + TestSelectTopApertures(); + } +} + +module TestFrameLidPrint(){ ////toplevel + rotate([0,180,0]) intersection(){ + Lid(); + TestSelectLidFrame(); + } +} + +module ButtonPlanForDemo(z, deep, cut){ + translate([0,0,z]) + ButtonPlan(8, deep, cut); +} + +module HingeScrews(){ + Flip_rhs() Flip_bot(1){ + for (c= [ hppT, hppB ]) + translate([ hex20, + -c[0], + c[1] ]){ + rotate([0,90,0]) + translate([0,0,-.2]) + cylinder( r= hingescrew_shaft_dia/2, + h = hingescrew_shaft_len+0.2 ); + rotate([0,-90,0]) + translate([0,0,+.1]) + cylinder( r= hingescrew_head_dia/2, h = hingescrew_head_th ); + } + } +} + +module DemoPropAngleSelect(c){ + color(c) difference(){ + union(){ children(); } + translate([ prop_x_pos, -400, -200 ]) + cube([ 400,800,400 ]); + } +} + +module DemoPropAngle(ang){ + hL = [0, -(phone_height - hppT[0]), hppT[1] - hp_k*2]; + hC = [0, -(phone_height - hppB[0]), hppB[1]]; + + translate(hL) + rotate([ang/2,0,0]) + translate(-hL) + translate(hC) + rotate([ang/2,0,0]) + translate(-hC) { + DemoPropAngleSelect("red") Case(); + + color("orange") + translate([prop_x_pos, -prcp1[0], prcp1[1]]) + PropProfileAssignments(ang) { + echo($prpp1); + rotate([-$prp_theta, 0, 0]) + translate([0, $prpp1[0], -$prpp1[1]]) + rotate([90,0,-90]) + Prop(); + } + } + + translate([0,0, -hp_k*2]) + DemoPropAngleSelect("blue") + Lid(); +} + +module DemoPropAngles(){ ////toplevel + for (i=[0 : len(prop_angles)-1]) + translate(i * [0, -100, 100]) + DemoPropAngle(prop_angles[i]); +} + +module DemoHingeAngle(ang1,ang2){ + hL = [0, -(phone_height - hppT[0]), hppT[1]]; + hC = [0, -(phone_height - hppB[0]), hppB[1]]; + + translate(hL) + rotate([ang2,0,0]) + translate(-hL) + translate(hC) + rotate([ang1,0,0]) + translate(-hC) { + color("red") Lid(); + } + + color("blue") intersection(){ + Case(); + union(){ + translate([bppJ[0], -400, -200]) + mirror([1,0,0]) + cube([400, 800, 400]); + translate([10, -400, -200]) + cube([10, 800, 400]); + } + } +} + +module DemoHingeAngles(){ ////toplevel + angles = [ 0, 4, 8, 12 ]; + echo("angles",angles); + for (i=[0 : len(angles)-1]) { + translate(i * [0, 0, 30]) { + DemoHingeAngle(0,angles[i]); + translate([0, 200, 0]) + DemoHingeAngle(angles[i],0); + } + } +} + +module DemoSelectAdhocLeftRight(right=0) { + translate([phone_width/2, -400, -100]) // , -15, -100 to cross-section + mirror([1-right, 0,0]) + cube([400, 800, 200]); +} + +module DemoLeft(){ ////toplevel + color("red") intersection(){ Case(); DemoSelectAdhocLeftRight(); } + color("blue") intersection(){ Lid(); DemoSelectAdhocLeftRight(); } +} + +module DemoFrame(){ ////toplevel + color("red") render() TestFrameCase(); + color("blue") render() intersection(){ Lid(); TestSelectLidFrame(); } + color("black") render() HingeScrews(); + %render() HingeLever(); +} + +module DemoLanyardCutout(){ ////toplevel + LanyardCutout(25); +} + +module DemoHingedFrame(){ ///toplevel + color("red") render() TestFrameCase(); + translate([0,0, -2*hp_k]) + color("blue") render() intersection(){ Lid(); TestSelectLidFrame(); } + + Flip_hinge(){ + color("orange") render() HingeLever(); + color("black") render() HingeScrews(); + } +} + +module DemoHinge(){ ////toplevel + translate([ -0.5*phone_width, phone_height, hp_k*3 ]) { + DemoFrame(); + translate([0,0, -hp_k*3]) + DemoHingedFrame(); + } +} + +module DemoProfiles(){ ////toplevel + LidEdgeProfile(); + %EdgeProfile(); + KeeperProfile(); + translate([0,0,-1]) color("black") KeeperProfile(1); + translate(ly_o){ + rotate(-ly_theta){ + translate([0,0,+1]) color("purple") LanyardMainChannelProfile(); + translate([0,0,+2]) color("red") LanyardCurveChannelProfile(); + translate([0, ly_q_z]){ + translate([0,0,-1]) color("blue") LanyardEntryChannelProfile(); + translate([ly_oec_y,0,-2]) color("black") LanyardEntryOuterProfile(); + } + } + } + translate([0,0,-5]) color("white") translate(epp2i) + rotate(-ly_theta) + rectfromto([-15, 0], + [+15, -max_case_bottom_edge_thickness]); + + translate([0,20]) { + LanyardMainChannelProfile(); + translate([0,0,1]) color("purple") LanyardCurveChannelProfile(); + translate([0,0,-1]) color("red") LanyardEntryChannelProfile(); + } + + translate([20,0]) { + LidEdgeProfile(); + %EdgeProfile(); + + demopoint_QR = [ bppS[0], bppQ[1] - 0.1]; + + color("blue") ButtonCoverProfile(); + color("red") { + rectfromto(bppQ, demopoint_QR); + rectfromto(bppR, demopoint_QR); + } + } + + translate([-20,0]) { + color("black") ButtonPlanForDemo(-2, 0,1); + color("red" ) ButtonPlanForDemo(-4, 1,1); + color("blue") ButtonPlanForDemo(-6, 1,0); + } + + translate([0, -30]) { + %LidEdgeProfile(); + %EdgeProfile(); + color("blue") HingeLidProfile(); + color("red") HingeBaseProfile(); + color("black") translate([0,0,-2]) HingeLeverOuterProfile(); + } + + for (f=[0,1]) { + translate([-30, -60 + 30*f]) { + translate([0,0,-4]) EdgeProfile(); + %translate([0,0,-10]) HingeBaseProfile(); + translate([0,-2] * f * hp_k) { + translate([0,0,-4]) LidEdgeProfile(); + %translate([0,0,-10]) %HingeLidProfile(); + } + translate(+hppB) rotate([0,0,180*f]) translate(-hppB) { + translate([0,0,-2]) color("black") HingeLeverOuterProfile(); + translate([0,0,0]) color("red") difference(){ + HingeLeverOuterProfile(); + HingeLeverInnerProfile(); + } + translate([0,0,3]) color("yellow") HingeLeverNutProfile(); + } + } + } + + translate([20,-30]) { + %EdgeProfile(); + %LidEdgeProfile(); + //translate([0,0,1]) ThumbRecessCutProfile(); + translate([0,0,+1]) color("red") + difference(){ EdgeProfile(); ThumbRecessCutProfile(); } + } + + translate([40,-30]) { + difference(){ + LidEdgeProfile(); + translate(prlp10) + PropProfile(10, 1, 0); + } + translate(prlp10) + PropProfile(15, 0); + } + translate([60,-30]) { + PropAggregateProfile(); + } +} + +//EdgeProfile(); +//KeeperProfile(); +//CaseBase(); +//%Case(); +//Keeper(); +//LidEdgeProfile(); +//KeeperProfile(); +//DemoProfiles(); +//PropRecess(); diff --git a/filament-test.scad b/filament-test.scad new file mode 100644 index 0000000..443aab5 --- /dev/null +++ b/filament-test.scad @@ -0,0 +1,16 @@ +// -*- C -*- +translate([3,3,0]) mirror([1,1,0]) cube([15,15,1]); + +multmatrix([[ 1, 0, 0, 0 ], + [ 0, 1, 1.0, 0 ], + [ 0, 0, 1, 0 ], + [ 0, 0, 0, 1 ]]) + cylinder(r=6.1/2, h=8); + +w=0.5; + +translate([15,0]) +difference(){ + cube([8,8,8]); + translate([w,w,-1]) cube([8-w*2, 8-w*2, 8+2]); +} diff --git a/filamentclip.scad b/filamentclip.scad new file mode 100644 index 0000000..e6d7afb --- /dev/null +++ b/filamentclip.scad @@ -0,0 +1,53 @@ +include +include + +rad=19; +h=3.5; +w=2.5; + +looprad=2.5; +loopw=w; + +fdia=1.77; +//fdia=3; + +d=0.01; + +module our_ClipHook(ye){ + ClipHook(h=h, w=w, g=0.6, k=1.5, g=0.6, ye=ye, cupcaph=0.5, cupcapg=0.8); +} + +module FilamentClip() { + rotate([0,0,-70]) { + translate([0,rad-1.5,0]) { + rotate([0,0,8]) + our_ClipHook(ye=-1.3); + } + } + + rotate([0,0,-35]) { + translate([0,rad,0]) { + rotate([0,0,180]) + our_ClipHook(ye=0.8); + } + } + + linear_extrude(height=h) { + assign($fn=80) { + FlatArc(0,0, rad-w/2,rad+w/2, 80,350); + } + assign($fn=30) { + FlatArc(0,rad+looprad+w, looprad,looprad+loopw); + } + } + + for (mir=[0,1]) { + mirror([mir,0,0]) + rotate([0,0,-40]) + translate([rad+w*0.3+teethw*0.3+fdia/2, 0, 0]) + rotate([0,0,95]) + FilamentTeeth(fdia=fdia, h=h); + } +} + +FilamentClip(); diff --git a/filamentspool-lt.scad b/filamentspool-lt.scad new file mode 100644 index 0000000..3605f00 --- /dev/null +++ b/filamentspool-lt.scad @@ -0,0 +1,4 @@ +//// toplevels-from: +include +lightduty = true; +fdia = 2.85; diff --git a/filamentspool-number.eps.pl b/filamentspool-number.eps.pl new file mode 100755 index 0000000..c05903a --- /dev/null +++ b/filamentspool-number.eps.pl @@ -0,0 +1,14 @@ +#!/usr/bin/perl -w +use strict; +die unless @ARGV==1 && $ARGV[0] =~ m/^\d+/; +my $num = $ARGV[0]; +$num /= 1000; +printf < +lightduty = true; +fdia = 1.75; diff --git a/filamentspool-storarm3.scad b/filamentspool-storarm3.scad new file mode 100644 index 0000000..31dbaa9 --- /dev/null +++ b/filamentspool-storarm3.scad @@ -0,0 +1,7 @@ +// -*- C -*- +include +lightduty = false; +fdia = 2.85; +storarm_spools=3; +//// module StorageArmLeft ////toplevel +//// module StorageArmRight ////toplevel diff --git a/filamentspool.scad b/filamentspool.scad new file mode 100644 index 0000000..b2991ea --- /dev/null +++ b/filamentspool.scad @@ -0,0 +1,1025 @@ +// -*- C -*- + +// filamentspool.scad +// 3D design for filament spools to hold coils as supplied by Faberdashery +// + +// +// Copyright 2012,2013,2016 Ian Jackson +// +// This work is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This work is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this work. If not, see +// + +// +// 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 +include +include +include +include + +hub_clip_baseextend = (hubeffrad - DoveClip_depth() + - hubbigrad + hublwidth); + +real_exteffrad = selsz(exteffrad + hub_clip_baseextend, + hubeffrad + DoveClip_depth(), + hubeffrad + nondove_armbase); + +channelwidth = prongthick + channelslop; +channeldepth = prongwidth + ratchettoothheight; +totalwidth = armendwallthick*2 + channelwidth; +totalheight = channeldepth + armendbasethick; +stalkwidth = prongwidth + prongstalkxwidth; + +tau = PI*2; + +module ArmEnd(length=armend_length){ ////toplevel + if (usedove()) { + translate([ratchettoothsmoothr, channelwidth/2, -armendbasethick]) { + rotate([0,0,-90]) + DoveClipPairBase(h=doveclipheight); + } + } else { + difference(){ + translate([1, -armendwallthick, -armendbasethick]) + mirror([1,0,0]) + cube([nondove_armbase+1, totalwidth, totalheight]); + translate([-nondove_armbase + nondove_armhole_x, + -armendwallthick + totalwidth/2, + -armendbasethick -1]) + cylinder(r= nondove_armhole_hole/2, h=totalheight+2, $fn=10); + translate([-nondove_armbase, -armendwallthick, -armendbasethick]) + rotate([90,0,0]) + Commitid_BestCount([nondove_armbase, totalwidth]); + } + } + + difference(){ + union(){ + difference(){ + translate([0, -armendwallthick, -armendbasethick]) + cube([length, totalwidth, totalheight]); + translate([-1, 0, 0]) + cube([length+1 - ratchettooth, channelwidth, channeldepth+1]); + translate([-1, 0, ratchettoothheight]) + cube([length+2, channelwidth, channeldepth+1]); + } + for (dx = [0 : ratchetstep : length - ratchetstep]) translate([dx,0,0]) { + translate([ratchettoothsmoothr+0.5, armendwallthick/2, 0]) minkowski(){ + rotate([90,0,0]) + cylinder($fn=20, r=ratchettoothsmoothr, h=armendwallthick); + multmatrix([ [ 1, 0, ratchettoothslope, 0 ], + [ 0, 1, 0, 0 ], + [ 0, 0, 1, 0 ], + [ 0, 0, 0, 1 ]]) + cube([ratchettooth - ratchettoothsmoothr*2, + channelwidth, ratchettoothheight - ratchettoothsmoothr]); + } + } + } + + for (otherside=[0,1]) { + for (circum = [300:100:1500]) { + assign(rad = circum / tau) + assign(fn = str("filamentspool-number-n",circum,".dxf")) + assign(rotateoffset = [0, totalwidth/2, 0]) + assign(xlen = rad - real_exteffrad) { + if (xlen >= numbers_tick_width/2 + + (otherside ? numbers_height_allow : 0) && + xlen <= length - (otherside ? 0 : numbers_height_allow)) + translate([xlen, -armendwallthick, + -armendbasethick + (totalheight - numbers_tick_len)/2]) + translate(rotateoffset) + rotate([0,0, otherside*180]) + translate(-rotateoffset){ + translate([-numbers_tick_width/2, -1, 0]) + cube([numbers_tick_width, numbers_relief+1, numbers_tick_len]); + translate([numbers_tick_width/2 + numbers_tick_linespc, + 1, + numbers_tick_len]) + rotate([90,0,0]) + rotate([0,0,-90]) + linear_extrude(height= numbers_relief+1) + // scale(templatescale) + import(file=fn, convexity=100); + } + } + } + } + + if (usedove()){ + translate([0, -armendwallthick, -armendbasethick]) + Commitid_BestCount_M([length/3, totalwidth]); + } + } +} + +module FilamentCupHandle(){ + pawlusewidth = ratchetpawl-ratchettoothsmoothr*2; + mirror([0,1,0]) { + cube([stalklength, stalkwidth, prongthick]); + translate([stalklength, stalkwidth/2, 0]) + cylinder(r=stalkwidth/2, h=prongthick, $fn=20); + translate([ratchettoothsmoothr, stalkwidth, 0]) { + minkowski(){ + cylinder($fn=20,r=ratchettoothsmoothr, h=1); + multmatrix([ [ 1, -ratchettoothslope, 0, 0 ], + [ 0, 1, 0, 0 ], + [ 0, 0, 1, 0 ], + [ 0, 0, 0, 1 ]]) + cube([pawlusewidth, + ratchettoothheight - ratchettoothsmoothr, + prongthick - 1]); + } + } + } +} + +module FilamentCupCup(){ + for (my=[0,1]) mirror([0,my,0]) { + translate([0, cupwidth/2, 0]) + cube([cupheight + prongwidth, prongwidth, prongthick]); + } +} + +module FilamentCupPositive() { + FilamentCupHandle(); + + gapy = prongwidth; + dy = cupwidth/2 + gapy + overclipcupgap; + baselen = dy+cupwidth/2; + + translate([0, dy, 0]) + FilamentCupCup(); + cube([prongwidth, baselen+1, prongthick]); + + translate([cupstrong_dx, prongwidth, 0]) { + cube([prongwidth, baselen-prongwidth, prongthick]); + for (y = [0, .33, .67, 1]) + translate([0, (baselen - prongwidth) * y, 0]) + cube([-cupstrong_dx + 1, prongwidth, prongthick]); + } + if (cupstrong_dx != 0) { + rotate([0,0,45]) + translate([-prongwidth*.55, -prongwidth*2.1, 0]) + cube([prongwidth*(2.65), prongwidth*4.2, prongthick]); + } + + translate([0, -0.2, 0]) + cube([prongribwidth, baselen, prongthick + prongribheight]); + + if (prongribheight > 0) { + translate([-prongwidth, baselen, 0]) + cube([cupheight/2, prongwidth + prongribheight, prongribwidth]); + } + + midrad = cupwidth/2 + prongwidth/2; + + propshift = stalklength - overclipdepth - prongthick + propxshift; + proptaken = propshift; + echo(midrad, propshift, proptaken); + + translate([propshift, -1, 0]) { + // something is wrong with the y calculation + cube([prongwidth, + gapy+2, + prongthick]); + } + for (y = [overclipcupgap, overclipcupgap+overclipcupnextgap]) { + translate([cupstrong_dx, y + prongwidth, 0]) + rotate([0,0, 102 + fdia]) + FilamentTeeth(fdia=fdia, h=teethh); + } + for (x = [-0.3, -1.3]) { + translate([cupheight + overclipcupnextgap*x, baselen + prongwidth, 0]) + rotate([0,0, 12 + fdia]) + FilamentTeeth(fdia=fdia, h=teethh); + } +} + +module FilamentCup() { ////toplevel + difference(){ + FilamentCupPositive(); + translate([0, -stalkwidth, 0]) + Commitid_BestCount_M([stalklength - stalkwidth, stalkwidth]); + } +} + +module CupSecuringClipSolid(w,d,h1,h2){ + rotate([0,-90,0]) translate([0,-h1/2,-w/2]) linear_extrude(height=w) { + polygon(points=[[0,0], [d,0], [d,h2], [0,h1]]); + } +} + +module CupSecuringClipSolidSmooth(xrad=0, xdepth=0){ + hbase = totalheight + prongstalkxwidth - overcliproundr*2; + minkowski(){ + CupSecuringClipSolid(w=totalwidth, + d=overclipdepth + xdepth, + h1=hbase + overclipbigger, + h2=hbase - overclipsmaller); + cylinder($fn=20, h=0.01, r=overcliproundr+xrad); + } +} + +module CupSecuringClip(){ ////toplevel + wingswidth = wingspoke*2 + overclipthick*2 + overcliproundr*2 + totalwidth; + difference(){ + union(){ + CupSecuringClipSolidSmooth(xrad=overclipthick, xdepth=0); + translate([-wingswidth/2, -wingsize/2, 0]) + cube([wingswidth, wingsize, wingthick]); + translate([-wingsize/2, -wingswidth/2, 0]) + cube([wingsize, wingswidth, wingthick]); + } + translate([0,0,-0.1]) + CupSecuringClipSolidSmooth(xrad=0, xdepth=0.2); + } +} + +module ArmDoveClipPin(){ ////toplevel + DoveClipPin(h=doveclipheight); +} + +module TowerDoveClipPin(){ ////toplevel + DoveClipPin(h=towercliph/2); +} + +module Hub(){ ////toplevel + axlerad = hubaxlerad + slop; + xmin = axlerad+hublwidth/2; + xmax = hubbigrad-hublwidth/2; + hole = hubeffrad - hubbigrad - DoveClip_depth() - hublwidth*2; + holewidth = DoveClipPairSane_width() - hubstemwidth*2; + nondove_allwidth = nondove_armhole_wall*2 + totalwidth; + difference(){ + union(){ + difference(){ + cylinder($fn=60, h=hublthick, r=hubbigrad); + translate([0,0,-1]) + cylinder($fn=30, h=hublthick+2, r=(hubbigrad-hublwidth)); + } + cylinder(h=hubaxlelen, r=axlerad+hublwidth); + for (ang=[0 : 360/num_arms : 359]) + rotate([0,0,ang]) { + if (usedove()){ + difference() { + translate([hubeffrad,0,0]) + DoveClipPairSane(h=doveclipheight, + baseextend = hub_clip_baseextend); + if (hole>hublwidth && holewidth > 2) { + translate([hubbigrad + hublwidth, -holewidth/2, -1]) + cube([hole, holewidth, hublthick+2]); + } + } + } else { + difference(){ + translate([0, + -nondove_allwidth/2, + 0]) + cube([hubeffrad + nondove_armhole_x + + nondove_armhole_hole/2 + nondove_armhole_support, + nondove_allwidth, + nondove_armhole_wall + totalheight]); + translate([hubeffrad - nondove_armhole_slop_x, + -nondove_allwidth/2 + + nondove_armhole_wall - nondove_armhole_slop, + nondove_armhole_wall]) + cube([nondove_armhole_x + 50, + totalwidth + nondove_armhole_slop*2, + totalheight + 1]); + translate([hubeffrad + nondove_armhole_x, 0, -20]) + cylinder(r= nondove_armhole_hole/2, h=50, $fn=10); + } + } + } + for (ang = [0 : 180/num_arms : 359]) + rotate([0,0,ang]) rotate([90,0,0]) { + translate([0,0,-hublwidth/2]) + linear_extrude(height=hublwidth) + polygon([[xmin,0.05], [xmax,0.05], + [xmax,hublthick-0.2], [xmin, hubaxlelen-0.2]]); + } + } + translate([0,0,-1]) cylinder($fn=60, h=hubaxlelen+2, r=axlerad); + + rotate([0,0, selsz(0,0,45)]) + translate([axlerad+hublwidth, + -hublwidth/2, + 0]) + rotate([90,0,0]) + Commitid_BestCount([(hubbigrad-hublwidth) - (axlerad+hublwidth), + hublthick + + hublwidth/2 * hubaxlelen/(hubbigrad-axlerad), + ]); + } +} + +module ArmExtender(){ ////toplevel + DoveClipExtender(length=exteffrad-hubeffrad, + ha=doveclipheight, + hb=doveclipheight); +} + +module FsAxlePin(){ ////toplevel + AxlePin(hubaxlerad, washerrad*2, axlepinrad, axlepintabrad, slop); +} + +module Axle(){ ////toplevel + pillarswidth = DoveClipPairSane_width(towerclipcount); + + rotate([0,0, -( axleaxlefudgebend + atan(slop/hubaxlelen) ) ]) + translate([-axlehorizoffset, -axlevertheight, 0]) { + rotate([0,0,-axletowerfudgebend]) + rotate([0,0,-90]) + DoveClipPairSane(h=towercliph, count=towerclipcount, baseextend=3); + translate([0, DoveClip_depth(), 0]) + rotate([0,0,90]) + ExtenderPillars(axlevertheight - DoveClip_depth(), + pillarswidth, towercliph, + pillarw=towerpillarw); + } + + axleclearlen = hubaxlelen + slop*4 + washerthick*2 + axlepadlen; + axlerad = hubaxlerad-slop; + bump = axlerad * 0.2; + shift = axlerad-bump; + joinbelowallow = 3; + + intersection(){ + translate([0, 0, shift]) { + difference() { + union(){ + translate([-1, 0, 0]) + rotate([0,90,0]) + cylinder($fn=60, + r = axlerad, + h = 1 + axleclearlen + axlepinrad*2 + 2); + mirror([1,0,0]) rotate([0,90,0]) + cylinder(r = axlerad*1.75, h = 3); + intersection(){ + mirror([1,0,0]) + translate([axlehorizoffset - pillarswidth/2, 0, 0]) + rotate([0,90,0]) + cylinder($fn=60, + r = towercliph - shift, + h = pillarswidth); + translate([-50, -joinbelowallow, -50]) + cube([100, joinbelowallow+50, 100]); + } + } + rotate([90,0,0]) + translate([axleclearlen + axlepinrad/2, 0, -25]) + cylinder(r = axlepinrad + slop, h=50); + } + } + translate([-50,-50,0]) cube([100,100,100]); + } +} + +module washer(thick){ + Washer(hubaxlerad, washerrad, thick, slop); +} + +module AxleWasher(){ ////toplevel + washer(thick=washerthick); +} + +module AxleThinWasher(){ ////toplevel + washer(thick=washerthinthick); +} + +module AxleVeryThinWasher(){ ////toplevel + washer(thick=washerverythinthick); +} + +module AxleFrictionWasher(){ ////toplevel + difference(){ + cylinder(h=washerthick, r=washerrad); + translate([0,0,-1]) cylinder(h=washerthick+2, r=hubaxlerad+slop); + } + frarmr = hubbigrad; + frarmw = frictionwasherarmwidth; + frarmpawlr = hublwidth; + frarmpawlpush = slop*4 + frictionwasherextrapush; + for (ang=[0,180]) rotate([0,0,ang]) { + translate([washerrad-1, -frarmw/2, 0]) + cube([frarmr - washerrad + 1, frarmw, washerthick]); + intersection(){ + translate([frarmr - frarmpawlr, -50, 0]) + cube([frarmpawlr, 100, 50]); + rotate([0,90,0]) + cylinder(h = 50, r = frarmpawlpush, $fn=36); + } + } +} + +module TowerExtender(){ ////toplevel + l = totalheightfromtower - axlevertheight; + echo("TowerExtender",l); + DoveClipExtender(length = l, + ha = towercliph, hb = towercliph, + counta = towerclipcount, countb = towerclipcount, + pillarw = towerpillarw); +} + +module FilamentCupPair(){ ////toplevel + FilamentCup(); + translate([cupheight + prongthick*3, + cupwidth/2*1.7, + 0]) + rotate([0,0,180]) FilamentCup(); +} + +//----- storarm ----- + +storarm_hooklen = 8; +storarm_hookheight = 5; +storarm_thick = 10; +storarm_axleslop = 4; + +storarm_base_w = 30; +storarm_base_h = 100; +storarm_base_d = 15; +storarm_base_mind = 2; + +storarm_cope_hubaxle_mk1 = true; + +storarm_screw_hole = 4; +storarm_screw_hole_slop = 0.5; +storarm_besides_hole = 4; + +storarm_under_hole = 5; +storarm_screw_hole_head = 8.8; +storarm_screw_hole_head_slop = 1.5; + +// calculated + +storarm_spools = 2; + +storarm_axlerad = hubaxlerad - storarm_axleslop; +storarm_mainlen = hubaxlelen*storarm_spools + + storarm_axleslop*(storarm_spools-1) + + (storarm_cope_hubaxle_mk1 ? 10 : 0); +storarm_totlen = storarm_mainlen + storarm_hooklen; + +storarm_taller = storarm_axleslop * (storarm_spools-2); + +storarm_mid_off_y = storarm_axlerad; + +storarm_base_off_y = storarm_mid_off_y + storarm_base_h/2; + +module StorageArmDiagPartSide(xmin, xmax){ + xsz = xmax-xmin; + yuse = storarm_thick/2; + + intersection(){ + translate([xmin-1, -storarm_axlerad, storarm_thick/2]) + rotate([0,90,0]) + cylinder(r=storarm_axlerad, h=xsz+2, $fn=60); + translate([xmin, -yuse, 0]) + cube([xsz, yuse, storarm_thick]); + } +} + +module StorageArmDiagPart(xmin, xmax, adjbot, shear){ + hull(){ + StorageArmDiagPartSide(xmin,xmax); + + multmatrix([[1,0,0,0], + [shear,1,0,0], + [0,0,1,0], + [0,0,0,1]]) + translate([0, -storarm_axlerad*2 + adjbot, 0]) + mirror([0,1,0]) + StorageArmDiagPartSide(xmin,xmax); + } +} + +module StorageArmBaseTemplate(){ + square([storarm_base_w, storarm_base_h]); +} + +module StorageArmAtMountingHoles(){ + bes = storarm_besides_hole + storarm_screw_hole; + + x0 = bes; + x1 = storarm_base_w-bes; + y1 = storarm_base_h - bes; + y0 = bes; + + for (pos=[ [x0, y1], + [x1, y1], + [x1, y0] ]) { + rotate([0,90,0]) + translate([pos[0] - storarm_base_w, + pos[1] - storarm_base_off_y, -storarm_base_d]) + children(); + } +} + +module StorageArmRight(){ ////toplevel + shear = storarm_hookheight / (storarm_mainlen/2); + shear2 = shear + storarm_taller / (storarm_mainlen/2); + base_xyz = [-storarm_base_d, -storarm_base_off_y, storarm_base_w]; + + StorageArmDiagPart(-1, storarm_mainlen/2+1, + -storarm_taller, shear2); + StorageArmDiagPart(storarm_mainlen/2-1, storarm_mainlen+1, + storarm_hookheight/2, shear/2); + + translate([0, storarm_hookheight, 0]) + StorageArmDiagPart(storarm_mainlen, storarm_totlen, + -storarm_hookheight/2, shear/2); + + difference(){ + union(){ + hull(){ + translate(base_xyz) + rotate([0,90,0]) + linear_extrude(height=storarm_base_mind) + StorageArmBaseTemplate(); + StorageArmDiagPart(-1, 0, -storarm_taller, shear); + } + StorageArmAtMountingHoles(){ + cylinder(r= storarm_screw_hole_head/2, + h=10); + } + } + StorageArmAtMountingHoles(){ + translate([0,0,-1]) + cylinder(r= (storarm_screw_hole + storarm_screw_hole_slop)/2 , + h=20); + translate([0,0,storarm_under_hole]) + cylinder(r= (storarm_screw_hole_head + storarm_screw_hole_head_slop)/2, + h=20); + } + translate(base_xyz + [0, storarm_base_h/4, -storarm_base_w/4]) + rotate([0,90,0]) + Commitid_BestCount([storarm_base_w/2, storarm_base_h/2]); + } +} + +module StorageArmLeft(){ ////toplevel + mirror([1,0,0]) StorageArmRight(); +} + +module StorArmHoleTest(){ ////toplevel + sz = storarm_screw_hole_head + storarm_besides_hole*2; + intersection(){ + StorageArmRight(); + translate([-50, -storarm_base_off_y, -1]) + cube([100, sz, sz+1]); + } +} + + +//----- filament guide spacer ----- + +guide_armdia = 15.0; +guide_armwidth = 10.2; +guide_armcorelen = 25.0; +guide_clipcirclethick = 10.0; + +guidefilclip_outerdia = 22.8; + +guidespacer_armslop = 0.75; +guidespacer_armlenslop = 1.05; + +guidespacer_prongprotrude = 4; +guidespacer_thick = 1.6; + +// calculated + +guidespacer_armdia = guide_armdia + guidespacer_armslop; +guidespacer_armwidth = guide_armwidth + guidespacer_armslop; +guidespacer_len = guide_armcorelen - guide_clipcirclethick + + guidespacer_armlenslop; + +guidespacer_wingheight = (guidefilclip_outerdia - guidespacer_armdia)/2; + +module FilamentGuideArmTemplate(extra=0){ + intersection(){ + circle(r= (guidespacer_armdia/2) + extra); + square(center=true, [guidespacer_armwidth+extra*2, + guidespacer_armdia + extra*2 + 10]); + } +} + +module FilamentGuideSpacerInnerTemplate(){ + FilamentGuideArmTemplate(); + translate([0, -guidespacer_armdia/2]) + square(center=true, [guidespacer_armwidth - guidespacer_prongprotrude, + guidespacer_armdia]); +} + +module FilamentGuideSpacer(){ ////toplevel + difference(){ + union(){ + linear_extrude(height= guidespacer_len) + FilamentGuideArmTemplate(extra= guidespacer_thick); + for (angle=[26, 60]) { + for (m=[0,1]) { + mirror([m,0,0]) { + rotate([0,0,angle]) { + hull(){ + for (t=[[0, guidespacer_wingheight], + [guidespacer_len-1, -guidespacer_wingheight]]) + translate([0,0, t[0] + 0.5]) + cube([guidespacer_thick, guidespacer_armdia + t[1]*2, + 1], + center=true); + } + } + } + } + } + } + translate([0,0,-1]) + linear_extrude(height= guidespacer_len+5) + FilamentGuideSpacerInnerTemplate(); + } +} + + +//----- replacement filament guide arm for TAZ-5 ----- + +guidearm_armslop = 0.25; +guidearm_armlenslop = 0.25; + +guidearm_hookprotr = 3; +guidearm_hookprotrflat = 1; +guidearm_hookslope = 0.3; + +guidearm_totallen = 60; + +guidearm_screwplatesz = 12; +guidearm_screwplateth = 4; +guidearm_screwplatewd = 15; +guidearm_screwhole = 5 + 0.5; + +guidearm_bendlen = 40; +guidearm_bendslot = 4.5; + +guidearm_stopthick = 4; +guidearm_protrslop = 1.0; + +// calculated + +guidearm_armdia = guide_armdia - guidearm_armslop; +guidearm_armwidth = guide_armwidth - guidearm_armslop; +guidearm_armcorelen = guide_armcorelen + guidearm_armlenslop; + +guidearm_base_z0 = -(guidearm_totallen - guidearm_armcorelen); + +guidearm_realbendlen = min(guidearm_bendlen, + guidearm_totallen - guidearm_screwplateth - 0.1); +guidearm_slopelen = guidearm_hookprotr/guidearm_hookslope; + +module FilamentGuideArmStop(h){ + for (ts=[-1,+1]) { + translate([ts * guidearm_hookprotr, 0,0]) + cylinder(r=guidearm_armdia/2, h, $fn=80); + } +} + +module FilamentGuideArmShaftPositive(){ + r = guidearm_armdia/2; + + translate([0,0, guidearm_base_z0+1]) + cylinder(r=r, h= guidearm_totallen, $fn=80); + translate([0,0, guidearm_armcorelen]){ + hull(){ + FilamentGuideArmStop(guidearm_hookprotrflat); + translate([0,0, guidearm_slopelen]) + cylinder(r=r, h=guidearm_hookprotrflat, $fn=80); + } + } + mirror([0,0,1]) + FilamentGuideArmStop(guidearm_stopthick); +} + +module FilamentGuideArmBase(){ + translate([0, + (guidearm_screwplatewd - guidearm_armwidth)/2, + guidearm_base_z0]){ + difference(){ + translate([0,0, guidearm_screwplateth/2]) + cube(center=true, + [guidearm_armdia + guidearm_screwplatesz*2, + guidearm_screwplatewd, + guidearm_screwplateth]); + for (ts=[-1,+1]) { + translate([ts * (guidearm_armdia/2 + guidearm_screwplatesz/2), + 0, + -20]) + cylinder(r= guidearm_screwhole/2, h=40, $fn=20); + } + } + } +} + +module FilamentGuideArm(){ ///toplevel + intersection(){ + difference(){ + FilamentGuideArmShaftPositive(); + translate([-guidearm_bendslot/2, + -50, + -guidearm_realbendlen + guidearm_armcorelen]) + cube([guidearm_bendslot, + 100, + guidearm_realbendlen + 100]); + hull(){ + for (zx=[ [ 0, guidearm_bendslot ], + [ guidearm_armcorelen + guidearm_slopelen, + guidearm_hookprotr*2 + guidearm_protrslop ] + ]) { + translate([-zx[1]/2, -50, zx[0]]) + cube([zx[1], 100, 1]); + } + } + } + cube(center=true, + [guidearm_armdia*2, + guidearm_armwidth, + guidearm_totallen*3]); + } + FilamentGuideArmBase(); +} + +module FilamentGuideArmPrint(){ ////toplevel + rotate([90,0,0]) + FilamentGuideArm(); +} + +module Demo(){ ////toplevel + translate([-real_exteffrad,-20,0]) Hub(); + ArmEnd(); + translate([ratchettooth*2, 30, 0]) FilamentCup(); + if (selsz(true,false,false)) { + translate([-exteffrad + hubeffrad - hub_clip_baseextend, -10, 0]) + ArmExtender(); + } +} + +//ArmEnd(); +//FilamentCup(); +//FilamentCupPair(); +//CupSecuringClip(); +//Hub(); +//ArmExtender(); +//Axle(); +//AxleWasher(); +//AxlePin(); +//AxleFrictionWasher(); +//StorageArmLeft(); +//StorArmHoleTest(); +//FilamentGuideSpacer(); +//FilamentGuideArm(); +//FilamentGuideArmPrint(); +//Demo(); diff --git a/filamentteeth.scad b/filamentteeth.scad new file mode 100644 index 0000000..f948c6e --- /dev/null +++ b/filamentteeth.scad @@ -0,0 +1,61 @@ +// -*- C -*- +// +// filamentteeth.scad +// +// 3D design for clips to hold FFF filament +// Copyright 2012,2016 Ian Jackson +// +// This work is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This work is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this work. If not, see . + + +d=0.01; +teethw=1.5; + +module FilamentTeeth(fdia, h, teethw=teethw, + stembendd=0.5, stembendl=7, teethxl=1.5) { + gapw = fdia-stembendd*2; + teethbigw = gapw + teethw*2; + basew = fdia+teethw*2-stembendd*2; + based = basew/3; + + translate([-based, -basew/2, 0]) cube([based, basew, h]); + difference() { + union() { + translate([-d, -teethbigw/2, 0]) + cube([d+stembendl + teethw, teethbigw, h]); +// translate([ +// stembigw = fdia + stembend +// translate([-d, -stemw, + linear_extrude(height=h) { + translate([stembendl-fdia/2, 0]) circle(fdia/2+teethw, $fn=30); + } + } + translate([0,0,-1]) { + translate([0,-gapw/2]) + cube([stembendl+teethxl+1, gapw, h+2]); + linear_extrude(height=h+2) { + translate([stembendl-fdia/2, 0]) circle(fdia/2, $fn=30); + } + } + } + + for (mirr=[0:1]) { + mirror([0,mirr,0]) { + translate([stembendl + teethw, gapw/2, 0]) + rotate([0,0,30]) + cube([teethxl, teethw, h]); + } + } +} + diff --git a/filamenttrestle.scad b/filamenttrestle.scad new file mode 100644 index 0000000..376a1ae --- /dev/null +++ b/filamenttrestle.scad @@ -0,0 +1,284 @@ +// -*- C -*- + +spoolinnerdia = 32; +spoolwidth = 88.0; +spoolinnerrad = (spoolinnerdia - 0.2) / 2; +spoolouterrad = spoolinnerrad + 61.5; + +include +include + +spoolradclear = 10; +spoolradslop = 2; + +spoolinnerslop = 3; +axleslop = 0.5; + +axlerad = 7; +barwasherrad = 17; + +hubbasethick = 4; +hubmainthick = 15; +hubbaseweb = 1.2; +hubbasestalkwidth = 4; +hubwalls = 2.5; +hubpillarw = 4; +hubbaserad = spoolinnerrad + 10; +hubmainrad = spoolinnerrad - spoolradslop; + +legw = 12; +plugl = 20; +plugwmin = 3; +plugh = 10; +plugslope = 0.5; +plugwmax = plugwmin + plugh * plugslope * 2; + +trestlefoot = 15; + +trestlelegw = 10; +trestlebaseh = 10; +trestleplugd = 1; + +topblockthick = 3; +topblockbasedepth = 5; + +pinbasew = 4.0; +pinminh = 1.0; +pinmaxh = 3.5; +pindh = 1.75; +pindwidth = 1.75; + +pintaperlen = 20; +pinstraightlen = 30-pintaperlen; + +spoolouterpad = AxlePin_holerad()*2 * 1.5; +spoolbarlen = spoolwidth + + 2*(Washer_thick() + hubbasethick + AxlePin_holerad() + + spoolinnerslop + spoolouterpad); + barz = axlerad * 0.5; +axlepin_x = spoolwidth/2 + hubbasethick + + Washer_thick() + spoolinnerslop + AxlePin_holerad()*0.5; + +trestleheight = spoolouterrad + spoolradclear - barz; +trestlebase = trestleheight * 1.2; + +module Plug(d=0){ + dw = d; + dh = d; + dhb = d*2; + a = atan(plugslope); + bdy = -dhb; + bdx = dw / cos(a) + bdy * plugslope; + tdy = dh; + tdx = bdx + tdy * plugslope; + translate([-d,0,0]) rotate([90,0,90]) linear_extrude(height=plugl+0.1+d*2){ + polygon([[-(plugwmin/2 + bdx), bdy], + [-(plugwmax/2 + tdx), plugh + tdy], + [+(plugwmax/2 + tdx), plugh + tdy], + [+(plugwmin/2 + bdx), bdy]]); + } +} + +module Bar(){ ////toplevel + spoolw = spoolbarlen; + biggestw = spoolw + 50; + + intersection(){ + for (mir=[0,1]) { + mirror([mir,0,0]) { + translate([spoolw/2, 0, 0]) + Plug(); + translate([-1, -50, -50]) + cube([spoolw/2+1.1, 100, 100]); + } + } + difference(){ + translate([-biggestw/2, -50, 0]) + cube([biggestw, 100, 100]); + for (mir=[0,1]) { + mirror([mir,0,0]) + translate([axlepin_x, 0, -50]) + cylinder(r=AxlePin_holerad(), 100, $fn=15); + } + } + translate([0,0,barz]) { + translate([-100,0,0]) + rotate([0,90,0]) cylinder(r=axlerad, h=200, $fn=60); + } + } +} + +module FtAxlePin(){ ////toplevel + AxlePin(axlerad, (axlerad + barwasherrad*2)/3 * 2); +} + +module AxleWasher(){ ////toplevel + Washer(axlerad, barwasherrad); +} + +module Trestle(){ ////toplevel + legang = atan2(trestlebase/2, trestleheight); + eplen = sqrt(trestleheight*trestleheight + trestlebase*trestlebase*0.25); + topblockw = plugwmax + trestleplugd*2 + topblockthick*2; + + pinholebasew = pinbasew + pindwidth*2; + pinholeh = pinmaxh + pindh; + + difference(){ + union(){ + for (mir=[0,1]) { + mirror([mir,0,0]) { + rotate([0,0, -90-legang]) + ExtenderPillars(length=eplen+trestlelegw, + width=trestlelegw, + height=legw, + baseweb=true); + + translate([-trestlebase/2, -trestleheight, 0]) + cylinder(r=trestlelegw/2*1.2, h=trestlefoot); + } + } + translate([-topblockw/2, -topblockbasedepth, 0]) + cube([topblockw, + topblockbasedepth + plugh + topblockthick + + (pinmaxh - pinminh)*0.5, + plugl]); + + translate([-trestlebase/2, -trestleheight, 0]) + ExtenderPillars(length=trestlebase, width=trestlebaseh*2, height=legw); + } + translate([-300, -trestleheight-50, -1]) + cube([600, 50, 52]); + + rotate([-90,-90,0]) + Plug(d=trestleplugd); + + for (rot=[0,180]) { + translate([0,0,plugl/2]) rotate([0,rot,0]) translate([0,0,-plugl/2]) { + translate([0, + plugh - (pinmaxh - pinminh)*1.00, + (plugl - pinholebasew*2)/3]) { + translate([-(topblockw*0.25+1), 0, pinholebasew/2]) + rotate([-90,0,0]) %Pin(); + translate([-(topblockw+1), 0, 0]) { + rotate([0,90,0]) { + linear_extrude(height = topblockw*2.0+2) { + polygon([[-1.0 * pinholebasew, -0.01], + [-0.5 * pinholebasew, pinholeh], + [ 0 , -0.01]]); + } + } + } + } + } + } + } +} + +module Pin(){ ////toplevel + rotate([90,0,90]) { + hull(){ + for (mir=[0,1]) { + mirror([mir,0,0]) { + linear_extrude(height=0.1) { + polygon([[-0.01, 0], + [-0.01, pinminh], + [pinbasew*0.5*(pinminh/pinmaxh), 0]]); + } + translate([0,0,pintaperlen]) + linear_extrude(height=pinstraightlen) { + polygon([[-0.01, 0], + [-0.01, pinmaxh], + [pinbasew*0.5, 0]]); + } + } + } + } + } +} + +module HubEnd(){ ////toplevel + thick = hubmainthick+hubbasethick; + difference(){ + union(){ + for (ang=[0 : 60 : 359]) { + rotate([0,0,ang]) { + translate([hubmainrad - hubwalls/2, -hubbasestalkwidth/2, 0]) + cube([hubbaserad - (hubmainrad - hubwalls/2), + hubbasestalkwidth, hubbasethick]); + ExtenderPillar(length = hubmainrad-hubwalls/2, + height = hubbasethick + hubmainthick, + pillarw = hubpillarw); + } + } + cylinder(r=axlerad+hubwalls, h=thick); + cylinder(r=hubmainrad-0.1, h=hubbaseweb); + difference(){ + cylinder(r=hubmainrad, h=thick, $fn=100); + translate([0,0,-1]) + cylinder(r=hubmainrad-hubwalls, h=thick+2); + } + difference(){ + cylinder(r=hubbaserad, h=hubbasethick, $fn=50); + translate([0,0,-1]) + cylinder(r=hubbaserad-hubwalls, h=hubbasethick+2); + } + } + translate([0,0,-1]) + cylinder(r=axlerad+axleslop, h=thick+2, $fn=50); + } +} + + +module TestKit(){ ////toplevel + translate([60,0,0]) mirror([1,0,0]) Pin(); + translate([60,15,0]) mirror([1,0,0]) Pin(); + translate([0,40,0]) intersection(){ + Trestle(); + translate([-50,-10,-1]) cube([100,100,100]); + } + intersection(){ + translate([-60,10,0]) Bar(); + cube(50,center=true); + } + %translate([50,40, AxlePin_zoffset()]) FtAxlePin(); + %translate([0,-20,0]) AxleWasher(); +} + +module DemoSpool(){ + rotate([0,90,0]) translate([0,0,-spoolwidth/2]) + difference(){ + cylinder(r=spoolouterrad, h=spoolwidth); + translate([0,0,-1]) cylinder(r=spoolinnerrad, h=spoolwidth+2); + } +} + +module Demo(){ ////toplevel + color("blue") Bar(); + for (mir=[0,1]) { + mirror([mir,0,0]) { + color("red") translate([spoolbarlen/2,0,0]) + rotate([90,0,90]) Trestle(); + color("orange") + translate([spoolwidth/2 + hubbasethick + spoolinnerslop*2/3, 0, barz]) + rotate([0,90,0]) AxleWasher(); + color("orange") translate([axlepin_x, 0, barz]) + rotate([90,0,90]) FtAxlePin(); + color("cyan") + translate([spoolwidth/2 + hubbasethick + spoolinnerslop*1/3, 0, barz]) + rotate([0,-90,0]) HubEnd(); + } + } + %translate([0,0,barz]) DemoSpool(); +} + +//Bar(); +//FtAxlePin(); +//AxleWasher(); +//Trestle(); +//Pin(); +//TestKit(); +//Plug(d=1); +//ExtenderPillars(80,12,8, baseweb=true); +//HubEnd(); +//Demo(); diff --git a/fire-blanket-wall-mushroom.scad b/fire-blanket-wall-mushroom.scad new file mode 100644 index 0000000..dae4769 --- /dev/null +++ b/fire-blanket-wall-mushroom.scad @@ -0,0 +1,40 @@ +// -*- C -*- + +fudge=0.15; + +screwrad = 4.5 / 2 + fudge; // xxx check +shaftrad = 7.5 / 2 - fudge; + +diskrad = 12.0 / 2 - fudge; + +csinkpart = 0.5; + +shaftlen = 8; // xxx check + +diskthick = 1.5; + +disktaperratio = 2; + +// computed + +disktaperrad = diskrad - diskthick / disktaperratio; +totallen = shaftlen + diskthick; + +module SidePlan(){ + polygon([[-screwrad, 0], + [-disktaperrad, 0], + [-diskrad, -diskthick], + [-shaftrad, -diskthick], + [-shaftrad, -totallen], + [-screwrad, -totallen]]); +} + +module Bush(){ + rotate_extrude($fn=25, convexity=3){ + SidePlan(); + } +} + +//SidePlan(); +rotate([0,180,0]) + Bush(); diff --git a/fire-blanket-wall-mushroom.slic3r b/fire-blanket-wall-mushroom.slic3r new file mode 100644 index 0000000..2aa4f4a --- /dev/null +++ b/fire-blanket-wall-mushroom.slic3r @@ -0,0 +1 @@ +first_layer_extrusion_width = 100% diff --git a/floating-phases.scad b/floating-phases.scad new file mode 100644 index 0000000..b419918 --- /dev/null +++ b/floating-phases.scad @@ -0,0 +1,103 @@ +// -*- C -*- + +// caller should define +// +// z_pause = 2; // higher than any thing in the model +// total_sz = [ 200, 141 ]; +// phases=4; // how many phases +// colours = [ +// "blue", +// "black", +// "red", +// "yellow", +// ]; +// +// Pause height z_pause value from caller +// head park X 15 +// head park Y 0 +// head move Z 1 +// min head park Z 1 +// retraction 1 +// re-home [ ] [ ] + +// when z pause occurs +// * set feed rate to 10% (or whatever minimum is) +// * press knob, printer will start +// * quickly, press again, select "stop" +// * set temp +// * set feed rate back to 100% +// * now change filament etc., start next file + +include + +$test_thicker = 1.0; // 2.5; + +th_l0 = .425 * $test_thicker; +th_l1 = .250 * $test_thicker; +frame_w = 0.8; +tower_w = 2.0; + +th_most = th_l0 + th_l1*2; + +noz_gap = 8; + + +multicolour_gap = 0.15; // each side + +underlayer_smaller = 0.35; // each side + +total_sz_actual = $test ? $test : total_sz; + +module Interlayer_Bigger(){ + offset(r=multicolour_gap, $fn=20){ + union(){ children(); } + } +} + +module Interlayer_Smaller(){ + offset(r=-multicolour_gap, $fn=20){ + union(){ children(); } + } +} + +module Underlayer_Smaller(){ + offset(r=-underlayer_smaller, $fn=20){ + union(){ children(); } + } +} + +module FloatingPhaseFraming(phase, zmin) { + frame_inner = + total_sz_actual + + noz_gap * [2,2] * (phase > 0 ? phase : 0.5); + + frame_outer = + frame_inner + + frame_w * [2,2]; + + tower_pos = + -0.5 * frame_inner + + noz_gap * [1,0] * (phases - phase) + + -[1,0] * tower_w + 0.5 * [0,-1] * frame_w; + + // frame for alignment + linear_extrude(height= th_l0) + difference(){ + square(frame_outer, center=true); + square(frame_inner, center=true); + } + + // tower to avoid diagonal traverse to start position + linear_extrude(height= zmin + th_l1) + translate(tower_pos) + square([1,1] * tower_w); + + // trick to pause rather than finishing + if (phase != phases-1) + translate([0,0, z_pause+th_l1]) + linear_extrude(th_l1) + translate(tower_pos) + square([1,1] * tower_w); +} + +echo(str("SET PAUSE AT Z HEIGHT TO ",z_pause)); diff --git a/floating-test.scad b/floating-test.scad new file mode 100644 index 0000000..5f4000c --- /dev/null +++ b/floating-test.scad @@ -0,0 +1,19 @@ +// -*- C -*- + +frameth=0.8; + +l0= 0.425; +l1= 0.25; + +del=l0+l1*2; + +for (r=[0,90,180,270]) + rotate([0,0,r]) + translate([10,-(10+frameth),0]) + cube([frameth, 20+2*frameth, 0.4]); + +translate([-11,-11,0]) +cube([2,2, del + l1]); + +translate([-3,-3, del]) + cube([6,6, l1*2]); diff --git a/flyscreen-handle.scad b/flyscreen-handle.scad new file mode 100644 index 0000000..28753b7 --- /dev/null +++ b/flyscreen-handle.scad @@ -0,0 +1,258 @@ +// -*- C -*- + +opening_height = 7.84; +opening_depth = 7.88; +openingedge_dia = 2.00; +opening_protrh = 2.00; + +pivot_x = 6; +inside_len = 4; + +pivoting_gap = 0.1; + +outside_gap = 3; +outside_len = 13; +outend_height = 3; + +outside_len_bot = 23; + +outside_pushh = 4; +outside_pushslope = 1.4; +outside_push_inadj = 0.82; + +ourcirc_r = 0.5 / 2; + +ribble_dia = 2.2;; + +opening_protr_slop = 0.1; + +intooth_top_slop = 0.1; +inside_h_xgap = 1; + +pivot_r = 2; +pivot_slop = 0.25; + +strap_above = 0.1; +strap_th = 2.5; +strap_below = 3; +strap_width = 5; + +width = 35; +nstraps = 2; + +test_width = 5; + +// calculated + +inside_h = opening_height/2 - opening_protrh - inside_h_xgap/2; + +edge_or = openingedge_dia/2 + opening_protr_slop; + +Q0 = [ openingedge_dia/2, + openingedge_dia/2 + opening_height/2 ]; + +p4p5d = [edge_or + ourcirc_r, 0]; + +P0 = [ pivot_x, pivoting_gap ]; +P4 = Q0 - p4p5d; +P3t = [ P4[0], Q0[1] - openingedge_dia/2 + opening_protrh + - intooth_top_slop - ourcirc_r ]; +P2 = P4 + [ -(inside_len - ourcirc_r*2), 0 ]; +P1 = [ P2[0], P3t[1] - (inside_h + ourcirc_r*2) ]; + +P5 = Q0 + p4p5d; + +P8t = [ outside_len - ourcirc_r, P5[1] ]; +P9t = P8t + [ 0, -(strap_above + strap_th + strap_below - ourcirc_r*2) ]; + +P9b = [ P9t[0], -P9t[1] + outside_gap ]; +P8b = P9b + [ 0, outend_height ]; + +P89eadj = [ outside_len_bot - outside_len, 0 ]; +P8eb = P8b + P89eadj; +P9eb = P9b + P89eadj; + +P6t = P5 + [ 0, outside_pushh - ourcirc_r*2 ]; +P7 = [ P6t[0] + (P6t[1] - P1[1]) / outside_pushslope, + P1[1] ]; + +P3a = P3t + [ -outside_push_inadj, 0 ]; +P6a = P6t + [ -outside_push_inadj, 0 ]; + +outside_push_inadj_slope = (P3t[1]-P4[1]) / (P6a[1]-P5[1]); + +ribble_rad = ribble_dia/2; + +kit_adj_shift = -opening_height - 2.0; + +module ExtrusionSect(){ + cr = openingedge_dia/2; + toph = opening_height/2 + opening_protrh; + + for (my=[0,1]) { + mirror([0,my]) { + translate(Q0) { + hull(){ + circle(r=cr, $fn=20); + translate([-cr,10]) square([cr*2, 1]); + } + } + } + } + translate([-opening_depth, -toph]) { + difference(){ + translate([-5,-5]) + square([opening_depth+6, toph*2+10]); + square([opening_depth+2, toph*2]); + } + } +} + +module PsHull(ps) { + hull(){ + for (p = ps) { + translate(p) + circle(r = ourcirc_r, $fn=10); + } + } +} + +module LeverSect(top, inadj=false){ + P3 = inadj ? P3a : P3t; + P6 = inadj ? P6a : P6t; + P8 = top ? P8t : P8b; + P9 = top ? P9t : P9b; + difference(){ + union(){ + PsHull([P2,P3,P4]); + PsHull([P0,P1,P2,P5,P8,P9]); + } + hull(){ + for (dp = [ [0,0], + (P6-P5), + (P3-P4) + ]) { + translate(Q0 + 5*dp) circle(r=edge_or, $fn=20); + } + } + } +} + +module StrapSectTop(){ + translate(P9t + ourcirc_r * [+1,-1]) { + difference(){ + circle(r = strap_below + strap_th, $fn=40); + circle(r = strap_below, $fn=40); + } + } +} + +module StrapSectBot(inadj=false){ + mirror([0,1]){ + for (dx = [ -(strap_below + strap_th), + 0 ]) { + translate(P9b + [ ourcirc_r + dx, -10 ]) { + square([strap_th, 20]); + } + } + } +} + +module Ribbles(xmax, xmin, y){ + for (x = [ xmax + ourcirc_r - ribble_rad : + -ribble_rad * 4 : + xmin ]) { + translate([x, y]) + circle(r = ribble_rad, $fn=20); + } +} + +module LeverSectTop(){ + difference(){ + union(){ + LeverSect(true, false); + Ribbles(P8t[0], + Q0[0] + edge_or + ribble_rad*2, + P5[1] + ourcirc_r); + } + translate([pivot_x,0]) circle(r= pivot_r + pivot_slop, $fn=20); + } +} + +module LeverSectBot(inadj=false){ + P6 = inadj ? P6a : P6t; + mirror([0,1]) { + LeverSect(false, inadj); + PsHull([P5,P6,P7]); + PsHull([P8b,P8eb,P9eb,P9b]); + Ribbles(P8eb[0], + P9b[0], + P8eb[1]); + translate([pivot_x,0]) circle(r=pivot_r, $fn=20); + } +} + +module Demo(){ + translate([0,0,-5]) color("white") ExtrusionSect(); + LeverSectTop(); + translate([0,0,5]) LeverSectBot(); + color("black") LeverSectBot(true); + color("blue") translate([0,0,10]) StrapSectTop(); + color("purple") translate([0,0,-10]) StrapSectBot(); +} + +module SomeLever() { + // SomeLever(){ LeverBot(inadj); LeverSectBot(); } + difference(){ + linear_extrude(height=width, convexity=100) children(0); + for (i = [ 0 : nstraps - 1 ]) { + translate([0,0, (i + 0.5) / nstraps * width - strap_width/2]) + linear_extrude(height=strap_width, convexity=10) + children(1); + } + } +} + +module Test(){ + linear_extrude(height=test_width, convexity=100) { + translate([0,2,0]) LeverSectTop(); + LeverSectBot(); + translate([0,kit_adj_shift]) LeverSectBot(true); + } +} + +module LeverTop(){ ////toplevel + SomeLever(){ + LeverSectTop(); + StrapSectTop(); + } +} +module LeverBotOutside(){ ////toplevel + SomeLever(){ + LeverSectBot(); + StrapSectBot(); + } +} +module LeverBotInside(){ ////toplevel + SomeLever(){ + LeverSectBot(true); + StrapSectBot(true); + } +} + +module KitOutside(){ ////toplevel + translate([0,2,0]) LeverTop(); + LeverBotOutside(); +} + +module KitInside(){ ////toplevel + translate([0,2,0]) LeverTop(); + LeverBotInside(); +} + +//LeverSectBot(true); +//Demo(); +//LeverTop(); +//Test(); +//Kit(); +//KitInside(); diff --git a/flyscreen-wall-spacer.scad b/flyscreen-wall-spacer.scad new file mode 100644 index 0000000..4abe1ef --- /dev/null +++ b/flyscreen-wall-spacer.scad @@ -0,0 +1,100 @@ +// -*- C -*- + +include + +bracket_th = 2.70; +left_inboard_to_wall = 9.78; +right_inboard_to_wall = 13.21; + +plug_dia = 10; +screw_dia = 5; +bucket_wall = 2.5; +bucket_floor = 2.5; +whole_dia = plug_dia + bucket_wall *2; + +min_spacing = 8; +max_spacing = 19; + +general_spacer_height = 10; + +// calculated + +module Oval(r, dc) { + hull(){ + circle(r); + translate([0, dc]) + circle(r); + } +} + +module MainCircle() { + difference(){ + circle(r = whole_dia/2); + circle(r = screw_dia/2); + } +} + +module MultiSpacer() { + difference(){ + linextr(0, $inboard_to_wall - bracket_th){ + Oval(whole_dia/2, max_spacing); + } + + linextr(bucket_floor, 100) { + Oval(plug_dia/2, max_spacing); + } + + linextr(-1, 100) { + circle(screw_dia/2); + + translate([0, min_spacing]) + Oval(screw_dia/2, max_spacing - min_spacing); + } + } +} + +module AnySpacer(max_z) { + linextr(0, bucket_wall) + MainCircle(); + linextr(0, max_z){ + difference(){ + MainCircle(); + circle(r = plug_dia/2); + } + } +} + +module Spacer($inboard_to_wall) { + AnySpacer($inboard_to_wall - bracket_th); +} + +module Spacers1() { + for (dy = [0, 30]) { + translate([0,dy,0]) { + Spacer($inboard_to_wall = left_inboard_to_wall); + translate([0, 70, 0]) + Spacer($inboard_to_wall = right_inboard_to_wall); + } + } + + translate([40, 0, 0]) + MultiSpacer($inboard_to_wall = left_inboard_to_wall); + translate([40, 70, 0]) + MultiSpacer($inboard_to_wall = right_inboard_to_wall); +} + +module Spacers2() { + for (dy = 30 * [0]) { + echo(dy); + translate([0, dy, 0]) + AnySpacer(general_spacer_height); + } +} + +module Spacers3() { + AnySpacer(6.08); + translate([0, 30, 0]) + AnySpacer(8.18); +} + +Spacers3(); diff --git a/fruit-bowl-stand.scad b/fruit-bowl-stand.scad new file mode 100644 index 0000000..634f4d6 --- /dev/null +++ b/fruit-bowl-stand.scad @@ -0,0 +1,51 @@ +// -*- C -*- + +include + +across = 12.5; +r0 = 71; +h = 36; +feet = 5; + +// calculated + +r_eff_across = across/2 * cos(360/8/2); + +effective_corner = across/2 * [ sin(360/8/2), cos(360/8/2) ]; + +r_mid = r0 + effective_corner[0]; + +h_mid = h - effective_corner[1] - r_eff_across;; + +module XSection(){ + rotate(360/8/2) + circle(r= across/2, $fn=8); +} + +module Ring(){ + rotate_extrude($fa=1) + translate([r_mid, 0,0]) + XSection(); +} + +module Foot(){ + rotate([180,0,0]) + linear_extrude(h_mid) + XSection(); +} + +module Stand(){ + Ring(); + intersection(){ + for (a=[0:feet-1]) { + rotate([0,0, 360*a/feet]) + translate([-r_mid, 0,0]) + Foot(); + } + + linextr(-across - h, across) + circle(r= r_mid + r_eff_across, $fa=1); + } +} + +Stand(); diff --git a/hole-repair-20191117.scad b/hole-repair-20191117.scad new file mode 100644 index 0000000..0138c67 --- /dev/null +++ b/hole-repair-20191117.scad @@ -0,0 +1,49 @@ +// -*- C -*- + +post_dia = 23.0; +post_height = 20; + +th = 4; + +nom_hole = 25; +min_r = 15 + nom_hole/2; +maj_r = 15 + nom_hole/2; + +postwall_th = 2; + +$fa=1; +$fs=1; + +module Profile(r) { + polygon([ [0, 0], + [r, 0], + [r-th, th], + [0, th] ]); +} + +module Body(){ + hull(){ + for (x= [-1,+1] * (maj_r-min_r)) + translate([x,0,0]) + rotate_extrude() + Profile(min_r); + } +} + +module Post(){ + difference(){ + translate([0,0,-1]) + cylinder(r= post_dia/2, h= post_height+1); + translate([0,0,-2]) + cylinder(r= post_dia/2 - postwall_th, h= post_height+3); + } +} + +module Cover(){ + rotate([0,180,0]) Body(); + Post(); +} + +//Body(); +//Post(); +Cover(); diff --git a/hole-transfer-punch.scad b/hole-transfer-punch.scad new file mode 100644 index 0000000..d9e4859 --- /dev/null +++ b/hole-transfer-punch.scad @@ -0,0 +1,32 @@ +// -*- C -*- + +include + +dia = 6; + +point = 3; +solid = 3; + +$fa= 1; +$fs = 0.1; + +// calculated + +depth = dia * 1.5; + +module Blivet(){ + rotate_extrude(){ + polygon([[ 0,0 ], + [ point, 0 ], + [ point, solid ], + [ 0, solid + point]]); + } + linextr(0, solid){ + square(center=true, [ dia*3, dia ]); + } + linextr(-depth, solid){ + circle(r= dia/2); + } +} + +Blivet(); diff --git a/holetest.scad b/holetest.scad new file mode 100644 index 0000000..328ef2d --- /dev/null +++ b/holetest.scad @@ -0,0 +1,52 @@ +// -*- C -*- + +h=2; + +$fa=1; +$fs=0.1; + +label=true; + +spc= 7; +l = 50; +w = 10; + +lt = 0.5; +lw = 10; + +// calculated + +// Actual sizes (according to calipers) of things that fit +// A +// C - 1.88mm (M3 screw) +// E - 2.97mm (3mm HSS bit shank) +// G - +// I - 3.15mm tight fit (Yale padlock from extra padlocks tray) +// K - 3.33mm (M3.5 screw) + +ly0 = -w/2 -lw; + +difference(){ + union(){ + cube([l,w,h], center=true); + if (label) + translate([-l/2, ly0, -h/2]) + cube([l, lw, lt]); + } + + for (i=[0:2:10]) { + + sz = 3 + 0.5 * i/10; + + echo(sz); + translate([(i-5)/2 * spc, 0, -7 ]) { + cylinder(r= sz/2, h=14); + linear_extrude(height=14, convexity=100) { + translate([0, ly0 + lw * .2]) + text(halign="center", + size= lw * .6, font="DejaVu Sans:style=Bold", + chr(i + 65)); + } + } + } +} diff --git a/hotel-piece-model.scad b/hotel-piece-model.scad new file mode 100644 index 0000000..2f8af81 --- /dev/null +++ b/hotel-piece-model.scad @@ -0,0 +1,29 @@ +// -*- C -*- + +h = 15; +w = 20; +l = 30; +roof = 10; +eave = 1; +peakw = 1; +overhang = 3.0; +chimnd= 7; +chimnhr = 1.00; + +$fs = 0.1; + +module Hotel(){ + cube([w,l,h + 0.1]); + hull(){ + translate([0,0, h] + overhang * [-1,-1,0]) + cube([w,l,eave] + overhang * [2,2,0]); + translate([0,0, h] + overhang * [0,-1,0] + + (w-peakw) * 0.5 * [1,0,0]) + cube([peakw, l, roof] + overhang * [0,2,0]); + } + translate([w/4, l/2, h] + overhang * [-0.5, 0,0]) + cylinder(r= chimnd/2, h = roof * chimnhr); +} + +scale(1.11) +Hotel(); diff --git a/itx-aperture-grommet.scad b/itx-aperture-grommet.scad new file mode 100644 index 0000000..53f74fa --- /dev/null +++ b/itx-aperture-grommet.scad @@ -0,0 +1,56 @@ +// -*- C -*- + +include +include + +ap_width = 21.30; +ap_height = 16.45; +tot_height = 22.74 + 1.0; +screw_ctr_to_tr = [ 7.89, 3.87 ]; + +tab_th = 2.5; +wall_th = 1; +wall_h = 2; + +app_slop = 0.60; // total across both sides + +// calculated + +tab_h = tot_height - ap_height; + +real_main_sz = [ ap_width, ap_height ] - app_slop * [ 1,1 ]; +real_all_sz = real_main_sz + tab_h * [0,1]; +real_inner_sz = real_main_sz - wall_th * [ 2,1 ]; + +screw_pos = real_all_sz - (screw_ctr_to_tr - 0.5 * app_slop * [1,1]); + +module GapPlan() { + rectfromto([ wall_th, -1 ], + real_main_sz - wall_th * [1,1]); +} + +module MainPlan() { + rectfromto([0,0], + real_main_sz); +} + +module AllPlan() { + rectfromto([0,0], real_all_sz); +} + +module Grommet(){ ////toplevel + difference(){ + union(){ + linextr(0, tab_th + wall_h) MainPlan(); + linextr(0, tab_th) AllPlan(); + } + linextr(-1, tab_th + wall_h + 1) GapPlan(); + translate(concat(screw_pos, [-1])) + english_thread(diameter = 0.1380, + threads_per_inch = 32, + length = tab_th + 2, + internal = true); + } +} + +Grommet(); diff --git a/keyring-kay.scad b/keyring-kay.scad new file mode 100644 index 0000000..9ebb34a --- /dev/null +++ b/keyring-kay.scad @@ -0,0 +1,59 @@ +// -*- C -*- + +letterheight = 25; +linewidth = 3.5; +letterthick = 2.5; + +basethick = 2.5; + +xborder = 5; +yborder = 5; + +kdiag = 1; +kprop = 0.50; + +diaglinewidth = linewidth * sqrt(1 + kdiag*kdiag); + +ringholerad = 2.5; +ringedgewidth = 3; + +module kay_leg (transamount, llen, mir) { + translate([0,transamount,0]) + mirror([0,mir,0]) + translate([0,-0.1,0]) + multmatrix([[1,kdiag,0,0], + [0,1,0,0], + [0,0,1,0], + [0,0,0,1]]) + cube([diaglinewidth, llen + 0.1, letterthick]); +} + +module kay () { + translate([0.1,0,0]) + cube([linewidth, letterheight, letterthick]); + kay_leg(letterheight*kprop, letterheight*(1-kprop), 0); + kay_leg(letterheight*kprop, letterheight*kprop, 1); +} + +totalw = letterheight*kprop + diaglinewidth + xborder*2; +totalh = letterheight + yborder*2; +basez = -(basethick-0.1); + +module main () { + kay(); + translate([-xborder, -yborder, basez]) + cube([totalw, totalh, basethick]); +} + +module ring (rad, extra) { + translate([totalw/2 - xborder, totalh - yborder, basez-extra]) + cylinder(r=rad, h=basethick + extra*2, $fn=30); +} + +difference(){ + union() { + main(); + ring(ringholerad + ringedgewidth/2, 0); + } + ring(ringholerad, 1); +} diff --git a/knifeblock,BlockPrint.auto.slic3r b/knifeblock,BlockPrint.auto.slic3r new file mode 100644 index 0000000..22dacf8 --- /dev/null +++ b/knifeblock,BlockPrint.auto.slic3r @@ -0,0 +1 @@ +fill_density = 0.1 diff --git a/knifeblock-knives-filter b/knifeblock-knives-filter new file mode 100755 index 0000000..17cff74 --- /dev/null +++ b/knifeblock-knives-filter @@ -0,0 +1,36 @@ +#!/usr/bin/perl -w + +use strict; +use POSIX; + +our %want; + +our $nknives = 3; + +our @part_order = qw(h b l); + +my $want = shift @ARGV; +my ($wknife,$wparts) = $want =~ m/^(\d)([a-z]+)$/ or die; + +sub want ($) { + my ($colournum) = @_; + my $knife = $nknives-1 - ($colournum-1) % $nknives; + return 0 unless $knife == $wknife; + my $part = $part_order[ floor(($colournum-1) / $nknives) ]; + die "huh colour $colournum?" unless defined $part; + return 0 unless $part =~ m/[$wparts]/o; + print STDERR "$0: including colour $colournum ($knife $part)\n"; + return 1; +} + +our $drop; +while (<>) { + if (m/^\S/) { + $drop = + m/^2 5 / || + (m/^(?:3 1|2 3) \d+ \d+ (\d+) / && !want($1)); + } + next if $drop; + + print or die $!; +} diff --git a/knifeblock-knives-photo.jpg b/knifeblock-knives-photo.jpg new file mode 100644 index 0000000..0d757cc Binary files /dev/null and b/knifeblock-knives-photo.jpg differ diff --git a/knifeblock-knives-trace.fig b/knifeblock-knives-trace.fig new file mode 100644 index 0000000..c42c2c6 --- /dev/null +++ b/knifeblock-knives-trace.fig @@ -0,0 +1,66 @@ +#FIG 3.2 Produced by xfig version 3.2.5b +Landscape +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +2 5 0 1 0 -1 50 -1 -1 0.000 0 0 -1 0 0 5 + 0 knifeblock-knives-photo.jpg + 585 270 11557 270 11557 8499 585 8499 585 270 +2 3 1 1 9 7 30 -1 -1 4.000 0 0 7 0 0 6 + 3093 6395 6633 6470 10898 6641 5808 7070 5818 6778 3093 6395 +2 3 1 1 8 7 30 -1 -1 4.000 0 0 7 0 0 6 + 3038 4586 6578 4661 10843 4832 5753 5261 5763 4969 3038 4586 +2 3 1 1 7 7 30 -1 -1 4.000 0 0 7 0 0 6 + 2978 2996 6518 3071 10783 3242 5693 3671 5703 3379 2978 2996 +3 1 0 1 2 7 40 -1 -1 0.000 0 0 0 21 + 5858 5445 6063 5290 6283 5115 6523 4865 6658 4670 6768 4510 + 6758 4405 6438 4365 5923 4315 5423 4295 4823 4290 4418 4300 + 4003 4335 3648 4380 3593 5170 3883 5190 4368 5185 4598 5160 + 4993 5075 5718 5365 5838 5435 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 +3 1 0 1 3 7 40 -1 -1 0.000 0 0 0 23 + 5878 6218 5318 6198 4858 6198 4463 6208 4118 6223 3908 6258 + 3908 6953 4293 6978 4668 6938 5078 6873 5693 7028 5718 7113 + 5783 7143 5873 7128 6028 7028 6243 6833 6453 6608 6583 6458 + 6638 6398 6598 6348 6548 6303 6418 6283 6033 6243 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 +3 1 0 1 1 7 40 -1 -1 0.000 0 0 0 20 + 5918 3753 6120 3555 6435 3285 6615 3060 6840 2745 6975 2520 + 6975 2475 6615 2430 5985 2385 5490 2355 4950 2340 4498 2360 + 3883 2385 3458 2425 3443 3430 3838 3435 4248 3410 5593 3723 + 5783 3858 5773 3848 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 +3 1 0 1 4 7 40 -1 -1 0.000 0 0 0 23 + 9223 2689 8811 2659 8398 2621 7993 2606 7603 2584 7228 2569 + 6861 2561 5871 2539 5121 3529 5818 3694 5991 3769 6583 3776 + 7206 3769 8113 3746 8826 3709 9298 3649 9816 3581 10311 3431 + 10737 3204 10702 3099 10551 3026 10243 2869 9606 2734 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 +3 1 0 1 5 7 40 -1 -1 0.000 0 0 0 23 + 7191 5366 8098 5329 8586 5291 9051 5194 9298 5119 9652 4993 + 9802 4898 9812 4828 9722 4748 9283 4676 9028 4616 8706 4594 + 8278 4549 7851 4534 7371 4534 6883 4511 6141 4511 5998 4744 + 5661 4969 5593 5366 6043 5351 6478 5366 7026 5374 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 +3 1 0 1 6 7 40 -1 -1 0.000 0 0 0 21 + 6816 7069 7191 7047 7558 6994 7896 6927 8097 6852 8277 6772 + 8267 6677 8067 6622 7881 6544 7611 6499 7356 6454 7093 6432 + 6838 6409 6561 6409 6096 6387 5901 6454 5578 6784 5668 7024 + 5811 7069 6141 7084 6388 7077 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 diff --git a/knifeblock.scad b/knifeblock.scad new file mode 100644 index 0000000..c1f1869 --- /dev/null +++ b/knifeblock.scad @@ -0,0 +1,346 @@ +// -*- C -*- + +// properties of the knives +nknives = 3; +widths = [15.5, 15.8, 19.0]; +handlelenbase = 75; +handlelendelta = [-15, 0, 10]; +locations = [-35, 0, 37]; +bladew = 5; // 2.5 +maxhandledepth = 45; + +templatescale = 27.2 / 19.6; + +coverlonglen = 120; // xxx +covershortlen = 70; // xxx + +// other tuneables +front = 5; +back = 5; +height = 50; +minsidein = 4; +minsideout = 4; + +frontbackslop = 0.25; + +knifewidthslop = 2.0; + +screwbackdepth = 6.0 - 1.0; +screwdia = 4.0 + 0.5; +screwcsinkdia = 9.8 + 1.0; + +screwabove = 15; + +coverthick = 2.4; +coverside = coverthick; + +covertopwing = 15; +covertopwingbase = 20; +coveredge = 3; + +holesize = 12.5; +holestrut = 7; +holeedge = 4; + +holeoffx = 0.33; +holeoffy = 0.23; + +indentdepth = 1; +indentoutersize = holesize + 2.15; +indentinnersize = indentoutersize - indentdepth * 3.0; + +pegstem = 3.5; +peghead = 10; +pegstemheight = 2; +pegheight = 9; +peglen = 12; + +recessblockwidth = peghead + pegstem*3; +recessblockheight = peglen/2 + pegstem*1.5; + +pegsloph = 0.5; +pegslopv = 0.5; +pegslopl = 0.5; + +pegdepthproportion = 0.80; + +// computed + +function width(k) = widths[k] + knifewidthslop; + +side = minsidein + screwcsinkdia + minsideout; +totaldepth = front + maxhandledepth + back; + +minkx = locations[0] - width(0) /2; +maxkx = locations[nknives-1] + width(nknives-1)/2; + +minx = minkx - side; +maxx = maxkx + side; + +holepitch = holesize+holestrut; + +pegrecess = pegdepthproportion*totaldepth - 0.5*peglen; + +module ImportTemplate(w,k,t) { + fn = str("knifeblock-knives-t",k,t,".dxf"); + echo(fn); + translate([0,0, -w/2]) + linear_extrude(height=w) + scale(templatescale) import(file=fn, convexity=100); +} + +module Knife(k){ + ImportTemplate(bladew, k,"bl"); + hull(){ + ImportTemplate(width(k), k,"hl"); + translate([-100,0,0]) + ImportTemplate(width(k), k,"hl"); + } +} + +module DoKnife(k){ + translate([locations[k],0,0]){ + rotate([0,90,0]) + translate([-(handlelenbase + handlelendelta[k]),0,0]) + Knife(k); + } +} + +module DoKnives(){ + for (k=[0:nknives-1]) + DoKnife(k); +} + +module ScrewHole(){ + translate([0,-50,0]) + rotate([-90,0,0]) + cylinder(r=screwdia/2, h=150, $fn=40); + translate([0, totaldepth-front - screwbackdepth, 0]) + rotate([90,0,0]) + cylinder(r=screwcsinkdia/2 / (sqrt(3)/2), h=100, $fn=6); +} + +module PegTemplate(apex){ + for (mx=[0,1]) for (my=[0,1]) { + mirror([mx,0,0]) mirror([0,my,0]) + polygon([[-0.1, -0.1], + [pegstem/2, -0.1], + [pegstem/2, pegstemheight/2], + [peghead/2, pegheight /2], + [-0.1, pegheight /2 + apex]]); + } +} + +module AtSides(){ + translate([minx,0,0]) child(0); + translate([maxx,0,0]) mirror([1,0,0]) child(1); +} + +module BlockPegSlot(){ + translate([recessblockwidth/2, pegrecess - peglen, -height]){ + rotate([-90,0,0]) linear_extrude(height=totaldepth){ + PegTemplate(peghead/2 * 1.2); + } + } +} + +module DecorativeIndents(){ + translate([0, -front, 0]) + rotate([90,0,0]) + HexGrid(-height, 0, minx,maxx) { + hull(){ + translate([0, 0, -indentdepth]) + cylinder(r=indentinnersize/2, h=indentdepth, $fn=40); + cylinder(r=indentoutersize/2, h=indentdepth, $fn=40); + } + } +} + +module Block(){ + sidemidx = minsideout + screwcsinkdia/2; + + difference(){ + mirror([0,0,1]) { + translate([minx, -front, 0]) + cube([maxx-minx, totaldepth, height]); + } + for (x=[minx + sidemidx, maxx - sidemidx]) { + translate([x, 0, -screwabove]) + ScrewHole(); + } + for (yshift=[-1,1]) + translate([0, yshift * frontbackslop, 0]) + DoKnives(); + AtSides() { BlockPegSlot(); BlockPegSlot(); } + DecorativeIndents(); + } +} + +module BlockPrint(){ ////toplevel + rotate([0,0,90]) + Block(); +} + +module CoverTemplate(){ + linear_extrude(height=coverthick) + polygon([[minx, 0], + [maxx, 0], + [maxx, coverlonglen+0.1], + [maxx - coverside, coverlonglen+0.1], + [minx, covershortlen+0.1]]); +} + +module CoverSide(len){ + translate([0, 0 ,0]) { + rotate([90,0,90]) + linear_extrude(height=coverside) + polygon([[0, 0], + [0, totaldepth], + [covertopwing, totaldepth], + [covertopwingbase, coverside + coverthick], + [len - covertopwingbase, coverside + coverthick], + [len - covertopwing, totaldepth], + [len, totaldepth], + [len, 0]]); + cube([recessblockwidth, recessblockheight, totaldepth]); + } +} + +module Peg(){ + echo("peg angle slope (must be <1)", + (peghead-pegstem)/(pegheight-pegstemheight)); + dx = pegsloph; + dy = pegslopv; + rotate([90,0,0]) { + linear_extrude(height=peglen-pegslopl) { + intersection(){ + translate([-dx,-dy,0]) PegTemplate(0); + translate([-dx,+dy,0]) PegTemplate(0); + translate([+dx,+dy,0]) PegTemplate(0); + translate([+dx,-dy,0]) PegTemplate(0); + } + } + } +} + +module CoverPegSlot(coverlen){ + translate([recessblockwidth/2, 0, -1]){ + linear_extrude(height= 1 + pegrecess + 0.5*peglen){ + PegTemplate(0); + } + } +} + +module HolesScope(){ + intersection_for (dx=[-1,+1]) { + intersection_for (dy=[-1,+1]) { + translate([dx * holeedge, dy * holeedge, -5]) + scale([1,1,10]) + CoverTemplate(); + } + } +} + +module HexGrid(xa,xb,ya,yb) { + imin = floor(xa / holepitch); + imax = ceil(xb / holepitch); + jmin = floor(ya / (sqrt(3)*holepitch)); + jmax = ceil(yb / (sqrt(3)*holepitch)); + echo("HexGrid ",imin,imax,jmin,jmax); + for (i=[imin:imax]) { + for (j=[jmin:jmax]) { + translate([(j * sqrt(3) + holeoffx) * holepitch, + (i + 0.5 + holeoffy) * holepitch, + 0]) { + child(); + translate([sqrt(3)/2 * holepitch, -0.5 * holepitch, 0]) + child(); + } + } + } +} + +module Hole(){ + cylinder(r=holesize/2, h=40, $fn=40); +} + +module Holes(){ + intersection(){ + translate([0, 0, -20]) + HexGrid(0, coverlonglen, minx, maxx) + Hole(); + HolesScope(); + } +} + +module CoverCover(){ + difference(){ + CoverTemplate(); + Holes(); + } +} + +module Cover(){ + difference(){ + union(){ + CoverCover(); + AtSides() { CoverSide(covershortlen); CoverSide(coverlonglen); } + } + AtSides() { CoverPegSlot(); CoverPegSlot(); } + } +} + +module CoverAligned(){ + translate([0,-front,-height]) + rotate([-90,0,0]) + Cover(); +} + +module DemoPeg(){ + translate([recessblockwidth/2, pegrecess, -height]) + Peg(); +} + +module Demo(){ ////toplevel + %Block(); + DoKnives(); + color([0,0,1]) CoverAligned(); + color([1,0,0]) AtSides() { DemoPeg(); DemoPeg(); } +} + +module Pegs(){ ////toplevel + Peg(); + translate([-peghead-3, 0,0]) Peg(); +} + +module CoverParts(){ ////toplevel + Cover(); + translate([0, coverlonglen, pegheight/2-pegslopv]) + Pegs(); +} + +module FrontDemo(){ ////toplevel + color([1,0,1]) Block(); + color([1,0,1]) CoverAligned(); + color([0,0,0]) DoKnives(); +} + +module BlockFrontTest(){ ////toplevel + intersection(){ + Block(); + translate([minx-10, -front-10, -height-10]) { + cube([75,14,35]); + cube([75,25,13]); + } + } +} + +//Block(); +//Demo(); +//Cover(); +//CoverParts(); +//Peg(); +//Cover(); +//Holes(); +//%CoverTemplate(); +//Pegs(); diff --git a/ksafe-base.scad b/ksafe-base.scad new file mode 100644 index 0000000..c2e2704 --- /dev/null +++ b/ksafe-base.scad @@ -0,0 +1,373 @@ +// -*- C -*- + +// from actual ksafe +bolt_above = 8.50 - 0.50; +bolthole_height = 4.24 + 1.00; +wall_thick = 4.50; +bolthole_width = 16.62 + 1.00; +main_sz = 149.06 - 0.25; +cnr_rad = 19.5; +lidinner_thick_allow = 20.78 + 0.50; +display_width = 69.81 - 0.50; + +dpp3 = [ -5.5, 8.5 ]; +dpp2 = [ -11.0, 7.0 ]; +dpp1 = [ -34.0, 14.0 ]; + +// other parameters +web_thick = 4; +web_height = 20; // excluding wall and base thick +bolthole_rhs = 20; +bolthole_lhs = 20; +boltreinf_thick = 6; +anchor_wall_space = 25; +base_thick = 4; +space = 25; +anchor_thick = 4; +anchor_rad = 4; +bevel = 8; +string_slot = 3.0; +string_depth = 6.0; +thumbslot_depth = 5.0; +thumbslot_width = 15.0; +thumbslot_between = 10; +ksafecover_lip = 4.62; +display_rhs_secs = 15; +dcover_endthick = 3.0; +dcover_mainthick = 5.0; +dcover_slop_height = 0.35; +dcover_slop_depth = 0.25; +dcover_slop_inside = 1.50; +dcover_commonvertoff = 0.00; // slop_height or slop_inside is added too +dcover_edge_gap_more_width = 2.0; // each side + +// ----- calculated ----- + +hsz = main_sz/2; +cut = main_sz + 20; + +gppA = [0,0]; +gppB = gppA - [ wall_thick, 0 ]; + +gppL = [ gppB[0], -(lidinner_thick_allow + space + base_thick) ]; + +yw1 = -web_thick/2; +yw2 = yw1 - bolthole_rhs; +yw3 = yw2 - anchor_thick; +yw4 = yw3 - anchor_wall_space; +yw5 = yw4 - wall_thick; +yw6 = -(hsz - cnr_rad + 0.1); + +yw10 = web_thick/2; +yw11 = yw2 + anchor_wall_space; +yw12 = yw11 + wall_thick; +yw13 = -yw6; + +cpp1 = dpp1 + [ dcover_slop_depth, dcover_slop_height ]; +cpp2 = [ dpp2[0] - dcover_slop_depth, dpp3[1] + dcover_slop_height ]; +cppH = cpp1 + [ 0, dcover_endthick ]; +cppA = [ cpp2[0], dpp3[1] + dcover_slop_inside ]; +cppK = cppA + [ 0, dcover_mainthick ]; +cppZ = [ -ksafecover_lip, -dcover_commonvertoff ]; +cppD = cppZ + [ 0, -dcover_slop_inside ]; +cppC = [ dcover_slop_inside, cppD[1] ]; +cppF = cppC + dcover_mainthick * [1,-1]; +cppB = [ cppC[0], cppA[1] ]; +cppG = [ cppF[0], cppK[1] ]; +cppE = [ cppD[0], cppF[1] - (cppF[0] - cppD[0]) ]; + +// anchor + +anchor_b = anchor_thick + anchor_rad; +appM = gppL + anchor_b * [1,1]; + +a_bevel = 2 * anchor_b * (1 + sqrt(0.5)); + +module upp_app_Vars(t_bevel){ + $xppE = gppL + t_bevel * [0,1]; + $xppF = gppL + t_bevel * [1,0]; + + $xppJ = $xppE + wall_thick * [ 1, tan(22.5) ]; + $xppI = $xppF + base_thick * [ tan(22.5), 1 ]; + + // must pass a_bevel for t_bevel for these to be valid + $gppP = gppA + [0,-1] * lidinner_thick_allow; + $gppQ = $gppP + [1,-1] * web_height; + $gppR = $xppJ + [ 1, tan(22.5) ] * web_height; + $gppS = $xppI + [ tan(22.5), 1 ] * web_height; + $gppT = [ $gppQ[0], $xppE[1] ]; + + children(); +} + +module upp_app_Profile(){ + polygon([ gppA, + gppB, + $xppE, + $xppF, + $xppF + [1,0], + $xppI + [1,0], + $xppJ ], + convexity=10); +} + + +module UsualProfile(){ + upp_app_Vars(bevel) upp_app_Profile(); +} + +module NearAnchorProfile(){ + upp_app_Vars(a_bevel) upp_app_Profile(); +} + +module AnchorProfile(){ + upp_app_Vars(a_bevel) { + + upp_app_Profile(); + + difference(){ + hull(){ + polygon([ $xppE, + $xppF, + $xppF + [0,1], + $xppE + [1,0] ], + convexity=10); + translate(appM) circle(r= anchor_b); + } + translate(appM) circle(r= anchor_rad); + } + } +} + +module AnchorWallProfile(){ + UsualProfile(); + NearAnchorProfile(); + hull(){ + for (bev = [bevel, a_bevel]) { + upp_app_Vars(bev) { + polygon([ $xppE, + $xppF, + $xppI, + $xppJ ], + convexity=10); + } + } + } +} + +module WebProfile(){ + upp_app_Vars(a_bevel){ + if ($gppR[1] <= $gppP[1]) { + polygon([ $gppP, + $xppE, + $gppT, + $gppQ ]); + polygon([ $gppP, + $xppE, + $xppF, + $gppS, + $gppR ], + convexity=10); + } else { + polygon([ $gppP, + $xppE, + $xppF, + $gppS, + $gppP + web_height * [1,0] ], + convexity=10); + } + polygon([ $gppS, + $xppF, + $xppF + [1,0], + $gppS + [1,0] ], + convexity=10); + } +} + +module SomeBaseProfile(I, F){ + polygon([ I, + F, + [ hsz+1, F[1] ], + [ hsz+1, I[1] ] ]); +} + +module BaseProfile(){ + SomeBaseProfile($xppI, $xppF); +} + +module DCoverProfileRaw(){ + polygon([ cpp1, + cpp2, + cppA, + cppB, + cppC, + cppD, + cppE, + cppF, + cppG, + cppK, + cppH ], + convexity = 10); +} + +module DCoverProfile(){ + mirror([1,0]) + translate(-cppZ) + DCoverProfileRaw(); +} + +module SWalls(ymin, ymax, t_bevel) { + upp_app_Vars(t_bevel) { + translate([0,ymin,0]) + mirror([0,1,0]) + rotate([90,0,0]) + linear_extrude(height= ymax-ymin, convexity=10) + for (xm=[0,1]) + mirror([xm,0]) + translate([-hsz, 0]) + children(); + } +} + +module AtTwoCorners(){ + for (xm=[0,1]) { + mirror([xm,0,0]) + translate((hsz - cnr_rad) * [1,1]) + intersection(){ + rotate_extrude(convexity=10) + translate([-cnr_rad,0]) + children(); + translate([0,0,-250]) + cube([50,50,500]); + } + } +} + +module Box(){ + /// corners, and front and back of base + for (ym=[0,1]) mirror([0,ym,0]) { + AtTwoCorners(){ + UsualProfile(); + } + hull() AtTwoCorners(){ + upp_app_Vars(bevel){ + polygon([ $xppI, + $xppF, + $xppF + [0.1, 0], + $xppI + [0.1, 0] + ]); + } + } + } + + // side walls and base + SWalls(yw6 , yw4 , bevel ) { UsualProfile(); BaseProfile(); } + SWalls(yw5 , yw4 , a_bevel) { AnchorWallProfile(); BaseProfile(); } + SWalls(yw5 , yw12, a_bevel) { NearAnchorProfile(); BaseProfile(); } + SWalls(yw3 , yw2 , a_bevel) { AnchorProfile(); BaseProfile(); } + SWalls(yw11, yw12, a_bevel) { AnchorWallProfile(); BaseProfile(); } + SWalls(yw11, yw13, bevel ) { UsualProfile(); BaseProfile(); } + SWalls(yw1, yw10, a_bevel) { WebProfile(); SomeBaseProfile($gppS, $xppF); } + + // front and rear walls + rotate([0,0,90]) SWalls(yw6, yw13, bevel) UsualProfile(); +} + +module DCover(){ ////toplevel + translate([ -display_width/2, -hsz, 0 ]) + rotate([0,90,0]) + rotate([0,0,90]) + linear_extrude( display_width - display_rhs_secs, convexity = 10) + DCoverProfile(); +} + +module DCoverSupportAllowance(){ + translate([0, -hsz, 0]) + cube(center=true, + [ display_width + 2 * dcover_edge_gap_more_width, + wall_thick * 2, + dcover_slop_inside * 2 + 0.01 ]); +} + +module BoltHoles(){ + translate([0,0, -(bolt_above + 0.5 * bolthole_height)]) + cube(center=true, [ cut, bolthole_width, bolthole_height ]); +} + +module KsafeBase(){ ////toplevel + DCover(); + + difference(){ + Box(); + + BoltHoles(); + + // string slot + translate([ -cut, + -(bolthole_width/2 + bolthole_rhs), + 1 ]) + mirror([0,1,0]) mirror([0,0,1]) + cube([ cut*2, + string_slot, + lidinner_thick_allow + string_depth + 1 ]); + + // thumb slots + for (mx=[0,1]) mirror([mx,0,0]) { + translate([ thumbslot_between/2, + 0, + -thumbslot_depth ]) + cube([ thumbslot_width, + cut, + thumbslot_depth+1 ]); + } + + DCoverSupportAllowance(); + } +} + +module DemoProfiles(){ ////toplevel + translate([0,0,-2]) color("yellow") AnchorWallProfile(); + color("red") AnchorProfile(); + translate([0,0,2]) color("black") NearAnchorProfile(); + translate([0,0,4]) color("blue") UsualProfile(); + translate([0,0,-4]) color("pink") WebProfile(); + translate([0,0,6]) color("purple") DCoverProfile(); +} + +module RimTest(){ ////toplevel + intersection(){ + Box(); + cube(center=true, [ main_sz*2, main_sz*2, + 2.5 ]); + } +} + +module DCoverTest(){ ////toplevel + intersection(){ + difference(){ + union(){ + Box(); + DCover(); + } + DCoverSupportAllowance(); + BoltHoles(); + } + translate([0,0,60]) + cube(center=true, [ main_sz*2, main_sz*2, + 2 * (60 + 10) ]); + } +} + +module BoltTest(){ ////toplevel + dy = 0.5 * (bolthole_width+4); + intersection(){ + KsafeBase(); + translate([ 0, -dy, -(bolt_above + bolthole_height + 1) ]) + cube([ main_sz, dy*2, 50 ]); + } +} + +//DemoProfiles(); +//Box(); +//KsafeBase(); +//RimTest(); diff --git a/laptop-camera-tripod-bracket.scad b/laptop-camera-tripod-bracket.scad new file mode 100644 index 0000000..cc6ffb2 --- /dev/null +++ b/laptop-camera-tripod-bracket.scad @@ -0,0 +1,76 @@ +// -*- C -*- + +// High Detail +// support enabled +// distance x/y 2.5mm +// fill density 30% + +laptop_w = 310; +laptop_th_rear = 14; +laptop_th_front = 11; +laptop_halfdepth_real = 125; +laptop_halfdepth = 115; //125; +laptop_inner_x_clear = 95; + +include +include + +bar_w = 15; +bar_th = 12; +flex_allow = 3; +claw_th = 6; +claw_overlap = 15; +mount_dia = 50; + +min_ceil = 1; + +//$test=true; +$test=false; + +module ClawProfile(laptop_th){ + laptop_zmin = bar_th + flex_allow; + laptop_zmax = laptop_zmin + laptop_th; + difference(){ + rectfromto([-claw_overlap, 0], + [claw_th, laptop_zmax + claw_th]); + rectfromto([-claw_overlap-1, laptop_zmin ], + [ 0, laptop_zmax ]); + } +} + +module ClawBar(inner_len, laptop_th){ + rotate([0,0,180]) linextr_y_xz(-bar_w/2, +bar_w/2) { + rectfromto([0,0], + [inner_len, bar_th]); + translate([inner_len, 0]) + ClawProfile(laptop_th); + } +} + +module Body(){ + translate([0, laptop_halfdepth - laptop_halfdepth_real, 0]) + for (m=[0,1]) { + mirror([m,0]) { + ClawBar(laptop_w/2, laptop_th_rear); + translate([ laptop_w/2 - laptop_inner_x_clear + bar_w/2, 0]) + rotate([0,0,-90]) + ClawBar(laptop_halfdepth, laptop_th_front); + } + } + cylinder(r = mount_dia/2, h= bar_th); +} + +module Bracket(){ + difference(){ + Body(); + rotate([0,180,0]) + CameraMountThread( bar_th + 2 ); + } + translate([0,0, bar_th-min_ceil]) + cylinder(r= mount_dia/2-1, h=min_ceil); +} + +//ClawProfile(laptop_th_front); +//ClawBar(125, laptop_th_front); +//Body(); +Bracket(); diff --git a/laptop-sound-cable-hooks.scad b/laptop-sound-cable-hooks.scad new file mode 100644 index 0000000..8cfd468 --- /dev/null +++ b/laptop-sound-cable-hooks.scad @@ -0,0 +1,145 @@ +// -*- C -*- + +include + +wall_th = 2; +hook_th = 4; +hook_hole = 4; +hook_w_min = 6; +hook_hook = 12; + +plug_entry_gap = 2.0; + +plug_l_d = [[ 27.78, + 10.62 + 0.75 ], + [ 40.88, + 8.56 + 0.75 ], + ]; + +plug_stem = [ 2.72 + 0.50, + 5.20 + 0.50 ]; + +palmrest_from_plug_z = 3.98; +laptop_th = 16.31 + 0.75; + +tongue_len = 50; + +// calculated + +hook_th_plug_holder = + plug_l_d[0][1]/2 + wall_th * sin(22.5); + +hook_tongue_h = hook_hole + wall_th*2; + +plug_l_d_smallest = plug_l_d[len(plug_l_d)-1]; +plug_hook_x_min = -plug_l_d_smallest[0] - wall_th; +plug_hook_z_start = -plug_l_d_smallest[1]/2 - wall_th; + +z_laptop_base = palmrest_from_plug_z - laptop_th; +z_hook_min = z_laptop_base - hook_tongue_h; + +module PlugMainPlan() { + for (l_d = plug_l_d) { + l = l_d[0]; + d = l_d[1]; + rectfromto([ -l, -d/2 ], + [ 0, +d/2 ]); + } +} + +module PlugHolderPlan() { + intersection(){ + hull() + offset(r= wall_th) + PlugMainPlan(); + + rectfromto([-100,-100], [-plug_entry_gap,+100]); + } +} + +module PlugHookHookPlan(){ + polygon([ [ plug_hook_x_min, 0 ], + [ plug_hook_x_min, plug_hook_z_start ], + [ plug_hook_x_min + (plug_hook_z_start - z_hook_min), + z_hook_min ], + [ -plug_entry_gap, z_hook_min ], + [ -plug_entry_gap, 0 ], + ]); +} + +module TonguePlan(){ + difference(){ + rectfromto([ -plug_entry_gap - 1, z_hook_min ], + [ tongue_len, z_laptop_base ]); + translate([ tongue_len - wall_th - hook_hole/2, + z_hook_min + wall_th + hook_hole/2 ]) + circle(r = hook_hole/2); + } +} + +module FarHookPlan(){ + mirror([1,0,0]) + TonguePlan(); + + rectfromto([ 0, z_hook_min ], + [ hook_w_min, palmrest_from_plug_z + 0.1]); + + translate([0, palmrest_from_plug_z]) + rectfromto([ -hook_hook, 0 ], + [ hook_w_min, hook_w_min ]); +} + +module RotateIntersect(n=6){ + intersection_for (r = [0:n-1]) { + rotate([r/n * 360,0,0]) + linextr(-100,100) children(0); + } +} + +module PlugHolder(){ + difference(){ + union(){ + RotateIntersect(8) + PlugHolderPlan(); + + rotate([0,0,180]) { + linextr_y_xz(-hook_th_plug_holder/2, + +hook_th_plug_holder/2) + PlugHookHookPlan(); + + linextr_y_xz(-hook_th/2, + +hook_th/2) + TonguePlan(); + } + } + + RotateIntersect(6) + PlugMainPlan(); + + linextr(-plug_stem[1]/2, 100) + rectfromto([ -100, -plug_stem[0]/2 ], + [ +100, +plug_stem[0]/2 ]); + } +} + +module PlugHolderPrint(){ ////toplevel + render() PlugHolder(); +} + +module FarHookPrint(){ ////toplevel + linextr(0, hook_th) + FarHookPlan(); +} + +module DemoPlan() { ////toplevel + translate([0,0,-10]) color("grey") PlugHolderPlan(); + PlugMainPlan(); + translate([0,0,-5]) color("blue") { + PlugHookHookPlan(); + TonguePlan(); + } + + translate([0,40,0]) { + FarHookPlan(); + } +} diff --git a/led-panel-ceiling-bracket.scad b/led-panel-ceiling-bracket.scad new file mode 100644 index 0000000..8f39a5b --- /dev/null +++ b/led-panel-ceiling-bracket.scad @@ -0,0 +1,56 @@ +// -*- C -*- + +holespc = 20; + +len = 50; +width = 20; +backspc = 8; +extra_height = 2; +light_height = 12.5; +hole_from_back = 7.2 + 0.5; +hole_dia = 3.5 + 0.5; +bolthead_dia = 7 + 1.0; +bolthead_depth = 5 + 20 - 12 + 2.7/2; + +plasfix_dia = 4.5 + 0.5 + 1.1; +plasfix_head = 8.7 + 0.5 + 1.1; +plasfix_headdep = plasfix_dia; +plasfix_sink = 8; + +height = light_height + backspc + extra_height; + +octagon_fudge = 1/cos(22.5); + +echo("remain", width - bolthead_depth); + +module Bracket(){ + difference(){ + translate([-len/2, 0, 0]) + cube([len, width, height]); + for (xsgn=[-1,+1]) { + translate([xsgn * holespc/2, -1, + light_height - hole_from_back + extra_height]) { + rotate([-90,0,0]) { + rotate([0,0,360/8/2]) { + cylinder(r=bolthead_dia/2 * octagon_fudge, + h= bolthead_depth +1, $fn=8); + cylinder(r=hole_dia/2 * octagon_fudge, + h=50, $fn=8); + } + } + } + } + translate([0, width/2, 0]) { + cylinder(r= plasfix_dia/2, h=50, $fn=20); + translate([0,0,-1]) + cylinder(r= plasfix_head/2, h= plasfix_sink + 1, $fn=20); + } + } +} + +module BracketPrint(){ + rotate([0,180,0]) + Bracket(); +} + +BracketPrint(); diff --git a/led-panel-ceiling-bracket.slic3r b/led-panel-ceiling-bracket.slic3r new file mode 100644 index 0000000..612eb56 --- /dev/null +++ b/led-panel-ceiling-bracket.slic3r @@ -0,0 +1,3 @@ +bottom_solid_layers = 3 +top_solid_layers = 3 +perimeters = 3 diff --git a/lemon-stand.scad.pl b/lemon-stand.scad.pl new file mode 100755 index 0000000..8c1d10f --- /dev/null +++ b/lemon-stand.scad.pl @@ -0,0 +1,164 @@ +#!/usr/bin/perl -w + +use strict; +use Math::Trig; +use Math::Vector::Real; +use IO::File; +use Data::Dumper; +use constant tau => pi*2; + +my $ellipse = 25 / 2; +my $circle = 7 / 2; +my $channelh = 3; +my $channelw = 4; +my $xscale = 35 / 25; +my $N = 180; # around ellipse +my $M = 80; # around each circle +my @channeldistprops = (0, 1/3, 2/3); + +my $NMdiv = $ENV{'LEMONSTAND_COARSE'} || 1; + +$M /= $NMdiv; +$N /= $NMdiv; + +print <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 () { print }; + +STDOUT->error and die $!; +STDOUT->flush or die $!; + +__DATA__ +module Stand(){ + difference(){ + translate([0,0,torusyup]) + Torusy(); + Channels(); + translate([-200,-200,-50]) + cube([400,400,50]); + } +} +Stand(); diff --git a/light-bracket.scad b/light-bracket.scad new file mode 100644 index 0000000..c49dc09 --- /dev/null +++ b/light-bracket.scad @@ -0,0 +1,139 @@ +shrinkage = 1.0126; // width of 56.2 gives 55.5 +remote_width= 56.35 * shrinkage; +remote_height=124.7 * shrinkage; +remote_thick=6.1; // height of 6.8 gives 6.3 +mainhole_thick=remote_thick+1; +hook_hook_thick=1.5; +hook_stem_thick=1.5; +hook_hook_len=1.5; +base_thick=3.5; +base_margin=3.0; +base_width=remote_width-base_margin*2; +base_height=remote_height-base_margin*2; +base_edgewidth=4; + +screw_ys=[ 28, remote_height-28 ]; + +// origin is base of mainhole + +module mainhole() { + translate([ -remote_width/2, 0, 0 ]) + cube(center=false, + size=[ remote_width, remote_height, mainhole_thick ] ); +} + +module hhook(extent) { + translate([ -hook_stem_thick, 0, -base_thick*2 ]) + cube(center=false, + size=[ + hook_stem_thick, + extent, + base_thick*2 + mainhole_thick + hook_hook_thick + ]); + translate([ -hook_stem_thick, 0, -base_thick*2 ]) + cube(center=false, + size=[ + hook_stem_thick+base_margin+base_edgewidth-1, + extent, + base_thick*2 + ]); + translate([ -hook_stem_thick+1.0, 0, mainhole_thick ]) + rotate(v=[0,1,0], a=-30) + cube(center=false, + size=[ + 3, + extent, + hook_hook_thick + ]); + //difference() { + // #translate([ -hook_stem_thick, 0, -base_thick*2 ]) + // cube(center=false, + // size=[ + // hook_stem_thick+base_margin+base_edgewidth-1, + // extent, + // base_thick*2 + mainhole_thick + hook_hook_thick + // ]); + // translate([hook_hook_len, -5, 0]) + // cube(center=false, size=[ 20, extent+10, 30 ]); + //} +} + +module hhookside(extent) { + translate([ -remote_width/2, 0, 0 ]) + hhook(extent); +} + +module hhookbot(extent) { + rotate(a=90, v=[0,0,1]) hhook(extent); +} + +module hstuff() { + translate([0,70,0]) hhookside(15); + translate([0,10,0]) hhookside(15); + translate([-10,0,0]) hhookbot(15); +} + +module slashes() { + for (y=[-30 : 60 : +40]) + translate([0,y,0]) + rotate(v=[0,0,1],a=45) + cube(center=true, [ 5,200,200 ]); +} + +module base() { + translate([ 0, base_height/2 + base_margin, -base_thick/2 ]) + intersection() { + cube(center=true, + [ base_width, base_height, base_thick+10 ]); + union() { + difference() { + cube(center=true, [ 200,200,200 ]); + cube(center=true, + [ base_width - base_edgewidth*2, + base_height - base_edgewidth*2, + base_thick + 15 ]); + + } + slashes(); + mirror([1,0,0]) slashes(); + } + } +// translate([-base_width/2, base_margin, -base_thick*2]) +// cube(center=false, [base_width,base_height,base_thick+10]); +} + +module stuff() { + hstuff(); + mirror([1,0,0]) hstuff(); + base(); + for (y=screw_ys) translate([0, y, -20]) + cylinder(r=6.5, h=21); +} + +module screwhole(holedia, csdia) { + // screw goes along z axis downwards + // origin is top of head + // results are positive, so this should be subtracted + translate([0,0,-100]) cylinder(h=200, r=holedia/2); + csdist=(csdia-holedia)/2; + translate([0,0,-csdist]) cylinder(h=csdist, r1=holedia/2, r2=csdia/2); + cylinder(h=100, r=csdia/2); +} + +module bracket() { + // this is the actual thing we want + difference() { + stuff(); + mainhole(); + for (y=screw_ys) translate([0, y, 0]) + screwhole(5.4,10); //dia=4 gives 2.9 + //holedia=10 gives 9.0 want 7.0 + translate([0,0,-50 - base_thick]) + cube(center=true,[300,300,100]); // print bed + } +} + +intersection() { + !bracket(); + translate([-50,6,-50]) #cube(center=false, [100,27,100]); +} diff --git a/lipo-flat-mount.scad b/lipo-flat-mount.scad new file mode 100644 index 0000000..865b6d3 --- /dev/null +++ b/lipo-flat-mount.scad @@ -0,0 +1,184 @@ +// -*- C -*- + +include +include + +// pimoroni 3000mAh +battery = [ 51.5, + 81.3, + 8.0 ]; + +battery_wall = 2.0; +battery_wall_unsupp = 4.0; +battery_wall_top_gap = 0.75; + +battery_keeper_m_th = 4; +battery_keeper_m_w = 4; +battery_keeper_x_gap = 0.75; // each side +battery_keeper_y_gap_overhang = 0.75; +battery_keeper_y_gap_nutbox = 1.0; +battery_keeper_z_gap_nutbox = 0.50; +battery_keeper_z_gap_overhang = 0.75; + +battery_keeper_x_th_min = 1.5 * 1.5; +battery_keeper_x_th_max = 2.5 * 1.5; +battery_keeper_x_w = 5; +battery_keeper_x_n = 5; + +battery_keeper_screw_x_off = -2.5; + +battery_wire_y = 4; + +battery_nutbox = nutbox_data_M3; + +// calculated + +battery_fix_sz = NutBox_outer_size(battery_nutbox); + +battery_nutbox_z = max( battery[2] + battery_wall_top_gap, + NutBox_h_base(battery_nutbox) ); +battery_keeper_overhang_z = battery[2] + battery_keeper_m_th + + battery_keeper_z_gap_overhang; +battery_keeper_overhang_wall = battery_keeper_m_w; +battery_keeper_overhang_th = battery_keeper_m_th; + +// calculated outputs + +battery_keeper_tab_top_z = battery_nutbox_z + + battery_keeper_z_gap_nutbox + battery_keeper_m_th; + // NB does not include screw head + +battery_keeper_legs_top_z = battery_keeper_overhang_z + + battery_keeper_overhang_th; + +battery_keeper_frame_top_z = battery[2] + battery_keeper_m_th; + +battery_mount_y_min = -battery_fix_sz; +battery_mount_y_max = battery[1] + battery_wall; +battery_mount_x_sz = battery[0] + battery_wall_unsupp*2; + +module BatteryPlan(){ + rectfromto([ -battery[0]/2, 0 ], + [ +battery[0]/2, battery[1] ]); +} + +module BatteryBase(){ ////toplevel + // wall + linextr(-0.1, battery[2] - battery_wall_top_gap, convexity=7) { + difference(){ + union(){ + offset(r=battery_wall) BatteryPlan(); + rectfromto([ 0,0 ], + [ -(battery[0]/2 + battery_wall_unsupp), battery[1]/3 ]); + } + BatteryPlan(); + rectfromto([ battery_fix_sz/2 - 0.5 + + battery_keeper_screw_x_off, -30 ], + [ -battery[0], battery_wire_y ]); + } + } + + // nutbox + translate([battery_keeper_screw_x_off, -battery_fix_sz/2, battery_nutbox_z]) + NutBox(battery_nutbox, battery_nutbox_z + 0.1); + + // overhang for legs at rear + for (m=[0,1]) { + mirror([m,0,0]) { + translate([ battery[0]/2, battery[1], 0]) { + difference(){ + linextr(-0.1, + battery_keeper_overhang_z + + battery_keeper_overhang_th, + convexity=1) + rectfromto([ -battery_keeper_m_w*2, -battery_keeper_m_w ], + [ battery_keeper_overhang_wall, battery_wall ]); + linextr(-1, battery_keeper_overhang_z, + convexity=1) + rectfromto([-20, -20], [0,0]); + } + } + } + } +} + +module BatteryKeeper(){ ////toplevel + // A-frame + translate([0,0, battery[2]]) { + linextr(0, battery_keeper_m_th) { + intersection(){ + // main legs + translate([0, +battery[1], 0]) + multmatrix([[ 1, -battery_keeper_screw_x_off/battery[1], 0, 0, ], + [ 0,1,0, 0, ], + [ 0,0,1, 0, ]]) + translate([0, -battery[1], 0]) + for (sx=[-1,+1]) { + multmatrix([[ 1,0,0, 0, ], + [ 0,1,0, 0, ], + [ 0,0,1, 0, ]] + + sx * + ( battery[0]/2 - 0.5 * battery_keeper_m_w + - battery_keeper_x_gap ) / + ( battery[1] - 0.5 * battery_keeper_m_w ) + * + [[ 0,1,0, 0, ], + [ 0,0,0, 0, ], + [ 0,0,0, 0, ]]) + rectfromto([ -battery_keeper_m_w/2, + battery_keeper_y_gap_nutbox ], + [ +battery_keeper_m_w/2, + battery[1] - battery_keeper_y_gap_overhang ]); + } + + // shape to round off the leg end corners + hull(){ + for (sx=[-1,+1]) { + translate([ sx * ( battery[0]/2 - battery_keeper_m_w/2 + -battery_keeper_x_gap) , + battery[1] - battery_keeper_m_w/2 + -battery_keeper_y_gap_overhang ]) + circle(r = battery_keeper_m_w/2); + } + square(center=true, [ battery[0], 1 ]); + } + } + } + + // x struts + for (i=[0 : battery_keeper_x_n-1]) { + linextr(0, + battery_keeper_x_th_min + + (battery_keeper_x_th_max - battery_keeper_x_th_min) + * pow( i/(battery_keeper_x_n-1)*2 - 1 , 2) + ) { + difference(){ + translate([0, battery[1] * ((i + 0.5) / battery_keeper_x_n)]) + square(center=true, [ battery[0], battery_keeper_x_w ]); + children(0); + } + } + } + } + + // tab for screw and nutbox + translate([battery_keeper_screw_x_off, + 0, + battery_nutbox_z + battery_keeper_z_gap_nutbox]) + linextr(0, battery_keeper_m_th, convexity=4) { + difference(){ + rectfromto([ -battery_fix_sz/2, -battery_fix_sz ], + [ +battery_fix_sz/2, + 0.5 * battery[1] / battery_keeper_x_n + + 0.5 * battery_keeper_m_w ]); + translate([ 0, -battery_fix_sz/2 ]) + circle(r = battery_nutbox[0]/2); + } + } +} + +module BatteryDemo(){ ////toplevel + color("grey") BatteryBase(); + BatteryKeeper() { union(){ } } +} + diff --git a/lock-inframe-bracket.scad b/lock-inframe-bracket.scad new file mode 100644 index 0000000..bfa6e39 --- /dev/null +++ b/lock-inframe-bracket.scad @@ -0,0 +1,349 @@ +// -*- C -*- + +// use shell thickness 1.50 +// use fill density 40% + +include + +tube_dia = 27.5 + 1.625 + 1.32; +lock_w = 42.5 + 0.5; +lock_d = 28.0 + 0.5; +main_h = 45.0; +backflange_d = 12; +backflange_hole_dy = -1; +lockshaft_dia = 14.35; + +cliprecess_h = 16; +total_h = 75; + +back_gap = 12.5; +main_th = 4.50; +tube_th = 5.50; + +midweb_d = 3; +clip_th = 3.5; +clip_gap = 2.5; +clip_d = 22.0; + +mountscrew_dia = 4 + 0.5; +clipbolt_dia = 5 + 0.6; + +mountscrew_washer = 10; + +backflange_th = 4.5; + +$fn=50; + +join_cr = 9; + +tube_rear_extra_th = 1; + +divide_shaft_w = 1.75; +divide_shaft_l = 1.5; +divide_head_dx = 1.75; +divide_head_th = 1.5; +divide_gap = 0.75; + +divide_angle = 26; +divide_fudge_r = 4.75; + +backflange_angle = 20; + +// calculated + +lockshaft_r = [1, 1] * lockshaft_dia / 2; +front_th = main_th; + +tube_or = tube_dia/2 + tube_th; +back_ohw = back_gap/2 + backflange_th; +backflange_ymin = tube_dia/2 + backflange_d; + +lock_0y = tube_dia/2 + lock_d/2 + midweb_d; +lock_0x = lock_w/2 - lock_d/2; +lock_0 = [lock_0x,lock_0y]; + +lock_or = [lock_w, lock_d]/2 + [front_th,front_th]; + +module oval(sz){ // sz[0] > sz[1] + xr = sz[0]; + yr = sz[1]; + hull(){ + for (sx=[-1,+1]) { + translate([sx * (xr-yr), 0]) + circle(r=yr); + } + } +} + +module JoinCircs(jr){ + // http://mathworld.wolfram.com/Circle-CircleIntersection.html + R = tube_or + join_cr; + r = lock_or[1] + join_cr; + d = dist2d( [0,0], lock_0 ); + x = (d*d - r*r + R*R) / (2*d); + y = sqrt( R*R - x*x ); + + echo(lock_0x, lock_0y, R,r, d, x,y); + + for (m=[0,1]) { + mirror([m,0]) { + rotate(atan2(lock_0y, lock_0x)) { + translate([x,-y]) + circle(r= jr); + } + } + } +} + +module DivideHook(){ ////toplevel + w = tube_th/2; + d = divide_gap; + + translate([-1,0] * (w + d + w)){ + for (sx=[-1,+1]) + translate([-(w + w+d) * sx, 0]) circle(r= w); + difference(){ + circle(r = 3*w + d); + circle(r = w + d); + translate([-10*w, -10*w]) square([20*w, 10*w]); + } + } +} + +module DivideCut(){ + w = tube_th/2; + d = divide_gap; + br = tube_dia/2 + tube_th; + + difference(){ + offset(r=divide_gap) DivideHook(); + DivideHook(); + translate([-2*w,0]) mirror([0,1]) square([4*w, 4*w]); + } +} + +module DivideCutB(){ + w = tube_th/2; + d = divide_gap; + br = tube_dia/2 + tube_th; + + intersection(){ + translate([br - tube_th/2,0]) { + difference(){ + circle(r=br + d); + circle(r=br); + } + } + translate([-2*w, 0]) mirror([0,1]) square(4*w); + } +} + +module DivideSurround(){ + w = tube_th/2; + d = divide_gap; + + offset(r= w*2) { + hull() { + DivideCut(); + translate([-(4*w + 2*d), 8*w]) circle(r=w); + } + } +} + +module DivideInPlace(){ + rotate([0,0, -divide_angle]) + translate([ -tube_dia/2 -tube_th/2, 0]) + children(); +} + +module MainPlan(){ ////toplevel + difference(){ + union(){ + difference(){ + union(){ + hull(){ + for (t=[0, tube_rear_extra_th]) + translate([0, -t]) + circle(r = tube_or); + } + rotate([0,0, backflange_angle]) + translate([-back_ohw,0]) mirror([0,1]) + square([back_ohw*2, backflange_ymin]); + + translate([0, lock_0y]){ + oval(lock_or); + } + + hull(){ + JoinCircs(0.01); + polygon([[0,0], lock_0, [-lock_0[0], lock_0[1]]]); + } + } + + rotate([0,0, backflange_angle]) + translate([-back_gap/2,1]) mirror([0,1]) + square([back_gap, backflange_ymin+2]); + + JoinCircs(join_cr); + } + + DivideInPlace() DivideSurround(); + } + translate([0, lock_0y]){ + oval([lock_w/2, lock_d/2]); + } + + circle(r = tube_dia/2); + + DivideInPlace() DivideCut(); + DivideInPlace() DivideCutB(); + } +} + +lockshaft_or = lockshaft_r + [clip_th,clip_th]; +cliprecess_ymax = cliprecess_h - lockshaft_r[1]; +clip_ymin = cliprecess_ymax - main_h; +clip_ogap = clip_gap + clip_th*2; + +module ClipElevationPositive(){ + hull(){ + oval(lockshaft_or); + translate([0, -lockshaft_or[1] * sqrt(2)]) + square(center=true, 0.5); + } + translate([-lockshaft_or[0], 0]) + square([lockshaft_or[0]*2, cliprecess_ymax]); + translate([-clip_ogap/2, 0]) mirror([0,1]) square([clip_ogap, -clip_ymin]); +} + +module ClipElevationNegative(){ + hull(){ + for (y=[0, cliprecess_ymax+1]) + translate([0, y]) + oval(lockshaft_r); + } + translate([-clip_gap/2, 1]) mirror([0,1]) square([clip_gap, 2-clip_ymin]); +} + +module ClipElevation(){ + difference(){ + ClipElevationPositive(1); + ClipElevationNegative(0); + } +} + +module ExtrudeClipElevation(extra=0){ + translate([0, + lock_0y + lock_d/2 + clip_d + extra, + -clip_ymin]) + rotate([90,0,0]) + linear_extrude(height= clip_d + extra*2, convexity=100) + children(0); +} + +module ThroughHole(r, y, z, x=-50) { + translate([x, y, z]) + rotate([0, 90, 0]) + cylinder(r=r, h=100, $fn=20); +} + +module MountingHoleCylinders(r, x=-50){ + for (z=[ 1/4, 3/4 ]) { + rotate([0,0, backflange_angle]) + ThroughHole( r, + -tube_dia/2 -0.5*backflange_d + backflange_hole_dy, + total_h * z, + x); + } +} + +module ThroughHoles(){ + MountingHoleCylinders(mountscrew_dia/2); + + ThroughHole( clipbolt_dia/2, + lock_0y + lock_d/2 + clip_d/2 + front_th/2, + main_h - cliprecess_h - clip_th - clip_d/2 ); +} + +module SlopeTrimElevation(){ + far_corner_nom = [ lock_0y + lock_d/2, main_h ]; + round_centre = far_corner_nom + lock_d/2 * [0,1]; + hull(){ + translate(round_centre) circle(r= lock_d/2); + translate([ lock_0y - lock_d/2, total_h ]) square([ lock_d + clip_d, 1 ]); + translate(far_corner_nom) square([clip_d*2, 1]); + } +} + +module SlopeTrim(){ + rotate([0,90,0]) + rotate([0,0,90]) + translate([0,0, -lock_w]) + linear_extrude(convexity=100, height=lock_w*2) + SlopeTrimElevation(); +} + +module MainPositive(){ + difference(){ + union(){ + linear_extrude(height=total_h, convexity=100) MainPlan(); + ExtrudeClipElevation() ClipElevationPositive(); + } + ExtrudeClipElevation(1) ClipElevationNegative(); + } +} + +module Bracket(){ ////toplevel + difference(){ + MainPositive(); + ThroughHoles(); + SlopeTrim(); + } +} + +module TestTopEdge(){ ////toplevel + intersection(){ + translate([0,0, -total_h]) + translate([0,0, 4]) + Bracket(); + translate([-200,-200,0]) + cube([400,400,100]); + } +} + +module TestClipBoltHole(){ ////toplevel + intersection(){ + union(){ + translate([0, 0, -5]) + Bracket(); + translate([-4, lock_0y + lock_d/2 + 1, 0]) + cube([8, 4, 1.5]); + } + translate([-200, lock_0y + lock_d/2 + 0.1]) + cube([400, 400, total_h-20]); + } +} + +module Demo(){ ////toplevel + Bracket(); + color("blue") MountingHoleCylinders(mountscrew_dia/2 - 0.1); + color("black") MountingHoleCylinders(mountscrew_washer/2, + back_ohw + 0.25); +} + +module DivideDemo(){ ////toplevel + color("black") translate([0,0,-2]) MainPlan(); + color("grey") DivideInPlace() DivideHook(); + color("blue") translate([0,0,-4]) DivideInPlace() DivideCut(); +} + +//MainPlan(); +//ClipElevationPositive(); +//ClipElevation(); +//MainPositive(); +//%ThroughHoles(); +//TestTopEdge(); +//TestClipBoltHole(); + +//Bracket(); + diff --git a/m8-thin-washer.scad b/m8-thin-washer.scad new file mode 100644 index 0000000..8e1f26f --- /dev/null +++ b/m8-thin-washer.scad @@ -0,0 +1,13 @@ +// -*- C -*- + +include + +$fa = 1; +$fs = 0.1; + +linextr(0, 0.425 + 0.125 * 2) { + difference(){ + circle(r= 15 /2); + circle(r= (8 + 0.5) /2); + } +} diff --git a/maglite-holder-photo.jpg b/maglite-holder-photo.jpg new file mode 100644 index 0000000..c0d19ce Binary files /dev/null and b/maglite-holder-photo.jpg differ diff --git a/maglite-holder-torch.fig b/maglite-holder-torch.fig new file mode 100644 index 0000000..9c25deb --- /dev/null +++ b/maglite-holder-torch.fig @@ -0,0 +1,19 @@ +#FIG 3.2 Produced by xfig version 3.2.6 +Landscape +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +5 1 0 1 4 4 50 -1 -1 0.000 0 0 0 0 707.697 1147.762 -1207 1110 -1113 554 -858 45 +2 2 0 1 2 1 75 -1 -1 0.000 0 0 -1 0 0 5 + -1800 -1350 2025 -1350 2025 3825 -1800 3825 -1800 -1350 +2 5 0 1 0 -1 100 -1 -1 0.000 0 0 -1 0 0 5 + 0 maglite-holder-photo.jpg + -1572 -1185 1891 -1185 1891 3762 -1572 3762 -1572 -1185 +2 2 0 1 2 1 75 -1 -1 0.000 0 0 -1 0 0 5 + -859 13 857 13 857 -795 -859 -795 -859 13 +2 3 0 1 1 1 50 -1 -1 0.000 0 0 -1 0 0 6 + -1201 1100 -1204 2667 -231 1187 -225 -159 -846 17 -1201 1100 diff --git a/maglite-holder.scad b/maglite-holder.scad new file mode 100644 index 0000000..9aed425 --- /dev/null +++ b/maglite-holder.scad @@ -0,0 +1,173 @@ +// -*- C -*- + +dxf_off = [ -40, -85 ]; + +torch_lit_dia = 37.5; +torch_big_dia = 56.5; +torch_tot_len = 256; +torch_big_len = 60; + +torch_clear = 30; +torch_clear_below = 10; + +stem_width = 20; +stem_thick = 8; + +torch_recess = 11; +arm_width = 10; +block_thick = 15; + +torch_out_more = 10; + +brace = [ 40, 20, 20 ]; + +hole_dia = 4 + 0.5; +hole_slot = 5; + +movement_extra_angle = 5; + +slop = 4; // total, not each side + +torch_min_xcoord_fig_cm = -2.7; // coordinates of bottom left of curve +torch_min_ycoord_fig_cm = -5.9; // & big part in fig file. mid top is origin +torch_smm_xcoord_fig_cm = -1.9; // x coord of lhs of narrow part + +miniature = 1; // can adjust this to get commitid size right and rescale *Print + +$fa=5; + +// calculated + +include + +torch_dxf_scale = + [ (torch_big_dia - torch_lit_dia) / + (-(torch_min_xcoord_fig_cm - torch_smm_xcoord_fig_cm) * 10 * 2), + torch_big_len / (-torch_min_ycoord_fig_cm * 10) ]; + +echo(torch_dxf_scale); + +above = torch_big_len + torch_clear + torch_clear_below; + +holes = [ 172, 265 ]; + +stem_below = stem_width/2; + +stem_len = holes[1] - above + stem_below; + +torch_out = stem_thick + torch_big_dia/2 + torch_out_more; + +block_width = arm_width*2 + torch_big_dia; + +block_out = torch_out + torch_big_dia/2/sqrt(2); + +module TorchOrig(){ + mirror([0,0,1]){ + hull(){ + rotate_extrude() + translate([-torch_lit_dia/2, 0]) + scale(torch_dxf_scale) + translate(dxf_off) + translate([-torch_smm_xcoord_fig_cm * 10, 0]) + import(file="maglite-holder-torch-curve.dxf", + convexity=10, center=true); + } + translate([0,0, -1]) + cylinder(r=torch_lit_dia/2, h= torch_tot_len - torch_big_len + 1); + } +} + +module Torch(){ + scale(slop/torch_lit_dia + 1.0) + TorchOrig(); +} + +module ScrewHole(y, rot) { + translate([0,0, above -y]){ + rotate([0,rot,0]){ + hull(){ + for (d= [-1,+1] * hole_slot/2) { + translate([d,0,0]) + rotate([90,0,0]) + translate([0,0,-stem_thick*2]) + cylinder(r= hole_dia/2, h= stem_thick*4); + } + } + } + } +} + +module TorchMovement(){ + translate([0, -torch_out, 0]) { + translate([0, 0, -torch_recess]) + Torch(); + for (as=[-1,+1]) { + rotate([0,0, as*movement_extra_angle]) + rotate([90,0,0]) + linear_extrude(height= block_out) + projection() rotate([-90,0,0]) Torch(); + } + } +} + +module Bracket(){ ////toplevel + cid_w = stem_width * .75; + hole_near = hole_slot + hole_dia; + + difference(){ + mirror([0,1,0]) { + translate([-stem_width/2, 0, -stem_len]) + cube([stem_width, stem_thick, stem_len]); + translate([0,0, -block_thick]) hull(){ + translate([-stem_width/2, 0, -brace[2]]) + cube([stem_width, stem_thick, 1]); + translate([-brace[0]/2, 0, 0]) + cube([brace[0], brace[1], 1]); + } + } + ScrewHole(holes[0], 90); + ScrewHole(holes[1], 0); + translate([-cid_w/2, 0, above - holes[0] - hole_near]) + rotate([-90,0,0]) + scale([miniature, miniature, 1]) + Commitid_BestCount([cid_w, holes[1]-holes[0] - hole_near*2] + / miniature); + } + difference(){ + mirror([0,1,0]) + translate([-block_width/2, 0, -block_thick]) + cube([block_width, block_out, block_thick]); + TorchMovement(); + } +} + +module BracketPrint(){ ////toplevel + scale(1/miniature) + rotate([-90,0,0]) + Bracket(); +} + +module TestTorchPrint(){ ////toplevel + scale(1/miniature) + intersection(){ + translate([0,0, torch_lit_dia / 2 / sqrt(2)]) + rotate([-90,0,0]) + Torch(); + translate([-100, -torch_tot_len*2, 0]) + cube([200, torch_tot_len*4, 200]); + } +} + +module Demo(){ ////toplevel + color("red") + translate([0, -torch_out, 0]) + TorchOrig(); + color("blue") + translate([0, -torch_out, above]) + cylinder(r=torch_big_dia/2, h=1); + Bracket(); +} + +//Demo(); +//BracketPrint(); +//TestTorchPrint(); diff --git a/makita-drill-handle-blivet.scad b/makita-drill-handle-blivet.scad new file mode 100644 index 0000000..831b83f --- /dev/null +++ b/makita-drill-handle-blivet.scad @@ -0,0 +1,42 @@ +// -*- C -*- + +include + +hex_across = 12.70 - 0.3; +screw_dia = 8.0 + 0.0; + +min_th = 0.425; +extra_th = 1.0; + +// calculated + +total_th = min_th + extra_th; +hex_rad = hex_across / 2 / cos(30); + +module Plan(){ + difference(){ + circle(r = hex_rad, $fn = 6); + circle(r = screw_dia/2); + } +} + +module Elevation(){ + hull(){ + rectfromto([ -hex_rad, -1], + [ 0.1, min_th]); + translate([ hex_rad, 0 ]) + rectfromto([ 0, -1 ], + [ 1, total_th]); + } +} + +module Blivet(){ + intersection(){ + linextr(0, total_th + 1) + Plan(); + linextr_y_xz(-hex_across, hex_across) + Elevation(); + } +} + +Blivet(); diff --git a/manual-gcode-generator b/manual-gcode-generator new file mode 100755 index 0000000..ba9a0cd --- /dev/null +++ b/manual-gcode-generator @@ -0,0 +1,142 @@ +#!/usr/bin/perl -w + +use strict; + +our @array; +our %procs; + +sub readdata () { + my $l = ''; + my $current = \@array; + while (<>) { + chomp or die; + s/\s+$//; + s/^\s*\!\s*/!/; + $l .= $_; + next if $l =~ s/\\$//; + + $_=$l; $l=''; + if (m/^\!(\w+)\s*\(\)\s*\{$/) { + my $pname=$1; + die if $current ne \@array; + die if exists $procs{$pname}; + $current = $procs{$pname} = []; + next; + } + if (m/^\!\}$/) { + $current = \@array; + next; + } + push @$current, $_; + } +} +readdata(); + +our %c; + +sub defvar ($;$) { + my ($cv,$v) = @_; + $c{$cv} = $v; +} + +defvar('extruderate',0.097200); +defvar('feedrate',540); +defvar('jerkfeedrate',7800); +defvar('retract',4.5); +defvar('restart',4.5); +defvar('restartfeedrate',1800); +defvar('retractfeedrate',1800); +defvar('movefeedrate',7800); +defvar('zlift',0.1); +defvar('zprint'); +defvar('orgx',0); +defvar('orgy',0); + +sub float_g ($) { + my ($f) = @_; + return sprintf "%.5f", $f; +} +sub coords_g ($) { + my ($coords) = @_; + return "X".float_g($coords->[0])." Y".float_g($coords->[1]); +} + +sub p ($) { print "$_[0]" or die $!; } +sub pl ($) { p("$_[0]\n"); } + +sub proc ($); + +sub proc ($) { + my ($aref) = @_; + local ($_); + foreach (@$aref) { + if (!m/^\!/) { + pl($_); + next; + } + pl(";$_"); + if (m/^\!(\w+)\s*\(\)$/) { + my $pname = $1; + die "$pname ?" unless $procs{$pname}; + proc($procs{$pname}); + } elsif (m/^\!draw\s+/) { + my @coords = split /\s+/, $'; #'; + my @undefs = grep { !defined $c{$_} } qw(zprint); + die "@undefs ?" if @undefs; + @coords = map { + my $jerk = s/^\*//; + m/\,/ or die $!; + [ $`, $', !!$jerk ]; # ']; + } @coords; + foreach my $co (@coords) { + foreach my $xy (qw(0 1)) { + my $xyv = $co->[$xy]; + next unless $xyv =~ s/^\@//; + my $orgxy = ($c{orgx},$c{orgy})[$xy]; + $co->[$xy] = float_g($xyv + $orgxy); + } + } + my $extrudepos=$c{restart}; + pl("G92 E0"); + my $zmove=$c{zprint}+$c{zlift}; + pl("G1 F$c{movefeedrate} Z$zmove"); + pl("G1 ".coords_g($coords[0])); + pl("G1 Z$c{zprint}"); + pl("G1 F$c{restartfeedrate} E".float_g($extrudepos)); + my $lastfeedrate=-1; + foreach (my $ci=1; $ci<@coords; $ci++) { + my $g = "G1 ".coords_g($coords[$ci]); + my $wantfeedrate; + if (!$coords[$ci][2]) { + $wantfeedrate=$c{feedrate}; + my $dist = 0; + foreach my $xy (qw(0 1)) { + my $dxy = $coords[$ci][$xy] - $coords[$ci-1][$xy]; + $dist += $dxy * $dxy; + } + $dist = sqrt($dist); + $extrudepos += $dist * $c{extruderate}; + $g .= " E".float_g($extrudepos); + } else { + $wantfeedrate=$c{jerkfeedrate}; + } + if ($wantfeedrate != $lastfeedrate) { + $g .= " F$wantfeedrate"; + $lastfeedrate = $wantfeedrate; + } + pl($g); + } + $extrudepos -= $c{retract}; + pl("G1 F$c{retractfeedrate} E".float_g($extrudepos)); + pl("G1 F$c{movefeedrate} Z$zmove"); + next; + } elsif (m/^\!(\w+)\=(\S+)$/) { + die "$1 ?" unless exists $c{$1}; + $c{$1} = $2; + } else { + die "$_ ?"; + } + } +} + +proc(\@array); diff --git a/mic-camera-adapter.scad b/mic-camera-adapter.scad new file mode 100644 index 0000000..a6cb9f6 --- /dev/null +++ b/mic-camera-adapter.scad @@ -0,0 +1,54 @@ +// -*- C -*- + +// print on High Detail +// but adjust infill to 50%, shell thickness to 2mm + +include +include + +positive_dia = inch * 3/8. - 0.375; +positive_l = inch * 1/2.; + +negative_l = negative_default_l; + +negative_wall = 4; +midsection = 4; + +spanner = 12; + +base_dia = 35; +base_th_min = 1; +base_th_max = 4; + +//$test = true; +$test = false; +$fs=0.1; +$fa=5; + +module Adapter(){ + translate([0,0,-0.1]) + english_thread(diameter=positive_dia/inch, threads_per_inch=16, + leadin=1, test=$test, + length= (positive_l + 0.1) / inch); + rotate([180,0,0]) { + difference(){ + union(){ + cylinder(r= spanner/2 * 1/(0.5 * sqrt(3)), + h = negative_l + midsection, + $fn=6); + translate([0,0, midsection+negative_l]) { + mirror([0,0,1]) { + hull(){ + cylinder(r= base_dia/2, h = base_th_min); + cylinder(r= 0.1, h = base_th_max); + } + } + } + } + translate([0,0, midsection + negative_l]) + CameraMountThread(negative_l); + } + } +} + +Adapter(); diff --git a/mic-table-clamp.scad b/mic-table-clamp.scad new file mode 100644 index 0000000..b88e8d1 --- /dev/null +++ b/mic-table-clamp.scad @@ -0,0 +1,229 @@ +// -*- C -*- + +// print Stem and Wingnut on High Detail +// but adjust shell thickness to 2mm + +// others on Standard + +include +include +include + +positive_dia = inch * 3/8. - 0.375; +positive_l = inch * 1/2.; + +positive_blank_dia = 8.12; +blank_l = 17; +blank_taper = 1.0; + +stem_l = 40; +stem_dia = 12; +stem_th = 3; +stem_ceil = 2; +stem_base_th = 4; +stem_base_dia = 25; +stem_inner_l = 15; + +thread_nom = 8; +thread_pitch = 1.25; +thread_act = thread_nom + 0.600; + +clamp_l = 40; +clamp_top_th = 7; +clamp_bot_th = 10; +clamp_bot_tooth_h = 2.5; +clamp_bot_tooth_d = 10; +clamp_bot_collar = 20; +clamp_bot_collar_th = 3.0; +clamp_reg_sz1 = 3; +clamp_reg_sz2 = 5; +clamp_w = 15; +clamp_max_table_th = 35; + +clamp_hole_dia = thread_nom + 0.30; + +clamp_reg_clear_x = 2.5; +clamp_reg_clear_y = 0.75; // each side +clamp_reg_extra_x = 4; + +//ct_h = 7; + +wingnut_th = 5; +wingnut_wall = 4; +wingnut_wing_mindia = 17.0; +wingnut_wing_xrad = 8; +wingnut_wing_xh = 5; +wingnut_wing_th = 3; + +//$test= true; +$test= false; + +$fa= 3; +$fs= 0.2; + +// calculated + +wingnut_cnr = wingnut_wing_th/2 -0.1; + +clamp_reg_bot_x_min = -stem_base_dia/2 -clamp_reg_clear_x -clamp_reg_sz2; +clamp_collar_r = thread_nom/2 + clamp_bot_collar_th; + +module OurThread(l){ + translate([0,0,-0.01]) + metric_thread(diameter=thread_act, pitch=thread_pitch, + leadin=3, internal=true, + test=$test, length=l); +} + +module StemWith(){ + translate([0,0, stem_l -0.1]) + children(); + + difference(){ + union(){ + cylinder(r= stem_dia/2 * 1/(0.5 * sqrt(3)), + h = stem_l, + $fn=6); + cylinder(r= stem_base_dia/2, + h = stem_base_th); + } + OurThread(stem_inner_l); + } +} + +module Stem(){ ////toplevel + StemWith() + english_thread(diameter=positive_dia/inch, threads_per_inch=16, + leadin=1, test=$test, + length= (positive_l + 0.1) / inch); +} + +module StemBlankPart(){ + hull(){ + cylinder(h = blank_l + 0.1 - blank_taper, + r = positive_blank_dia/2); + cylinder(h = blank_l + 0.1, + r = positive_blank_dia/2 - blank_taper); + } +} + +module BlankStem(){ ////toplevel + StemWith() + StemBlankPart(); +} + +module Wingnut(){ ////toplevel + difference(){ + union(){ + cylinder(r= (thread_nom+wingnut_wall)/2, + h= wingnut_th); + minkowski(){ + sphere(r= wingnut_cnr); + translate([0,0, wingnut_cnr*0.5]) + linear_extrude(height= wingnut_wing_xh + wingnut_th + - wingnut_cnr*1.5) + square([wingnut_wing_mindia + wingnut_wing_xrad*2 - wingnut_cnr*2, + wingnut_wing_th - wingnut_cnr*2], + center=true); + } + } + translate([0,0, wingnut_th]) + linear_extrude(height= wingnut_wing_xh+1) + square(wingnut_wing_mindia, center=true); + translate([0,0, wingnut_th]) + rotate([180,0,0]) + OurThread(wingnut_th+3); + mirror([0,0,1]) + linear_extrude(height=5) + square(center=true, wingnut_wing_mindia*2); + } +} + +module ClampCollarPlan(){ + circle(r= clamp_collar_r); +} +module ClampHolePlan(){ + circle(r= clamp_hole_dia/2); +} +module ClampArmPlan(){ + r = clamp_collar_r; + hull(){ + rectfromto([r, -clamp_w/2], + [clamp_l, +clamp_w/2]); + ClampCollarPlan(); + } +} + +module ClampTop(){ ////toplevel + linear_extrude(height = clamp_top_th, convexity=4) { + difference(){ + union(){ + ClampArmPlan(); + ClampCollarPlan(); + } + ClampHolePlan(); + } + } + linear_extrude(height = clamp_reg_sz1, convexity=4) { + difference(){ + for (m=[0,1]){ + mirror([0,m,0]) + translate([0, clamp_reg_sz2/2 + clamp_reg_clear_y, 0]) + rectfromto([clamp_reg_bot_x_min - clamp_reg_extra_x, 0 ], + [0, clamp_reg_sz1 ]); + } + ClampHolePlan(); + } + } +} + +module ClampBot(){ ////toplevel + linear_extrude(height = clamp_bot_th, convexity=4) { + difference(){ + ClampArmPlan(); + ClampHolePlan(); + } + } + translate([clamp_l, 0, clamp_bot_th-0.1]) + linear_extrude(height = clamp_bot_tooth_h +0.1) + rectfromto([ -clamp_bot_tooth_d, -clamp_w/2 ], + [ 0, +clamp_w/2 ]); + translate([0,0, clamp_bot_th]) + mirror([0,0,1]) + linear_extrude(height = clamp_bot_collar) + difference(){ + ClampCollarPlan(); + ClampHolePlan(); + } + translate([0, 0, clamp_bot_th]) { + linextr(-clamp_reg_sz2, clamp_max_table_th+clamp_reg_sz2) { + translate([clamp_reg_bot_x_min, 0]) { + rectfromto([ 0, -clamp_reg_sz2/2 ], + [ clamp_reg_sz2, +clamp_reg_sz2/2 ]); + } + } + linextr(-clamp_reg_sz2, 0) { + difference(){ + rectfromto([ clamp_reg_bot_x_min, -clamp_reg_sz2/2 ], + [ 0, +clamp_reg_sz2/2 ]); + ClampHolePlan(); + } + } + } +} + +module StemBlankTest(){ ////toplevel + StemBlankPart(); + linextr(-1.5,0) square(center=true, [10,35]); +} + +module Demo(){ ////toplevel + color("blue") translate([0,0, clamp_top_th+0.5]) BlankStem(); + color("red") ClampTop(); + color("grey") translate([0,0, -(clamp_bot_th + 5)]) ClampBot(); + translate([0,0, -(clamp_bot_collar +10)]) + rotate([180,0,0]) Wingnut(); +} + +//Wingnut(); +//Stem(); diff --git a/nook-case-test.scad b/nook-case-test.scad new file mode 100644 index 0000000..3f4cc51 --- /dev/null +++ b/nook-case-test.scad @@ -0,0 +1,6 @@ +// -*- C -*- + +//// toplevels-from: +include + +$test = true; diff --git a/nook-case.scad b/nook-case.scad new file mode 100644 index 0000000..ecbbbc2 --- /dev/null +++ b/nook-case.scad @@ -0,0 +1,351 @@ +// -*- C -*- + +// Infill density: 20% + +include +include + +nook_th = 12.41 + 0.50 - 1.50 + 1.35 - .25; +nook_w = 127.12 + 0.75 - .95 - .50; +nook_h = 123.44 + 21.88 + 21.05 + 0.75 - 1.90 - 0.50 - 0.50; + +edge_ledge_w = 9.60; +edge_ledge_h = 2.44 - .25; +edge_ledge_inc_ang = 10; // degrees + +usb_w = 14.5; +usb_below = 1.5; + +open_recess_w = 12.5; +open_recess_h = 2.5; + +open_recess_2_len = 15; +open_recess_2_d_tooth = 30; + +nook_cnr_rad = 10; + +case_th = 2.5; +ledge_w = 4; +tape_th = 1.75; +tape_inside = 2.0; + +gap = 0.5 * [1,1]; + +tape_w = 15; + +test_pillar = 4; + +engage_l0 = 10; +engage_l1 = 10; +engage_l2 = 3; + +tooth_inward = gap[0] * 1.0 + 0.25 + 0.25; +tooth_w = 15; + +diag_near_hinge_slope = 0.5; + +$test = false; + +$fa = $test ? 10 : 3; +$fs = $test ? 0.1 : 1; + +// calculated + +tooth_height = nook_th; +ledge_h = case_th; +lid_th = case_th; +tooth_th = case_th; + +spp0 = [0,0]; +spp1 = spp0 + case_th * [-1,0]; +spp9 = spp0 + ledge_h * [0,-1]; +spp8 = spp9 + nook_th * [0,-1]; +spp7 = spp8 + case_th * [-1,-1]; + +spp11y = spp1[1] - tape_th; +spp4y = 0.5 * (spp0[1] + spp7[1]); +spp3y = spp4y + tape_inside/2; spp5y = spp4y - tape_inside/2; +spp2y = spp3y + tape_th; spp6y = spp5y - tape_th; + +spp20 = spp8 + nook_cnr_rad * [1,0]; +spp20x = spp20[0]; + +tppA = spp0 + gap; +tppB = spp1 + [0, gap[1]]; +tppC = tppB + lid_th * [0,1]; +tppD = [ spp20x, tppC[1] ]; +tppE = [ spp20x, tppB[1] ]; +tppF = tppA + ledge_w * [1,0]; +tppG = tppF + ledge_h * [0,-1]; +tppH = [ tppA[0], tppG[1] ]; + +tppJx = tppA[0] + tape_th; + +tppK = [ tppC[0], tppG[1] ]; +spp31 = tppK - [0, gap[1]]; +spp30 = [ spp8[0], spp31[1] ]; + +nom_cnr = 0.5 * [nook_w, nook_h, 0] - nook_cnr_rad * [1,1,0]; + +tooth_y = nom_cnr[1] - tooth_w/2; + +etxa = nom_cnr[0] - engage_l2; +etxb = etxa - engage_l1; +etxc = -(nom_cnr[0] - engage_l2); + +tapa = nom_cnr[1] - engage_l2; +tapb = tapa - tape_w; + +opra = nom_cnr[1] - engage_l2; +oprb = opra - open_recess_w; + +opqa = tooth_y - open_recess_2_d_tooth + open_recess_2_len/2; +opqb = opqa - open_recess_2_len; + +tppS = tppB + [-gap[0], 0]; +tppP = [ tppS[0] - tooth_th, tppC[1] ]; +tppQ = tppP + tooth_height * [0,-1] + tooth_inward * [1,0]; +tppR = [ tppS[0] + tooth_inward, tppQ[1] ]; +tppM = (tppQ + tppR) * 0.5 + tooth_th * 0.5 * [0,1]; + +edge_ledge_rad = edge_ledge_h; + +module RightSideMainProfile() { + rectfromto(spp7, spp0); + rectfromto(spp7, spp20); + EdgeLedgeProfile(); +} + +module LeftSideMainProfile() { + rectfromto(spp7, spp30); + rectfromto(spp7, spp20); + EdgeLedgeProfile(); +} + +module EdgeLedgeProfile() { + intersection(){ + hull(){ + for (t=[[0,0], [-20,0], [0,-10]]) { + translate(spp8 + + [edge_ledge_w, edge_ledge_h] + + edge_ledge_rad * [ -sin(edge_ledge_inc_ang), + -cos(edge_ledge_inc_ang) ] + + t) + circle(edge_ledge_rad); + } + } + translate(spp7) + square(30); + } +} + +module TopTapeCutout() { + polygon([ tppA, + tppA + [-40, 0], + tppG + [-40,-1], + [ tppJx, tppH[1]-1 ], + [ tppJx, tppC[1]+1 ], + [ tppA[0], tppC[1]+1 ]]); +} + +module RightTopMainProfile() { + l = [ tppA, tppB, tppC, tppD, tppE, tppF, tppG, tppH ]; + polygon(l); +} + +module LeftTopMainProfile() { + l = [ tppC, tppD, tppE, tppF, tppG, tppK ]; + polygon(l); +} + +module SideTapeCutout1(y0,y1) { + rectfromto([ spp7[0]-1, y0 ], + [ spp8[0]+1, y1 ]); +} + +module SideTapeCutout() { + SideTapeCutout1(spp6y, spp5y); + SideTapeCutout1(spp3y, spp2y); + SideTapeCutout1(spp3y, spp2y); + SideTapeCutout1(spp11y, spp1[1] + 1); // obsolete I think +} + +module ToothProfile(){ + polygon([tppA, + tppB, + tppS + [-0.1,0], + tppP, + tppC]); + hull(){ + polygon([tppP, + tppM, + tppS]); + translate(tppM) + circle(r= tooth_th/2); + } +} + +module Demo(){ ////toplevel + translate([-1,0,0]) { + translate([0,0,-2]) LeftSideMainProfile(); + translate([0,0,-2]) color("yellow") LeftTopMainProfile(); + color("red") difference(){ + LeftSideMainProfile(); + SideTapeCutout(); + } + translate([0,0,-4]) color("brown") EdgeLedgeProfile(); + translate(concat(spp8 + [edge_ledge_w, edge_ledge_h], [2])) + rotate(-edge_ledge_inc_ang) { + color("blue") square(3); + color("lightblue") mirror([1,0]) square(3); + } + } + translate([0,0,0]) color("purple") difference(){ + LeftTopMainProfile(); + TopTapeCutout(); + } + translate([nook_cnr_rad*2 + 5, 0,0]) mirror([1,0,0]) { + color("red") RightSideMainProfile(); + color("purple") RightTopMainProfile(); + color("grey") translate([0,0,-2]) ToothProfile(); + } + //%SideTapeCutout(); +} + +module FaceCore(z0,z1, extra_left, extra_right){ + difference(){ + for (mx=[0,1]) mirror([mx,0,0]) { + for (my=[0,1]) mirror([0,my,0]) { + translate(-nom_cnr) { + rotate_extrude(angle=90, convexity=10) { + intersection(){ + translate(-[1,0,0] * nook_cnr_rad) + children(mx); + rectfromto([-100,-100], [0,100]); + } + } + } + } + translate([nook_w/2, 0,0]) + linextr_y_xz(-nom_cnr[1]-0.1, nom_cnr[1]+0.1) + children(1-mx); + } + for (my=[0,1]) mirror([0,my,0]) { + translate([-nook_w/2, 0,0]) + mirror([1,0,0]) + linextr_y_xz(tapb, tapa) + children(2); + } + } + for (my=[0,1]) mirror([0,my,0]) { + translate([0, -nook_h/2, 0]) { + linextr_x_yz(-nom_cnr[0]-0.1, etxc + extra_left) children(0); + linextr_x_yz(etxc - extra_right, etxb + extra_right) children(1); + linextr_x_yz(etxb - extra_left, etxa + extra_left) children(0); + linextr_x_yz(etxa - extra_right, nom_cnr[0]+0.1) children(1); + } + } + if (!$test) { + linextr(z0,z1) + rectfromto(-nom_cnr, nom_cnr); + } +} + +module DiagonaliseNearHinge(wider){ + sz = spp0[1] - spp30[1] + gap[1]; + + for (my=[0,1]) mirror([0,my,0]) { + translate([-etxa, -nook_h/2, 0]) + mirror([1,0,0]) + linextr_y_xz(spp31[0] - wider, spp30[0] + gap[0] + 0.1) + translate([ 0, spp30[1] ]) + polygon([[ -1, 0 ], + [ 0, 0 ], + [ sz/diag_near_hinge_slope, sz ], + [ sz/diag_near_hinge_slope, sz + 0.1 ], + [ -1, sz + 0.1 ]]); + } +} + +module Base(){ ////toplevel + difference(){ + FaceCore(spp7[1],spp8[1], 0.3, 0) { + LeftSideMainProfile(); + RightSideMainProfile(); + SideTapeCutout(); + } + translate([0, -nook_h/2, 0]) + mirror([0,1,0]) + linextr_x_yz(-usb_w/2, usb_w/2) + rectfromto(spp8 + [-40, usb_below], [40, 40]); + translate([ gap[0], 0,0 ]) + DiagonaliseNearHinge(10); +/* + translate([nook_w/2, 0, 0]) + linextr_y_xz(oprb, opra) + translate(spp0) + rectfromto([-40, -open_recess_h], [40, 1]); +*/ + } + if ($test) { + linextr(spp7[1], spp8[1]) { + hull(){ + for (r=[0,180]) + rotate([0,0,r]) + translate(nom_cnr + -1 * nook_cnr_rad*[1,1]) + square(12); + } + } + } +} + +module Top(){ ////toplevel + difference(){ + FaceCore(tppE[1],tppD[1], -gap[0], gap[0] + 0.3) { + LeftTopMainProfile(); + RightTopMainProfile(); + TopTapeCutout(); + } + translate([nook_w/2, 0,0]) + linextr_y_xz(opqb, opqa) + rectfromto(spp8, tppC + [-1,1]); + } + translate([0,0, gap[1]]) + DiagonaliseNearHinge(0); + translate([nook_w/2, tooth_y, 0]) + linextr_y_xz(-tooth_w/2, +tooth_w/2) + ToothProfile(); +} +module TopPrint(){ ////toplevel + rotate([0,0,90]) rotate([180,0,0]) Top(); +} +module BasePrint(){ ////toplevel + rotate([0,0,90]) Base(); +} + +module TestExtrude(){ + difference(){ + linextr_y_xz(-test_pillar, tape_w+test_pillar) children(0); + linextr_y_xz( 0, tape_w ) children(1); + } +} + +module BaseTestRest(){ ////toplevel + cube([30,15, spp8[1]-spp7[1]]); +} + +module Demo3(){ ////toplevel + color("purple") Top(); + color("red") Base(); +} + +module TestSide(){ ////toplevel + TestExtrude() { LeftSideMainProfile(); SideTapeCutout(); } +} + +module TestTop(){ ////toplevel + TestExtrude() { LeftTopMainProfile(); TopTapeCutout(); } +} +module TestTopPrint(){ ////toplevel + rotate([180,0,0]) TestTop(); +} diff --git a/nutbox.scad.m4 b/nutbox.scad.m4 new file mode 100644 index 0000000..5384eff --- /dev/null +++ b/nutbox.scad.m4 @@ -0,0 +1,65 @@ +// -*- C -*- +// edit nutbos.scad.m4, not nutbos.scad! +// shaft, nut_across, nut_thick, nut_recess, wall, ceil + +nutbox_data_M4 = [ + 4.0 + 0.5, + 6.89 + 0.45, + 3.10 + 0.75, + 0.75, + 2.0, + 2.5 + ]; + +nutbox_data_M3 = [ + 3.0 + 0.5, + 5.48 + 0.45, + 2.26 + 0.75, + 0.75, + 1.8, + 2.0 + ]; + +m4_define(shaft, (nutbox_data[0])) +m4_define(nut_across, (nutbox_data[1])) +m4_define(nut_thick, (nutbox_data[2])) +m4_define(nut_recess, (nutbox_data[3])) +m4_define(wall, (nutbox_data[4])) +m4_define(ceil, (nutbox_data[5])) + +m4_define(nut_dia, (nut_across / cos(30))) +m4_define(outer_size, (nut_dia + wall * 2)) +m4_define(h_base, (ceil + nut_thick + nut_recess)) + +function NutBox_shaft(nutbox_data) = shaft; +function NutBox_outer_size(nutbox_data) = outer_size; +function NutBox_h_base(nutbox_data) = h_base; +function NutBox_wall(nutbox_data) = wall; // not sure why anyone needs this + +module NutBox(nutbox_data, h, h_above_extra=0) { + // origin is centre of top of mount + // entrance is to positive y + // height is h which must be at least h_base + // can be mad extra tall (with hole all the way through) with h_above_extra + w = outer_size; + difference(){ + mirror([0,0,1]) translate([-w/2,-w/2, -h_above_extra]) + cube([w,w, h + h_above_extra]); + mirror([0,0,1]) translate([0,0,-1 -h_above_extra]) + cylinder(r = shaft/2, h = h+2 + h_above_extra, $fn=20); + for (offset = [ [0,0, -nut_recess], + [0, outer_size, 0] ]) { + hull(){ + for (toffset = [[0,0,0], offset]) { + translate(toffset) + translate([0,0, -ceil]) + mirror([0,0,1]) + rotate([0,0, 360/6/2]) + cylinder(r = nut_dia/2, + h = nut_thick, + $fn = 6); + } + } + } + } +} diff --git a/osstest-arm-hub-bracket.scad b/osstest-arm-hub-bracket.scad new file mode 100644 index 0000000..63fefed --- /dev/null +++ b/osstest-arm-hub-bracket.scad @@ -0,0 +1,36 @@ +// -*- C -*- + +len = 80; +basethick = 4; +sidewall = 5; +width = 40; + +strapthick = 4; +strapwidth = 7; + +strapbotgap = 1; +strapsidegap = 4; +overstrap = 4; + +wallheight = strapbotgap + strapthick + overstrap; + +availlen = (len - strapsidegap); +numstraps = floor(availlen / (strapwidth + strapsidegap)); +strapstride = availlen / numstraps; +echo(numstraps, strapstride); + +module Bracket(){ + difference(){ + cube([len, width, basethick+wallheight]); + translate([-1, sidewall, basethick]) + cube([len+2, width-sidewall*2, wallheight+1]); + for (i=[0:numstraps-1]) { + translate([ (0.5+i)*strapstride + strapsidegap/2, + width/2, + basethick + strapbotgap + strapthick/2 ]) + cube([strapwidth, width*2, strapthick], center=true); + } + } +} + +Bracket(); diff --git a/osstest-arm-net-bracket.scad b/osstest-arm-net-bracket.scad new file mode 100644 index 0000000..54e7d3b --- /dev/null +++ b/osstest-arm-net-bracket.scad @@ -0,0 +1,94 @@ +// -*- C -*- + +holedist = 64; +tonguewidth = 10; +tongue2width = 15; +totaldepth = 26; +tongue2depth = 35; +thick = 4; +tabover = 7+6; +tabunder = 15; + +tabsidel = 7+1; +tabsider = 7+10; + +tonguethick = 4; +tongue2thick = 5; +strapthick = 2; +strapwidth = 5 + 0.35; +ridgewidth = 2; + +hstrengthick = 2.5; +hstrengdepth = strapwidth; + +cutoutover = 7; +rcutoutside = 7+2 - 0.5; +lcutoutside = 7-6.5 - 0.5; + +t2strengwidth = 10; +t2strengwidtht = 4; +t2strenglen = tongue2depth + 5; +t2strengthicker = 1; + +strapholethicker = 1.5; + +holedia = 3.5; + +tongue2x = tongue2width - holedist; + +module Tongue(tw,tt,ad,slots=2){ + y0=thick+0.1; + yn=ad-ridgewidth-strapwidth; + difference(){ + union(){ + translate([-tw, 0, 0]) + cube([tw, ad, tt+strapthick]); + child(); + } + for (yi=[1:slots-1]) + translate([-tw-1, y0 + (yn-y0)*yi/(slots-1), tt]) + cube([tw+2, strapwidth, strapthick+strapholethicker]); + } +} + +module Body(){ + translate([-holedist-tabsider, 0, 0]) { + cube([tabsidel+tabsider+holedist, thick, tabunder+tabover]); + cube([tabsidel+tabsider+holedist, thick+hstrengdepth, hstrengthick]); + } + Tongue(tonguewidth,tonguethick,totaldepth,3); + translate([tongue2x,0,0]) + Tongue(tongue2width,tongue2thick,tongue2depth+thick,5) { + mirror([1,0,0]) hull(){ + translate([-(tongue2width-t2strengwidth)*0.05, 0,0]) + cube([t2strengwidth,t2strenglen, + tongue2thick+strapthick+strapholethicker+t2strengthicker]); + cube([t2strengwidtht,thick+0.1,tabunder+tabover]); + } + } +} + +module Object(){ + difference(){ + Body(); + translate([0,-25,tabunder+cutoutover]) { + translate([-(holedist+rcutoutside), 0,0]) + mirror([1,0,0]) + cube([50,50,50]); + translate([lcutoutside, 0,0]) + cube([50,50,50]); + } + for (x=[-holedist,0]) + translate([x, 0, tabunder]) { + translate([0, -1, 0]) { + rotate([-90,0,0]) { + cylinder(r= holedia/2+0.5, h=thick+2, $fn=20); + } + } + translate([0, 19.95 + thick, 0]) + cube(center=true,[10,40,10]); + } + } +} + +Object(); diff --git a/osstest-arm-psu-bracket.scad b/osstest-arm-psu-bracket.scad new file mode 100644 index 0000000..2703cbc --- /dev/null +++ b/osstest-arm-psu-bracket.scad @@ -0,0 +1,88 @@ +// -*- C -*- + +mainlen = 33; +straps = [10,23]; +width = 60; +endwall = 5; +sidewall = 8; +basethick = 3; +endwallheight = 20; +morebase = 20; + +plugwidth = 35; +plugstartheight = 10; + +strapthick = 4; +strapwidth = 7; +strapbotgap = 1; +overstrap = 6; + +discdia = 60; +discoff_rear = 10; +discoff_front = 50; + +sidewallraise = strapbotgap + strapthick + overstrap; + +module Sides(){ + difference(){ + for (y=[0, width-sidewall]) { + translate([0,y,0]) + cube([mainlen, sidewall, basethick + sidewallraise]); + } + for (x=straps) { + translate([x, 0, basethick + strapbotgap + strapthick/2]) + cube([strapwidth, width*3, strapthick], center=true); + } + } +} + +module Ell(baseoff){ + translate([-endwall,0,0]) { + translate([baseoff,0,0]) + cube([mainlen + endwall + morebase, width, basethick]); + cube([endwall+0.1, width, endwallheight + sidewallraise + basethick]); + } +} + +module Plug(){ + translate([0, width/2, + basethick + sidewallraise + plugstartheight + 50]) + cube([endwall*3, plugwidth, 100], center=true); +} + +module Disc(discoff){ + translate([discoff + discdia/2, width/2, -1]) + cylinder(r=discdia/2, h=50, $fn=100); +} + +module Main(baseoff){ + difference(){ + union(){ + Ell(baseoff); + Sides(); + } + Plug(); + } +} + +module RearBlock(){ + difference(){ + Main(-morebase); + Disc(discoff_rear); + } +} + +module FrontBlock(){ + difference(){ + Main(0); + Disc(discoff_front - endwall); + } +} + +module Both(){ + RearBlock(); + translate([mainlen + endwall + 10, 0, 0]) + FrontBlock(); +} + +Both(); diff --git a/pandemic-counter-letters.fig b/pandemic-counter-letters.fig new file mode 100644 index 0000000..fb9c92a --- /dev/null +++ b/pandemic-counter-letters.fig @@ -0,0 +1,16 @@ +#FIG 3.2 Produced by xfig version 3.2.5b +Landscape +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3780 3870 675 675 3780 3870 4455 3870 +4 0 0 32 -1 14 56 0.0000 4 540 555 3522 4119 A\001 +4 0 0 30 -1 14 56 0.0000 4 570 555 3510 4140 C\001 +4 0 0 33 -1 14 56 0.0000 4 570 555 3510 4140 S\001 +4 0 0 35 -1 14 48 0.0000 4 465 960 3337 4098 DF\001 +4 0 0 34 -1 14 56 0.0000 4 540 555 3510 4140 T\001 +4 0 0 31 -1 14 56 0.0000 4 540 555 3537 4125 L\001 diff --git a/pandemic-counter.scad b/pandemic-counter.scad new file mode 100644 index 0000000..4d427af --- /dev/null +++ b/pandemic-counter.scad @@ -0,0 +1,115 @@ +// -*- C -*- + +tokenrad=13; +tokenthick=1.9; + +joinwidth=1.0; + +circlerad=15; + +module Letter(depth) { + translate([-circlerad,-circlerad]) + import(file=str("pandemic-counter-l",depth,".dxf"), convexity=100); +} + +module Token(depth) { + rotate([0,180,0]) + linear_extrude(height=tokenthick) union(){ + difference(){ + circle(tokenrad); + Letter(depth); + } + child(); + } +} + +module Token_CDC(){ ////toplevel + Token(30){}; +} +module Token_Lab(){ ////toplevel + Token(31){}; +} +module Token_Act(){ ////toplevel + Token(32){ + translate([0, 1]) + square([tokenrad*.75, joinwidth], center=true); + } +} +module Token_Spec(){ ////toplevel + Token(33){}; +} +//module Token_Terr(){ ////toplevel +// Token(34){}; +//} +//module Token_TerrMove(){ ////toplevel +// Token(35){ +// translate([-tokenrad*.75, -1]) +// square([tokenrad*.75, joinwidth]); +// }; +//} + +spacing = tokenrad * 2 + 2; + +module Tokens(rows=1,cols=1) { + for (i=[0:rows-1]) + for (j=[0:cols-1]) + translate([j*spacing, i*spacing, 0]) + child(0); +} + +module Tokens_Act(){ ////toplevel + // Print *twice*, LAPIS BLUE or SQUEEZED ORANGE + // ordinary actions + // up to 4 for 5 players, plus 2 for Borrowed Time plus 1 for Generalist + // so need 23, make 24 + Tokens(4,3) Token_Act(); +} + +module Tokens_Spec(){ ////toplevel + // ELECTRIC BLUE or MELLOW YELLOW + // once-per-turn special action, one each for 5 players + Tokens(3) Token_Spec(); + translate([spacing,0,0]) Tokens(2) Token_Spec(); +} + +module Tokens_CDC(){ ////toplevel + // STORM GREY + // CDC + // 1 action per turn + 2 Borrowed Time + Tokens(3) Token_CDC(); +} + +module Tokens_Lab(){ ////toplevel + // WHITE + // free Lab action (on building research station, etc) + // make 2 (probably want less than that) + Tokens(2) Token_Lab(); +} + +//module Tokens_Terr(){ ////toplevel +// // FIRE TRUCK RED +// // Bioterrorist general actions +// Tokens(2) Token_Terr(); +//} + +//module Tokens_TerrMove(){ ////toplevel +// // CLASSIC BLACK +// // Bioterrorist drive/ferry +// Tokens(1) Token_TerrMove(); +//} + +module PosToken(i,j){ + translate([j*spacing, i*spacing, 0]) child(); +} + +module Demo(){ ////toplevel + PosToken(0,0) Token_CDC(); + PosToken(1,0) Token_Lab(); + PosToken(2,0) Token_Act(); + PosToken(3,0) Token_Spec(); +// PosToken(1,1) Token_Terr(); +// PosToken(2,1) Token_TerrMove(); +} + +//Tokens_Act(); +//Demo(); diff --git a/pandemic-counter.slic3r b/pandemic-counter.slic3r new file mode 100644 index 0000000..add6df7 --- /dev/null +++ b/pandemic-counter.slic3r @@ -0,0 +1 @@ +fill_density = 0.5 diff --git a/pandemic-quarantine-numbers.fig b/pandemic-quarantine-numbers.fig new file mode 100644 index 0000000..1524380 --- /dev/null +++ b/pandemic-quarantine-numbers.fig @@ -0,0 +1,13 @@ +#FIG 3.2 Produced by xfig version 3.2.5b +Landscape +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1800 1800 2385 1800 2385 2385 1800 2385 1800 1800 +4 0 0 1 -1 14 24 0.0000 4 255 240 1980 2205 1\001 +4 0 0 2 -1 14 24 0.0000 4 255 240 1980 2205 2\001 diff --git a/pandemic-quarantines.scad b/pandemic-quarantines.scad new file mode 100644 index 0000000..834d128 --- /dev/null +++ b/pandemic-quarantines.scad @@ -0,0 +1,65 @@ +// -*- C -*- + +prisml = 13; +triedge = 13; + +etchdepth = 1.0; + +figboxsize = 13; + +// calculated + +triheight = triedge / 2 * sqrt(3); +tricentre = triedge / 2 * tan(30); + +module Number(number) { + translate([-figboxsize/2, -figboxsize/2]) + import(file=str("pandemic-quarantine-l",number,".dxf"), convexity=100); +} + +module FaceTriangle(){ + x = triedge / 2; + y = triheight; + polygon([[-x, 0], + [ 0, y], + [ x, 0]]); +} + +module Body(){ + translate([0, prisml/2, 0]) + rotate([90,0,0]) + linear_extrude(height=prisml) FaceTriangle(); +} + +module NumberCut(number){ + translate([0,0, -etchdepth]) + linear_extrude(height= etchdepth + 1) + Number(number); +} + +module Etchings(){ + for (rot=[0,180]) { + rotate([0,0, rot]) + translate([0, -prisml/2, triedge * 0.3]) + rotate([90, 0, 0]) + NumberCut(2); + } + for (rot=[0,120,240]) { + translate([0,0, tricentre]) + rotate([0, rot, 0]) + translate([0,0, -tricentre]) + rotate([0,180,0]) + rotate([0,0, rot==240 ? 90 : -90]) + NumberCut(1); + } +} + +module Counter(){ + difference(){ + Body(); + Etchings(); + } +} + +Counter(); +//NumberCut(1); diff --git a/pannierstay.scad b/pannierstay.scad new file mode 100644 index 0000000..65538a6 --- /dev/null +++ b/pannierstay.scad @@ -0,0 +1,14 @@ +len = 54.3 - 1; +bigdep = 10.7 - 1; +smalldep= 4.9 - 1; +tallw1 = 3.5 - 1.5; +tallw2 = 3.5 - 1.5; // ish +totalw = 11.6 - 2; + +gapw = totalw - tallw1 - tallw2; + +difference() { + cube([len, totalw, bigdep]); + translate([-1, tallw2, smalldep]) + cube([len+2, gapw, bigdep - smalldep + 1]); +} diff --git a/pattress-boxes-3-cover.scad b/pattress-boxes-3-cover.scad new file mode 100644 index 0000000..7c1d4b8 --- /dev/null +++ b/pattress-boxes-3-cover.scad @@ -0,0 +1,201 @@ +// -*- C -*- + +patbox_side = 87; +patbox_centres = 60.3; + +lid_thinboxbase_overlap = 5; +lid_fatbox_overlap = 12; + +lid_thinbox_h = 9; +lid_fatbox_h = 24; +lid_fatbox_switches_h = 6+4; + +lid_max_switches_w = 70; +lid_switches_y_slop = 3; + +total_len = 260; +thinbox_len = 87; + +rail_overlap = 8; + +lid_top_wall = 1.5; +lid_front_wall = 1.5; +lid_side_wall = 1.5; + +peg_engage_depth = 1; +peg_engage_dia = 6.5; +peg_main_dia = 9; +peg_max_dia = 15; +peg_inner_dia = 3.5; +peg_top_thick = 1; +peg_straight_len = 3; + +$peg_inner_slop = 0.75; +$peg_outer_slop = -0.9; +$peg_outer_slop_engage = 0.1; + +peg_slope = 1; + +lid_side_slop = 0.5; +lid_rail_behindslop = 0.5; +lid_rail_strongwall = 2.5; + +// computed + +lid_inner_max_h = + lid_thinboxbase_overlap + lid_fatbox_h + lid_fatbox_switches_h; +lid_inner_min_h = lid_fatbox_overlap + lid_fatbox_switches_h; + +lid_inner_kink = [thinbox_len, thinbox_len*2]; + +lid_inner_w_nom = patbox_side; +lid_inner_w = lid_inner_w_nom + lid_side_slop * 2; + +lid_seatline_w = (lid_inner_w - lid_max_switches_w)/2 - lid_switches_y_slop; + +lid_seatline_h = lid_fatbox_switches_h; + +peg_main_height = peg_straight_len + (peg_max_dia - peg_main_dia)/2/peg_slope; + +thinbox_front_z = lid_fatbox_switches_h + lid_fatbox_h - lid_thinbox_h; + +raillen = patbox_side/2 + rail_overlap; + +module LidSideProfile(){ + polygon([[-lid_top_wall, lid_inner_max_h], + [min(lid_inner_kink[0],total_len), lid_inner_max_h], + [min(lid_inner_kink[1],total_len), lid_inner_min_h], + [total_len, lid_inner_min_h], + [total_len, -lid_front_wall], + [-lid_top_wall, -lid_front_wall]]); +} + +module RailProfile(lslop=0.1){ + yt_base = thinbox_front_z; + yt = yt_base - lid_rail_behindslop; + pegx = (lid_inner_w_nom - patbox_centres)/2; + sw = lid_rail_strongwall; + + polygon([[-lslop, yt], + [pegx - peg_main_dia/2, yt], + [pegx - peg_main_dia/2, yt_base - peg_straight_len], + [sw, + yt_base - peg_straight_len - (pegx - peg_main_dia/2)/peg_slope + +sw], + [sw, + lid_seatline_h - 1], + [-lslop, + lid_seatline_h - 1]]); +} + +module LidSide(){ + overlap = [0.1, 0.1, 0.1]; + + // main side profile + rotate([90,0,0]) + linear_extrude(height= lid_side_wall) + LidSideProfile(); + + // seatline + translate(-overlap) + cube(overlap + [total_len, lid_seatline_w, lid_seatline_h]); + + // lid front + translate([-0.1, -0.1, -lid_front_wall]) + cube([total_len+0.1, lid_inner_w/2 + 0.2, lid_front_wall]); + + // lid top + translate([-lid_top_wall, -lid_side_wall, -lid_front_wall]) + cube([lid_top_wall, lid_inner_w/2 + 10, lid_front_wall + lid_inner_max_h]); + + // rail + rotate([90,0,90]) + translate([0,0,-0.1]) + linear_extrude(height=raillen+0.1) //todo + RailProfile(); + + // rail end + translate([raillen, 0,0]) + intersection(){ + rotate_extrude($fn=50) + RailProfile(lslop=0); + translate([0, 25-0.1, 0]) + cube(50, center=true); + } +} + +module Lid(){ ////toplevel + for (m=[0,1]) + mirror([0,m,0]) + translate([0, -lid_inner_w/2, 0]) LidSide(); +} + +module PegProfile(){ + polygon([[-peg_engage_depth, (peg_engage_dia - $peg_outer_slop_engage)/2], + [0, (peg_engage_dia - $peg_outer_slop_engage)/2], + [0, (peg_main_dia - $peg_outer_slop)/2], + [peg_straight_len, (peg_main_dia - $peg_outer_slop)/2], + [peg_main_height, (peg_max_dia - $peg_outer_slop)/2], + [peg_main_height+peg_top_thick, (peg_max_dia - $peg_outer_slop)/2], + [peg_main_height+peg_top_thick, (peg_inner_dia + $peg_inner_slop)/2], + [-peg_engage_depth, (peg_inner_dia + $peg_inner_slop)/2]]); +} + +module Peg(){ ////toplevel + echo($peg_outer_slop_engage); + rotate_extrude($fn=50) + rotate([0,0,-90]) + PegProfile(); +} + +module Pegs(){ ////toplevel + baseslop = 0.1; + dslops = [0, -0.5, -1.0, -1.5]; + stride = peg_max_dia + 4; + for (i=[0:len(dslops)-1]) { + translate([i*stride,0,0]) + assign($peg_outer_slop_engage= baseslop + dslops[i]) + Peg(); + } +} + +module AtFixingCentres(){ + for (fc=[-1,+1]) { + translate([patbox_side/2 + fc*patbox_centres/2, + patbox_side/2, + 0]) + children(); + } +} + +module TopPattressBox(){ + difference(){ + translate([0,0, -lid_thinbox_h]) + cube([patbox_side, patbox_side, lid_thinbox_h]); + AtFixingCentres(){ + translate([0,0,-10]) cylinder(r=peg_engage_dia/2, h=20); + } + } +} + +module Demo(){ + Lid(); + translate([0,0, thinbox_front_z]) + rotate([0,180,0]) translate([0, -patbox_side/2, 0]) + rotate([0,0,90]) union(){ + %TopPattressBox(); + color("blue") AtFixingCentres(){ + rotate([180,0,0]) Peg(); + } + } +} + +//LidSide(); +//PegProfile(); +//Peg(); +//Pegs(); +//TopPattressBox(); +//RailProfile(); +//Demo(); +//Lid(); +//translate([0,0,-lid_fatbox_switches_h]) Lid(); diff --git a/pawn.scad b/pawn.scad new file mode 100644 index 0000000..7277dcd --- /dev/null +++ b/pawn.scad @@ -0,0 +1,80 @@ +// -*- C -*- + +// shape parameters + +r1 = 2.85; +r2 = 4; a2 = 27; +r3 = r2; +r4 = 4; a4 = 18; +r5 = 30; +h6 = 7; a6 = 4; + +// coordinates + + z1 = 0; +h2 = r2 * sin(a2); z2 = z1 - h2; +h3 = r3 * sin(a2); z3 = z2 - h3; +h4 = r4 * sin(a4); z4 = z3 - h4; + +zc5 = z4 - r5 * sin(a4); +z5 = zc5 + r5 * sin(a6); z6 = z5 - h6; + +x1 = 0 - r1; +x2 = x1 + r2 * (1-cos(a2)); +x3 = x2 + r3 * (1-cos(a2)); +x4 = x3 - r4 * (1-cos(a4)); + +xc5 = x4 + r5 * cos(a4); +x5 = xc5 - r5 * cos(a6); + +x6 = x5 - h6 * tan(a6); + +htotal = r1 - z6; +echo("height", htotal); + +d = 0.01; +dx = 0.00; + +$fa=2; +$fs=0.2; + +module SegmentBasisSquare(zmin, zmax, xmin){ + translate([xmin, zmin-d]) square([-xmin+dx, zmax-zmin+d*2]); +} +module ConvexSegment(xc, zc, r, zmin, zmax){ + intersection(){ + translate([xc,zc]) circle(r=r); + SegmentBasisSquare(zmin,zmax,-50); + } +} +module ConcaveSegment(xc, zc, r, zmin, zmax){ + difference(){ + SegmentBasisSquare(zmin,zmax, xc); + translate([xc,zc]) circle(r=r); + } +} + +module PawnTemplate(){ + ConvexSegment( x1 + r1, z1, r1, z1, 50); + ConvexSegment( x1 + r2, z1, r2, z2, z1); + ConcaveSegment( x3 - r3, z3, r3, z3, z2); + ConcaveSegment( x3 - r4, z3, r4, z4, z3); + ConvexSegment( xc5, zc5, r5, z5, z4); + polygon([[x6, z6], + [x5, z5+d], + [dx, z5+d], + [dx, z6]]); +} + +module Pawn(h=htotal){ + scale(h/htotal) { + rotate_extrude(convexity=10, $fn=50){ + assign($fn=undef){ + PawnTemplate(); + } + } + } +} + +Pawn(h=30); +//PawnTemplate(); diff --git a/pawn.slic3r b/pawn.slic3r new file mode 100644 index 0000000..be356f2 --- /dev/null +++ b/pawn.slic3r @@ -0,0 +1,7 @@ +first_layer_bed_temperature = 80 +bed_temperature = 70 +skirts = 3 +first_layer_temperature = 190 +temperature = 177 +extrusion_multiplier = 0.9 +fill_density = 1.0 diff --git a/pin-hinge.scad b/pin-hinge.scad new file mode 100644 index 0000000..81d527e --- /dev/null +++ b/pin-hinge.scad @@ -0,0 +1,100 @@ +// -*- C -*- + +include + +$hinge_pin_dia = 0.795 + 0.75; +$hinge_main_dia = 4.0; +$hinge_inter_gap = 0.50; +$hinge_prong_minwidth = 3.0; +$hinge_noncrit_gap = 1.0; + +$fa = 3; +$fs = 0.05; + +module HingePinPlan(){ + circle(r= $hinge_pin_dia/2); +} + +module HingeProngPlan(behind){ + hull(){ + circle(r= $hinge_main_dia/2); + polygon([[0,0], + [-$hinge_main_dia/2, -behind], + [+$hinge_main_dia/2, -behind]]); + } +} + +module HingeGapPlan() { + circle(r = $hinge_main_dia/2 + $hinge_inter_gap); +} + +module PlanDemo(){ + HingeProngPlan(5); + %HingeGapPlan(); + translate([0,0,1]) color("red") HingePinPlan(); +} + +module HingePinUnitCell(l) { + eff_l = l + $hinge_inter_gap; + pairs = floor(eff_l / (2*($hinge_prong_minwidth + $hinge_inter_gap))); + stride = eff_l / pairs; + $hinge__prong_width = stride/2 - $hinge_inter_gap; + for (i=[0:pairs-1]) { + translate(stride * i * [1,0,0]) + children(0); + } +} + +module HingePositive(l, behind){ + HingePinUnitCell(l){ + linextr_x_yz(0, $hinge__prong_width) + HingeProngPlan(behind); + } +} + +module HingeNegative(l){ + linextr_x_yz(-0.1, l+0.1) + HingePinPlan(); + HingePinUnitCell(l){ + linextr_x_yz($hinge__prong_width, + $hinge__prong_width*2 + 2*$hinge_inter_gap) + HingeGapPlan(); + } +} + +test_l = 30; +test_wb = 12; +test_h = 12; + +test_face_gap = 0.75; + +module Demo(){ + difference(){ + HingePositive(test_l, test_h/2); + %HingeNegative(test_l); + } +} + +module TestBody(){ + linextr_x_yz(0, test_l){ + offset(delta = -test_face_gap/2) + polygon([[0,0], + [-test_wb/2, -test_h], + [+test_wb/2, -test_h]]); + } +} + +module Test(){ + difference(){ + union(){ + TestBody(); + HingePositive(test_l, test_h/2); + } + HingeNegative(test_l); + } +} + +//PlanDemo(); +//Demo(); +//TestBody(); +Test(); diff --git a/poster-tube-lid-coarse.scad b/poster-tube-lid-coarse.scad new file mode 100644 index 0000000..2ff8f3d --- /dev/null +++ b/poster-tube-lid-coarse.scad @@ -0,0 +1,6 @@ +// -*- C -*- + +//// toplevels-from: +include + +coarse = true; diff --git a/poster-tube-lid-parametric.scad.pl b/poster-tube-lid-parametric.scad.pl new file mode 100755 index 0000000..64d4d00 --- /dev/null +++ b/poster-tube-lid-parametric.scad.pl @@ -0,0 +1,234 @@ +#!/usr/bin/perl -w +use strict; + +use Math::Vector::Real; +use Math::Trig qw(pi); +use POSIX; +use Data::Dumper; + +sub TAU () { pi*2; } + +my $thick = 2.5; + +my $small_dia = 20; +my $large_dia = 30; +my $slope_angle = 45 * TAU/360; +my $jcurverad = 5; +my $tall = 50; + +my $lin_len = 2; +my $sine_size = 5; +my $sine_angle = 1.20 * TAU/8; + +my $ballend_xr = $thick/2; + +my $skew_slope = 0.7; + +my @i_sections = qw(ball0 -6 + sine0 -10 + lin0 -2 + circle 40 + lin1 2 + sine1 10 + ball2 6 + - + ); + +my @j_sections = qw(lin0 2 + - + curve1 10 + - + curveE 20 + - + curve2 -10 + - + ); + +sub point ($$$$) { + my ($ip,$it, $jp,$jt) = @_; + + my ($i_offset); + + my $i_outward = V( 0, + ($ip =~ m/0$/ ? -1 : +1), + 0 ); + + my $i_j_y_angle = 0; + + my $i_thickscale = 1.0; + my $sine_len = $sine_size * sin($sine_angle); + my $sine_height = $sine_size * (1 - cos($sine_angle)); + + if ($ip =~ m/^lin[01]$/) { + $i_offset = V( -$lin_len * $it, + 0, + 0 ); + } elsif ($ip =~ m/^circle$/) { + $i_offset = V( 0,0,0 ); + $i_outward = V( sin($it * TAU/2), + -cos($it * TAU/2), + 0 ); + } elsif ($ip =~ m/^sine[01]$/) { + my $angle = $it * $sine_angle; + $i_offset = V( -$lin_len -$sine_size * sin($angle), + 0, + +$sine_size * (1 - cos($angle)) + ); + $i_j_y_angle = $angle; + } elsif ($ip =~ m/^ball[02]$/) { + $i_j_y_angle = $sine_angle; + my $angle = $it * TAU/4; + my $dx = sin($angle) * $ballend_xr; + $i_offset = V( -$lin_len -$sine_len - $dx * cos($sine_angle), + 0, + +$sine_height + $dx * sin($sine_angle) + ); + $i_thickscale = cos($angle); + } else { + die "$ip ?"; + } + + my $i_j_y_vect = V( sin($i_j_y_angle), + 0, + cos($i_j_y_angle )); + + my $j_plus_th = $jp =~ m/2$/ ? $thick : 0; + + my $i_thick = $thick * $i_thickscale; + my $j_p_x = $small_dia/2 + $thick/2; + my $j_rs_x = $large_dia/2 + $thick/2; + my $j_dqr_x = (1-cos($slope_angle)) * $jcurverad; + my $j_q_x = $j_rs_x - $j_dqr_x; + my $j_dpq = ($j_q_x - $j_p_x) / asin($slope_angle); + #print STDERR "($j_q_x - $j_p_x) / asin($slope_angle); => $j_dpq\n"; + my $j_p_y = 0; + my $j_q_y = $j_p_y + $j_dpq * cos($slope_angle); + my $j_r_y = $j_q_y + sin($slope_angle) * $jcurverad; + my $j_s_y = $tall; + my $j_qrc_x = $j_rs_x - $jcurverad; + my $j_qrc_y = $j_r_y; + + my $j_x; + my $j_y; + + if ($jp =~ m/^curveE$/) { + my $angle = ($jt + 1) * TAU/2 - $slope_angle; + $j_x = $j_p_x + $i_thick/2 * cos($angle); + $j_y = $j_p_y + $i_thick/2 * sin($angle); + } elsif ($jp =~ m/^curve[12]$/) { + my $angle = $slope_angle * $jt; + my $outwards = $jp =~ m/1/ ? -1 : +1; + $j_x = $j_qrc_x + cos($angle) * ($jcurverad + $outwards * $i_thick/2); + $j_y = $j_qrc_y - sin($angle) * ($jcurverad + $outwards * $i_thick/2); + } elsif ($jp =~ m/^lin0$/) { + $j_x = $j_rs_x + $i_thick * (+0.5 - $jt); + $j_y = $j_s_y; + $i_offset->[2] = 0; + } else { + die "$jp ?"; + } + + $j_y -= $j_qrc_y; + + if ($j_y > 0) { + $i_j_y_vect = V(0,0,1); + } + +# print STDERR "@_ $j_x $j_y $i_offset $i_outward\n"; + return + $i_offset + + $j_x * $i_outward + + $i_j_y_vect * $j_y + + V(0,0,1) * $j_qrc_y + + V(0,0,-$tall) ; +} + +sub get_sections_ptvals { + my $last_ptval; + my @out; + while (my $name = shift @_) { + if ($name eq '-') { + push @out, $last_ptval; + } else { + my $count = shift @_; + my $neg = sub { $_[0] }; + if ($count < 0) { + $count = -$count; + $neg = sub { 1- $_[0] }; + } + foreach (my $ix = 0; $ix < $count; $ix++) { + push @out, [ $name, $neg->($ix/$count) ]; + } + $last_ptval = [ $name, $neg->(1.0) ]; + } + } + return @out; +} + +our @points; +our %point_indices; +our @triangles; + +my @ipts; +my @jpts; + +my $qi; +my $qj; + +sub triangle { + my @pixs; + foreach my $pval (@_) { + my $pix = $point_indices{$pval} + //= ((push @points, $pval), $#points); + if (grep { $pix eq $_ } @pixs) { + print "// elide @{ $ipts[$qi] } @{ $jpts[$qj] }\n"; + return; + } + push @pixs, $pix; + } + push @triangles, [ $qi,$qj, \@pixs ]; +} + +sub make_sheet () { + @ipts = get_sections_ptvals(@i_sections); + @jpts = get_sections_ptvals(@j_sections); + my @sheet; + foreach my $ipt (@ipts) { + my @row = (); + foreach my $jpt (@jpts) { + push @row, &point(@$ipt, @$jpt); + } + push @sheet, \@row; + } + foreach ($qi=0; $qi<$#ipts; $qi++) { # i direction does not wrap + my $qi2 = $qi+1; + foreach ($qj=0; $qj<@jpts; $qj++) { # j direction does wrap + my $qj2 = ($qj+1) % @jpts; + my $p0 = $sheet[$qi][$qj]; + triangle($p0, $sheet[$qi2][$qj], $sheet[$qi2][$qj2]); + triangle($p0, $sheet[$qi2][$qj2], $sheet[$qi][$qj2]); + } + } +} + +sub pv ($) { + my $v = shift @_; + return "[".(join ',', @$v)."]"; +} + +sub write_out () { + print "module ImplHeadCup(){ polyhedron(points=[\n" or die $!; + print pv($_),",\n" or die $! foreach @points; + print "],faces=[\n" or die $!; + foreach (@triangles) { + print pv($_->[2]),", // @{ $ipts[$_->[0]] } @{ $jpts[$_->[1]] }\n" or die $!; + } + print "],convexity=10); }\n" or die $!; + print < +include + +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 + +// bayonet definition + +bayo_a = [ bayo_entry_x, 0 ]; +bayo_p = [ 0, bayo_entry_z ]; +bayo_n = [ 0, bayo_behind-bayo_inramp_z ]; +bayo_m = [ bayo_inramp_x, bayo_behind ]; +bayo_l = bayo_m + bayo_interf * [ 1/bayo_interf_slope, 1 ]; +bayo_k = bayo_l + [ bayo_interf_width, 0 ]; +bayo_j = bayo_k + bayo_interf * [ 1/bayo_interf_slope, -1 ]; +bayo_i = bayo_j + [ rivet_width + inside_rivet_gap, 0 ]; +bayo_h = [ bayo_i[0], bayo_behind + bayo_gap + bayo_interf ]; +bayo_g = [ bayo_m[0] - rivet_width, bayo_h[1] ]; + +bayo_e = [-bayo_p[0], bayo_p[1]] - [rivet_entry_width,0]; +bayo_d = [-bayo_a[0], bayo_a[1]] - [rivet_entry_width,0]; +bayo_c = bayo_d + [0,-5]; +bayo_b = bayo_a + [0,-5]; + +bayo_f = [ bayo_e[0], bayo_g[1] + (bayo_e[0] - bayo_g[0]) ]; + +bayo_polygon = [ bayo_a, + bayo_b, + bayo_c, + bayo_d, + bayo_e, + bayo_f, + bayo_g, + bayo_h, + bayo_i, + bayo_j, + bayo_k, + bayo_l, + bayo_m, + bayo_n, + bayo_p ]; + +echo(bayo_polygon); + +// CATCH + +cppxC = 0.41 * sm_inner_rad * TAU; + +// catch pin + +cpp_adj = (bayo_n[0] - bayo_f[0]) * (1 - sm_inner_rad / bayo_nom_rad); +// radius scaling due to nom and actual radius difference in +// bayo entry construction + +cppa = bayo_f + [1,-1] * catch_pin_slop + [1,0] * cpp_adj; +cppb = bayo_g + [1,-1] * catch_pin_slop + [1,0] * cpp_adj; +cppd = [ bayo_n[0] + - catch_pin_slop - catch_pin_slop_x_extra, + -catch_stalk_above_gap ]; +cppi = [ cppa[0], cppd[1] ]; +cppc = [ cppd[0], cppb[1] ]; +cpph = cppd + [0,-1] * catch_stalk_h; +cppe = cppd + [0,-1] * (catch_knob_above_gap + catch_knob_dia/2); +cppf = [ cppa[0], cppe[1] ]; +cppg = [ cppa[0], cpph[1] ]; +cppB = 0.5 * (cppf + cppe); + +echo("RR", sm_inner_rad / bayo_nom_rad); + +// catch assembly depression below pin + +cppy6 = cppB[1] - (catch_knob_dia/2 + + (cppc[1] - cppd[1]) + + catch_stalk_below_gap); +cpp7 = [ cppB[0], cppy6 + catch_cr ]; +cpp11 = cpp7 + [1,0] * catch_cr; +cppy9 = cppy6 + catch_strap_width * 1/3; +cpp9 = [ cpp7[0] + catch_cr * 2, cppy9 ]; +cpp8 = cpp9 + [0,-1] * catch_cr; +cpp10 = cpp8 + [-1,0] * catch_cr; +cppC = [ cppxC, cpp9[1] ]; +cppD = cppC + [0,-1] * catch_strap_width; + +// catch assembly stalk and so on + +catch_cr3 = catch_cr + catch_stalk_h; + +cppF = [ cppg[0] - catch_stalk_eff_bend_rad, cppd[1] ]; +cpp4 = [ cppg[0] - catch_stalk_len, cpph[1] ] + [1,-1] * catch_cr; +cpp5 = [ cpp4[0], cppC[1] + catch_cr ]; +cpp2 = cpp5 + [-1,0] * (catch_cr * 2 + catch_stalk_base_width); +cpp2r = cpp2 + [1,0] * catch_cr; +cpp2d = cpp2 + [0,-1] * catch_cr; +cpp3 = [ cpp2[0] + catch_cr + catch_cr3, cppd[1] - catch_cr3 ]; +cppA = [ -cppxC, cpp9[1] ]; +cppE = [ cppA[0], cppD[1] ]; + +catch_assembly_dy = -cppy9 + catch_strap_width; + + +module MainProfile(){ + main_cnr_pos = [ side_thick, top_z ] - [1,1]*main_cnr; + difference(){ + union(){ + translate(main_cnr_pos){ + intersection(){ + difference(){ + circle(r = main_cnr); + circle(r = main_cnr * 0.5); + } + square([10,10]); + } + } + polygon([[ -top_middle_dr, middle_bot_z ], + [ -top_middle_dr, top_z ], + [ main_cnr_pos[0], top_z ], + [ side_thick, main_cnr_pos[1] ], + [ side_thick, -side_height ], + [ side_taper, -side_height ], + [ 0, -rivet_posn ], + [ 0, by_oring_z ], + [ -oring_x_rad, by_oring_z ], + ], + convexity=10); + } + translate([ oring_mid_dia/2 - main_dia/2, 0 ]) + hull(){ + translate([ 0, oring_y_rad ]) + scale([ 1/oring_oblate * (oring_compress_more+1) , oring_oblate ]) + circle(oring_thick/2); + translate([ 0, oring_y_rad*2 - oring_thick/2 ]) + circle(oring_thick/2); + } + } +} + +module StrapLoopProfile(){ + circle(r = strap_loop_thick/2); +} + +module StrapLoop(){ ////toplevel + bigrad = strap_loop_inside/2 + strap_loop_thick/2; + extralen = strap_loop_thick * 5; + + intersection(){ + rotate([strap_loop_elevation, 0,0]){ + for (x= [ -1, +1 ] * bigrad) { + translate([x, -extralen, 0]) + rotate([-90,0,0]) + linear_extrude(height= extralen + strap_loop_strlen + 0.1, + convexity=10) + StrapLoopProfile(); + } + translate([0, strap_loop_strlen, 0]){ + intersection(){ + rotate_extrude(convexity=10) + translate([bigrad, 0,0]) + StrapLoopProfile(); + translate([0,50,0]) + cube([100,100,100], center=true); + } + } + } + translate([0, 50, 0]) + cube(100, center=true); + } +} + +module RotateProjectSlice(offset, slice_size, nom_rad, real_rad){ + // nom_rad > real_rad + rotate([0,0, atan2(offset, nom_rad) ]){ + intersection(){ + translate([-offset, -10, 0]) + rotate([90,0,0]) + linear_extrude(height= nom_rad*2, convexity=50) + children(0); + translate([0,0, -25]) + cylinder(h=50, r= real_rad); + translate([0,0, -25]) + linear_extrude(height= 50, convexity=50) + polygon([ [ 0,0 ], + [ -slice_size, -real_rad*2 ], + [ +slice_size, -real_rad*2 ] ]); + } + } +} + +module RotateProject(x_min, x_max, slice_size, nom_rad, real_rad){ + offs = [ for (i=[ x_min : + slice_size : + x_max + slice_size ]) i ]; + echo (offs); + for (off=offs) + RotateProjectSlice(off, slice_size, nom_rad, real_rad) + children(0); +} + +module BayonetCutout(){ + RotateProject(bayo_c[0], bayo_i[0], bayo_slice_size, + bayo_nom_rad, + bayo_real_rad) + translate([-0.5 * (bayo_a[0] + bayo_d[0]), 0]) + polygon(bayo_polygon, convexity=10); +} + +module ProfilesDemo(){ ////toplevel + translate([-10,0]) MainProfile(); + translate([+10, -side_height]) polygon(bayo_polygon, convexity=10); +} + +module LimitForHandling(){ ////toplevel + hull() for (r=[0,180]) + rotate([0,0,r]) { + for (rs=[-1,+1]) { + for (xd=[0,1]) { + rotate([0,0, rs * handling_angle/2]) { + translate([rs * xd * main_dia/2 * tan(handling_angle/2), + main_dia/2 + side_thick - main_cnr, + top_z - main_cnr]) { + mirror([0,0,1]) + cylinder(r= main_cnr, h=50); + sphere(main_cnr); + } + } + } + } + } + hull() rotate_extrude(convexity=10){ + translate([ handling_dia/2 - main_cnr, top_z - main_cnr ]) { + circle(r = main_cnr); + mirror([0,1]) square([ main_cnr, 50 ]); + } + } + //cylinder(r= handling_dia/2, h=20); +} + +module Cover(){ ////toplevel + render() difference(){ + intersection(){ + union(){ + rotate_extrude(convexity=10) + translate([main_dia/2, 0]) + MainProfile(); + translate([0,0, middle_bot_z]) + cylinder(h= top_thick_middle, r = main_dia/2 - top_middle_dr + 1); + } + LimitForHandling(); + } + for (r=[0,180]){ + rotate([0,0, r]) + translate([0,0, -side_height]) + BayonetCutout(); + rotate([0,0, r + asin((-oring_rm_beside) / (main_dia/2))]) + translate([0, + oring_mid_dia/2 + oring_thick/4 * oring_rm_scale, + oring_y_rad * 1.5]) + rotate([-oring_rm_angle, 0, 0]) + mirror([0,0,1]) + cylinder(r = oring_thick/4 * oring_rm_scale, h=20); + } + for (r=[0 : 60 : 179]) { + rotate([0,0, r]) { + height = top_thick_middle - brace_above_below*2; + translate([0,0, middle_bot_z + brace_above_below + height/2 ]) + cube(center=true, [ oring_bore - brace_end_shorter, + brace_hole_width, height ]); + } + } + } + if (enable_head_cups) + for (r=[0,180]) + rotate([0,0,r]) + translate([-implheadcup_large_dia * .5 - implheadcup_thick/2, + -implheadcup_large_dia * .0, + middle_bot_z + 0.1]) + ImplHeadCup(); + +// translate(strap_loop_thick * [-0.5, 0, +1]) +// translate([handling_dia/2, 0, -side_height]) +// rotate([0,180,0]) rotate([0,0,90]) +// StrapLoop(); +} + +module SavingHole(){ + translate([0,0, -10]) + cylinder(r= main_dia/2 - jig_rim, h=20); +} + +module Jig(){ ////toplevel + difference(){ + union(){ + translate([0,0, -side_height]){ + cylinder(r= main_dia/2 + jig_thick, h= side_height + jig_thick); + } + translate([-jig_mark_rad, 0, jig_thick - jig_mark]) + cube([jig_mark_rad*2, jig_mark, jig_mark]); + } + translate([0,0, -side_height-1]) + cylinder(r= main_dia/2, h= side_height + 1); + SavingHole(); + translate([0,0, -rivet_posn]) + rotate([90, 0,0]) + translate([0,0, -100]) + cylinder(r= jig_hole_dia/2, h = 200); + } +} + +module CoverPrint(){ ////toplevel + rotate([0,180,0]) Cover(); +} + +module CoverTest2(){ ////toplevel + difference(){ + Cover(); + SavingHole(); + } +} + +module CoverTest1(){ ////toplevel + difference(){ + CoverTest2(); + difference(){ + for (r= [ 40, 147 ]){ + rotate([0,0, r]){ + translate([0,0, -10]) { + cube([ main_dia*3, main_dia * .53, 18], center=true); + } + } + } +// translate([ 50, 0, 0 ]) +// cube([ 100, +// strap_loop_inside + strap_loop_thick*2 + 1, +// 100 ], +// center=true); + } + } +} + +module ImplHeadCupTest(){ ////toplevel + for (r=[0,180]) + rotate([0,0,r]) + translate([-17,0,0]) + ImplHeadCup(); +} + +module SomeStrap(width, cut_width=0){ + // children(0) is to add, (1) subtract + difference(){ + union(){ + cylinder(r=sm_outer_rad, h=width); + StrapMountProtrusion(smc_cnr_c_x + sm_closure_cnr, + smc_max_y, + sm_closure_cnr, + width); + children(0); + } + translate([0,0,-1]) + cylinder(r=sm_inner_rad, h=max(width+2, cut_width)); + translate(smc_pos) + StrapMountBolt(5, width); + translate(smc_pos) + cube([ sm_bolt_tighten_allow, 40,100 ], center=true); + children(1); + } +} + +module StrapMountBolt(l_delta, strap_width){ ///toplevel + // positioned relative to smc_pos + translate([(smc_bolt_nut_eff_thick - sm_bolt_head_thick)/2, + smc_bolt_y, + strap_width/2]){ + translate([ -sm_bolt_shaft/2-1, 0,0 ]){ + rotate([0,90,0]) cylinder(r= sm_bolt_dia/2, h= sm_bolt_shaft+2); + } + translate([ -sm_bolt_shaft/2, 0,0 ]) + rotate([0,-90,0]) + cylinder($fn=6, r=smc_bolt_nut_dia/2, + h=smc_bolt_nut_eff_thick + l_delta); + translate([ sm_bolt_shaft/2, 0,0 ]) + rotate([0,90,0]) + cylinder(r=sm_bolt_head_dia/2, h=sm_bolt_head_thick + l_delta); + } +} + +module StrapMountProtrusion(half_x, max_y, cnr, width){ + translate(smc_pos){ + linear_extrude(height=width, convexity=10){ + hull(){ + for (m = [0,1]) mirror([m,0,0]) { + translate([-(half_x - cnr), max_y - cnr]) + circle(r=cnr); + translate([-half_x, -sm_inner_rad]) + square([1,1]); + } + } + } + } +} + +module StrapMount(){ ////toplevel + SomeStrap(sm_main_width){ + rotate([0,0,180]){ + StrapMountProtrusion(strap_loop_inside/2 + strap_loop_thick, + strap_loop_thick, + sm_closure_cnr, + sm_main_width); + translate(smc_pos + + [0,0, sm_main_width] + + strap_loop_thick * [ 0, 0.5, -1.0 ]) + StrapLoop(); + } + union(){ }; + } +} + +module WallScrewHoleSlot(){ ////toplevel + ds = [-1,+1] * wm_screw_slot/2; + linextr_x_yz(-(wm_thick + 1), 1) { + hull(){ + for (d = ds) + translate([d, 0]) + circle(r = wm_screw_dia/2); + } + } + hull(){ + for (d = ds){ + translate([0, d, 0]){ + linextr_x_yz(0, 1) + circle(r = wm_screw_head/2); + linextr_x_yz(-(wm_screw_head - wm_screw_dia)/2, 0) + circle(r = wm_screw_dia/2); + } + } + } +} + +module WallMountMounts(){ + linextr(0, wm_z_max){ + translate([ -sm_outer_rad, 0 ]) + rectfromto([ 0, wm_lhs_y_min ], + [ wm_thick, wm_y_slot1_max ]); + } +} +module WallMountScrewHoles(){ + translate([ -sm_outer_rad + wm_thick, 0, wm_z_slotc_screw ]) { + translate([ 0, wm_y_slotc_screw, 0 ]) + WallScrewHoleSlot(); + translate([ 0, -wm_y_slotc_screw, 0 ]) + rotate([90,0,0]) + WallScrewHoleSlot(); + } +} + +module WallMount(){ ////toplevel + SomeStrap(sm_main_width, wm_z_max + 2){ + WallMountMounts(); + WallMountScrewHoles(); + } +} + +module WallMountBaseRingCut(){ + circle(r = wmb_mount_cut_rad); +} + +module WallMountBaseMounts(){ + linextr(0, wmb_z_max) { + difference(){ + rectfromto([ -sm_outer_rad, -wmb_y_mount_max ], + [ wmb_x_outer, +wmb_y_mount_max ]); + WallMountBaseRingCut(); + } + } +} + +// screws, nuts, slots for nuts to go down into +module WallMountBaseScrewsEtc(){ ////toplevel + for (my=[0,1]) { + mirror([0, my, 0]) { + translate([wmb_x_screw, 0, wmb_z_screw]) { + linextr_y_xz(wmb_y_screw_end, + wmb_y_screw_end + 50) + circle(r = wmb_screw_dia/2); + linextr_y_xz(wmb_y_screw_end + web_screw_len, + wmb_y_screw_end + 50) + circle(r = wmb_screw_head_dia/2); + linextr_y_xz(wmb_y_nut_min, + wmb_y_nut_min + wmb_nut_th) { + hull(){ + rotate(30) + circle(r = wmb_nut_rad, $fn = 6); + translate([0, 50]) + square(wmb_nut_across, center=true); + } + } + } + } + } +} + +module WallMountForBase(){ ////toplevel + SomeStrap(sm_main_width, wm_z_max + 2){ + union(){ + WallMountMounts(); + WallMountBaseMounts(); + } + union(){ + WallMountScrewHoles(); + WallMountBaseScrewsEtc(); + } + } +} + +module WallMountForBaseFixingsTest(){ ////toplevel + intersection(){ + WallMountForBase(); + linextr(-100,100) + rectfromto([ -sm_outer_rad-10, -wm_y_min ], + [ wmb_x_outer + 1, -100 ]); + } +} + +module WallMountBaseFixingsTest(){ ////toplevel + intersection(){ + WallMountBase(); + linextr(-2,100) + rectfromto([ -sm_outer_rad-10, -wm_y_min ], + [ wmbb_x_outer + 1, -100 ]); + } +} + +module WallMountBasePillarsPlan(){ + for (my = [0,1]) { + mirror([0, my]) { + rectfromto([ -sm_outer_rad, wmbb_y_max - wmb_mount_y_width ], + [ wmbb_x_outer, wmbb_y_max ]); + } + } +} + +// trim parts that are would foul the wall +module WallMountTrimWallFoulPlan(){ + translate([ -sm_outer_rad, 0]) + rectfromto([ -wmbb_r_top, -(wmbb_r_top + 1) ], + [ 0, +(wmbb_r_top + 1) ]); +} + +module WallMountBase(){ ////toplevel + difference(){ + union(){ + // vertical blocks rising to join to wall mount + linextr(wmbb_z_min, wmb_z_max) { + difference(){ + WallMountBasePillarsPlan(); + WallMountBaseRingCut(); + } + } + + hull(){ + linextr(wmbb_z_flat_whole_min, wmbb_z_flat_max) + circle(r = wmbb_r_top); + linextr(wmbb_z_min, wmbb_z_flat_max) + circle(r = wmbb_r_bottom); + } + linextr(wmbb_z_min, wmbb_z_flat_max) { + hull(){ + WallMountBasePillarsPlan(); + circle(r = wmbb_r_bottom); + } + } + } + + // cutaway for mount part + linextr(-wmb_mount_gap_z, wmb_z_max+1) { + for (my = [0,1]) { + mirror([0, my]) + rectfromto([ -sm_outer_rad-1, wmb_y_mount_max + wmb_mount_gap_xy ], + [ wmb_x_outer + wmb_mount_gap_xy, 1 ]); + } + } + + linextr(wmbb_z_min - 1, wmb_z_max + 1) + WallMountTrimWallFoulPlan(); + WallMountBaseScrewsEtc(); + } +} + +module WallMountBaseCutJigPlan(){ ////toplevel + difference(){ + union(){ + circle(r = wmbb_r_top); + } + + translate([ wmb_jig_around_gap, 0 ]) + WallMountTrimWallFoulPlan(); + + offset(delta = wmb_jig_around_gap) + WallMountBasePillarsPlan(); + } +} + +module WallMountBaseCutJig(){ ////toplevel + translate([ 0,0, wmbb_z_flat_max + 0.5 ]) + linextr(0, wmb_jig_th) + WallMountBaseCutJigPlan(); +} + +module WallMountForBaseDemo(){ ////toplevel + render() WallMountForBase(); + color("blue") render() WallMountBase(); + %WallMountBaseScrewsEtc(); + %WallMountBaseCutJig(); +} + +module CatchAssemblyCoreProfile(){ + difference(){ + union(){ + hull(){ + translate(cpp3) circle(r= catch_cr3); + polygon([ cpp3, + cpp2r, + cpp5, + cpph, + cppd + ]); + } + polygon([cppD, + cppC, + cpp9, + cpp10, + cpp11, + cpp4, + cpp2r, + cpp2d, + cppA, + cppE + ]); + translate(cpp8) circle(r= catch_cr); + } + hull(){ + translate(cpp4) circle(r= catch_cr); + translate(cpp5) circle(r= catch_cr); + translate(cpp7) circle(r= catch_cr); + polygon([cpp4, + cppg, + cpph, + cpp10, + cpp11, + ]); + } + translate(cpp2) circle(r= catch_cr); + } + // if cpp11 is above cpp10, the subtracted hull above + // can go down too far. Ensure we do not cut off below cppy6. + polygon([ cppE, + cppD, + cpp9, + [ cpp9[0], cppy6 ], + [ cpp7[0] - catch_cr, cppy6 ], + cpp2d + ]); +} + +module CatchTipProfile(dy){ + ddy = [0,dy]; + intersection(){ + translate(cppF){ + difference(){ +// circle(r = dist2d(cppF, cppd)); + //circle(r = dist2d(cppF, cppa)); + } + } + polygon([ cppa, + cppi + ddy, + cppd + ddy, + cppc, + cppb ]); + } +} + +module CatchHeadProfile(){ + polygon([ cppd, + cppd, + cppi, + cppf, + cppe, + cpph ]); +} + + +module CatchCore(){ /////toplevel + linear_extrude(height=catch_strap_thick, convexity=10) + CatchAssemblyCoreProfile(); + + hull(){ + linear_extrude(height=catch_head_th, convexity=10) + CatchTipProfile(0); + linear_extrude(height=catch_tip_th, convexity=10) + CatchTipProfile(catch_tip_th - catch_head_th); + } + + linear_extrude(height=catch_head_th, convexity=10) + CatchHeadProfile(); + + translate(concat(cppB,[0])) hull(){ + translate([0,0, catch_knob_height + catch_head_th - catch_knob_dia/2]) + sphere(r = catch_knob_dia/2); + cylinder(r = catch_knob_dia/2, h = 0.1); + } +} + +module CatchPreDistort(){ /////toplevel + scale(100 / sm_inner_rad) + rotate([90,0,0]) + CatchCore(); +} + +module CatchAssembly(){ /////toplevel + rotate([0,0, -(cppe[0] + cppB[0] + catch_pin_slop) / sm_inner_rad * 360/TAU]) + translate([0,0, catch_assembly_dy]) + scale(sm_inner_rad / 100) + import(str("poster-tube-lid,CatchPostDistort-fa", + (coarse ? 20 : 3), + ".stl"), + convexity=20); + + SomeStrap(catch_strap_width){ + union(){ } + union(){ + translate([-200, -200, -200]) + cube([400, 200, 400]); + } + } +} + +module CatchDemo(){ /////toplevel + color("blue") translate([0,0, + -catch_assembly_dy + ]) + CatchAssembly(); + translate([0,0,+side_height + ]) + Cover(); +} + +module CatchDemoS(){ /////toplevel + color("blue") translate([0,0, + -catch_assembly_dy + ]) + CatchAssembly(); + intersection(){ + translate([0,0,+side_height + ]) + Cover(); + mirror([0,1,0]) translate([-250,33,0]) cube([500,500,500]); + } + color("black") + translate([0,-33,0]) + cube([6.15, 2,2], center=true); +} + +module CatchPinProfileDemo(){ /////toplevel + translate([0, 0 * -bayo_behind,0]) { + echo("G ", + bayo_n[0] - bayo_e[0]); + color("blue") translate([0,0, + +1, + ]) { + CatchAssemblyCoreProfile(); + CatchHeadProfile(); + } + translate([0,0,10]) + color("red") + CatchTipProfile(0); + + polygon(bayo_polygon, convexity=10); + + // adhoc show a position + color("purple") + translate(concat( + cppa, + 10 + )) difference(){ circle(2.5); circle(2.0); } + + } +} + +//ProfilesDemo(); +//BayonetCutout(); +//MainProfile(); +//Cover(); +//Jig(); +//CoverTest(); diff --git a/powerbank-anker-10000.eps b/powerbank-anker-10000.eps new file mode 100644 index 0000000..01359d8 --- /dev/null +++ b/powerbank-anker-10000.eps @@ -0,0 +1,89 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Fri Feb 5 23:12:37 2021 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 149 274 598 1452 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 149 274 598 1452 +%%EndPageSetup +q 149 274 449 1178 rectclip +1 0 0 -1 0 1612 cm q +0 g +360.93 1337.57 m 298.676 1334.211 219.23 1335.672 183.594 1274.547 c 142.047 + 1187.812 152.582 1088.758 149.211 995.395 c 151.73 795.32 144.426 595.004 + 156.238 395.148 c 157.656 328.41 155.359 251.004 205.434 199.816 c 263.293 + 157.641 339.281 161.27 407.469 160.113 c 465.984 160.328 537.961 171.273 + 565.277 230.957 c 601.477 310.977 590.16 401.633 593.281 486.977 c 592.355 + 612.16 596.633 737.312 597.473 862.445 c 596.184 971.23 597.672 1080.117 + 595.984 1188.836 c 591.852 1244.773 555.328 1300.199 498.418 1313.117 c + 459.289 1331.371 416.695 1338.148 373.711 1336.93 c 367.316 1337.203 l +h +360.93 1337.57 m f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/powerbank-anker-10000.svg b/powerbank-anker-10000.svg new file mode 100644 index 0000000..5999a71 --- /dev/null +++ b/powerbank-anker-10000.svg @@ -0,0 +1,59 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/powerbank-bike-clamp.scad b/powerbank-bike-clamp.scad new file mode 100644 index 0000000..2a755a8 --- /dev/null +++ b/powerbank-bike-clamp.scad @@ -0,0 +1,431 @@ +// -*- C -*- + +// Print one of each: +// Main +// TubeClampRight + +include + +tube_dia = 22.4; + +hinge_around = 2.5; +hinge_pin = 1.8 + 0.75; +main_th = 3; +minor_wall_min = 1; + +screw = 5.0 + 0.75; +screw_head = 8.7 + 1.2; +screw_head_space_above = 10; +screw_nut_across = 7.9 + 0.75; +screw_nut_th = 3.9 + 0.75; + +knob_behind_across = 12.2 + 0.75; +behind_knob_th = 5; +knob_standout_h = 2; + +clamp_min_width = 20; + +clamp_gap = 2; + +corner_rounding_r = 10; + +lower_th = 1; + +overlap_l = 0.1; + +bridge_slop = 1.2; + +hinge_lobe_per = 10; +hinge_gap_z = 0.75; +hinge_gap_xy = 0.75; + +$fs = 0.1; +$fa = 5; + +bank_eps_bbox_x = [149, 598]; +bank_eps_bbox_y = [274, 1452]; + +bank_y_sz = 102.25 + 0.50 + 3.2; +bank_x_sz = (26.0 + 0.5); +bank_recess_y = 5; + +strap_th = 3; +strap_w = 5; +strap_above = 2.5; + +strap_around_over = 1.0; +strap_around_attach = 2.0; + +retainer_walls = [18, 30]; + +bank_profile_scale_bodge = 1.0; + +bank_output_ctr = [ 12.5, 11.5 ]; // from nearest corner +bank_output_sz = [ 11.0, 10.5 ]; + +mounted_pos_y_offset_lim = -100; + +liner_th = 0.8; + +brace_r = 5; +brace_len = 50; + +straps_y_adj = [ 3.5, + 0, + 0 ]; + +// calculated + +straps_y = [ -bank_y_sz * 0.25, // these entries are special and used + +bank_y_sz * 0.25, // for the brace struts + 0 ]; + +screw_head_behind = main_th; +endwall_th = main_th; + +bank_recess_dx = minor_wall_min; + +pspt_to_mm = 25.4 / 72; + +main_r = tube_dia/2 + main_th; + +screw_max_y_lhs = -main_r -screw_nut_across/2; +screw_max_y_rhs = -main_r -knob_behind_across/2; + +screw_y = min(screw_max_y_lhs, + screw_max_y_rhs); + +bot_y = screw_y -max( screw_nut_across/2, knob_behind_across/2 ) + -minor_wall_min; + +holder_x_sz = bank_x_sz + bank_recess_dx*2; +bank_bot_y = strap_above + strap_th; +strap_r = strap_th; + +brace_total_len = brace_len + main_th; +brace_ctrs_y_nom = [ straps_y[0] - (brace_r + strap_w/2), + straps_y[1] + (brace_r + strap_w/2) ]; + +brace_ctrs_y = [ (straps_y + straps_y_adj)[0] + (brace_r + strap_w/2), + (straps_y + straps_y_adj)[1] + (brace_r + strap_w/2) ]; + +clamp_width_actual = max(clamp_min_width, holder_x_sz); + +hinge_lobes = floor(clamp_width_actual / hinge_lobe_per); +hinge_stride = (clamp_width_actual + hinge_gap_z) / hinge_lobes; + +hinge_outer_r = hinge_around + hinge_pin/2; +hinge_y = tube_dia/2 + hinge_outer_r; + +top_cnr_r = min(endwall_th, main_th); + +mounted_pos_y_offset = max(mounted_pos_y_offset_lim, + bot_y - (-(bank_y_sz/2 + endwall_th))); + + +module TubePlan(){ circle(r = tube_dia/2); } +module HingePinPlan(){ translate([0, hinge_y]) circle(r= hinge_pin/2); } +module HingeBodyPlan(){ translate([0, hinge_y]) circle(r= hinge_outer_r); } + +module TubeClampLeftPlan(){ + difference(){ + union(){ + polygon([[ 0, hinge_y + hinge_outer_r ], + [ -(main_r + overlap_l), hinge_y + hinge_outer_r ], + [ -(main_r + overlap_l), bot_y ], + [ -clamp_gap/2, bot_y ], + [ -clamp_gap/2, 0, ], + [ 0, 0, ], + ]); + HingeBodyPlan(); + } + TubePlan(); + HingePinPlan(); + } +} + +module TubeClampLeft() { ////toplevel + linextr(-clamp_width_actual/2, clamp_width_actual/2) + TubeClampLeftPlan(); +} + +module TubeClampRightPlan(){ ////toplevel + difference(){ + // It broke at the inside corner, round these a bit + offset(r=-corner_rounding_r) + offset(r=+corner_rounding_r) + difference(){ + union(){ + rectfromto([ clamp_gap/2, bot_y ], + [ clamp_gap/2 + behind_knob_th, 0 ]); + intersection(){ + circle(r= main_r); // maybe split off from main_r and increase? + union(){ + rectfromto([0,0], + main_r * [5,5]); + rectfromto([ clamp_gap/2, main_r*5 ], + main_r * [2,-5]); + } + } + HingeBodyPlan(); + } + TubePlan(); + } + HingePinPlan(); + } +} + +module Screws(){ + linextr_x_yz(-main_r*5, main_r*5) + translate([screw_y, 0]) + circle(r= screw/2); + + translate([0, screw_y, 0]) { + linextr_x_yz(-(clamp_gap/2 + screw_nut_th), 0) + square([screw_nut_across, + screw_nut_across / cos(30) + bridge_slop*2], + center=true); + + linextr_x_yz(-(main_r + bank_recess_y + screw_head_space_above), + -(clamp_gap/2 + screw_nut_th + screw_head_behind)) + square([screw_head, screw_head + bridge_slop*2], + center=true); + } +} + +module SomeClamp(hinge_alt=false){ + difference(){ + linextr(-clamp_width_actual/2, clamp_width_actual/2) + children(0); + + Screws(); + + for (i=[0 : hinge_lobes-1]) { + translate([0, + hinge_y, + -clamp_width_actual/2 + i * hinge_stride + + (hinge_alt ? hinge_stride/2 : 0) + ]) + linextr(-hinge_gap_z, hinge_stride/2) + square(hinge_outer_r*2 + hinge_gap_xy, center=true); + } + } +} + +module PowerBankItselfSidePlan(){ + translate([0, bank_bot_y]){ + minkowski(){ + circle($fn=8, r=liner_th); + scale( bank_profile_scale_bodge * + bank_x_sz / ( ( + bank_eps_bbox_x[1] - + bank_eps_bbox_x[0] + ) * pspt_to_mm )) + translate(pspt_to_mm * + [-0.5 * (bank_eps_bbox_x[0] + + bank_eps_bbox_x[1]), + -bank_eps_bbox_y[0]]) + import("powerbank-anker-10000.dxf", convexity=5); + } + } +} + +module PowerBankItself(){ ////toplevel + rotate([0,90,0]) + linextr_y_xz(-bank_y_sz/2, + +bank_y_sz/2) + PowerBankItselfSidePlan(); +} + +module PowerBankSidePlan(){ ////toplevel + render() difference(){ + rectfromto([ -holder_x_sz/2, 0 ], + [ +holder_x_sz/2, bank_recess_y + bank_bot_y ]); + + PowerBankItselfSidePlan(); + } +} + +module PowerBankStrapCut(){ ////toplevel + difference(){ + rectfromto([ -holder_x_sz, -0.05 ], + [ +holder_x_sz, strap_th + strap_r ]); + hull(){ + for (sx=[-1,+1]) { + translate([sx * (holder_x_sz/2 - strap_r + 0.1), + strap_th + strap_r]) + circle(strap_r); + } + } + } +} + +module PowerBankHolderTest(){ ////toplevel + difference(){ + linextr(-1,5) PowerBankSidePlan(); + linextr(0, strap_w) PowerBankStrapCut(); + } +} + +module EndRetainer(depth){ ////toplevel + translate([0, -bank_y_sz/2, 0]) { + linextr_y_xz(-endwall_th, 0) + rectfromto([ 0, -holder_x_sz/2 ], + [ -depth, +holder_x_sz/2 ]); + + for (m=[0,1]) { + mirror([0,0,m]) { + linextr(-holder_x_sz/2, -bank_x_sz/2){ + hull(){ + rectfromto([ 0, -endwall_th ], + [ depth, 0 ]); + rectfromto([ 0, 0 ], + [ 0.1, depth-0.1 ]); + } + } + } + } + } +} + +module BraceTubePlan(){ + intersection(){ + circle(r= brace_r); + rectfromto(brace_r * [-2, 0], + brace_r * [+2, +2]); + } +} + +module PowerBankHolder(){ ////toplevel + difference(){ + union(){ + rotate([0,90,0]) + linextr_y_xz(-(bank_y_sz/2 + 0.1), + +(bank_y_sz/2 + 0.1)) + PowerBankSidePlan(); + + EndRetainer(retainer_walls[0]); + mirror([0,1,0]) EndRetainer(retainer_walls[1]); + + translate([0,0, bank_x_sz/2]){ + for (y = brace_ctrs_y) { + translate([0,y,0]) { + linextr_x_yz(0, brace_total_len) + BraceTubePlan(); + } + } + translate([brace_total_len, 0,0]) + linextr_y_xz(brace_ctrs_y_nom[0] - brace_r, + brace_ctrs_y_nom[1] + brace_r) + BraceTubePlan(); + } + + for (strap_y = straps_y + straps_y_adj) { + translate([0, strap_y, 0]) { + linextr(-holder_x_sz/2, + +holder_x_sz/2){ + hull(){ + for (dy = [-1,+1] * + (strap_w/2 + strap_around_attach - strap_around_over)) { + translate([0, dy, 0]) + circle(r=strap_around_over); + } + } + } + } + } + } + + for (strap_y = straps_y + straps_y_adj) + translate([0, strap_y, 0]) + rotate([0,0,-90]) + rotate([0,90,0]) + linextr(-strap_w/2, + +strap_w/2) + PowerBankStrapCut(); + + translate([ bank_bot_y, -bank_y_sz/2, -bank_x_sz/2 ]) + linextr_y_xz(-50,50) + rotate([0,0,90]) + translate(bank_output_ctr) + square(center=true, bank_output_sz); + + translate([0, -(bank_y_sz/2 + endwall_th), 0] + 0.01 * [-1,-1]) { + linextr(-200,200){ + difference(){ + square(center=true, top_cnr_r*2); + translate(top_cnr_r * [1,1]) + circle(r= top_cnr_r); + } + } + } + } +} + +module TubeClampLeft() { ////toplevel + // We want this to print with the recess overhand to the right + // where the workpiece cooling fan is + rotate([0,0,180]){ + difference(){ + SomeClamp(true) + TubeClampLeftPlan(); + + Screws(); + } + } +} + +module PlacePowerBank(){ + translate([main_r, -mounted_pos_y_offset, 0]) + children(0); +} + +module Main(){ ////toplevel + TubeClampLeft(); + difference(){ + PlacePowerBank() + PowerBankHolder(); + rotate([0,0,180]) + Screws(); + } +} + +module TubeClampRight() { ////toplevel + rotate([0,0,180]) { + rotate([180,0,0]) { + difference(){ + union(){ + SomeClamp() + TubeClampRightPlan(); + + translate([clamp_gap/2 + behind_knob_th, screw_y, 0]) { + hull(){ + linextr_x_yz(-0.1, 0) + square(center=true, + [knob_behind_across, + knob_behind_across + knob_standout_h*2]); + linextr_x_yz(0, knob_standout_h) + square(center=true, + knob_behind_across); + } + } + } + Screws(); + } + } + } +} + +module TubeClampDemo() { ////toplevel + TubeClampLeft(); + rotate([180,0,0]) + TubeClampRight(); +} + +module Demo() { ////toplevel + Main(); + rotate([180,0,0]) + TubeClampRight(); + PlacePowerBank() + %PowerBankItself(); +} diff --git a/pull-cord-keeper.scad b/pull-cord-keeper.scad new file mode 100644 index 0000000..c8655c9 --- /dev/null +++ b/pull-cord-keeper.scad @@ -0,0 +1,189 @@ +// -*- C -*- + +hoopthick = 3; + +hinnerrad = 15; +houterrad = hinnerrad + hoopthick; +hcentredist = 10; + +blockdepth = 5; +blockwidth = hcentredist*2 + 6; + +height = 20; + +roundedgedia = 7.5; + +ziglen = hcentredist/2; + +feedxgap = 5; +feedzgap = 5; +ribsgap = 1; + +ribdepth = 3; +ribheight = 4; + +backxgap = 1; + +blockoverlapcnr = 5; + +screwholedia = 4 + 0.5; + +module Oval(centredist, rad) { + hull() { + translate([-centredist/2,0,0]) circle(r=rad); + translate([+centredist/2,0,0]) circle(r=rad); + } +} + +module VExtrude(){ + translate([0,0, -height/2]) + linear_extrude(height=20) + children(0); +} + +module OuterOval(){ + Oval(hcentredist, houterrad); +} + +module Hoop(){ + difference(){ + hull(){ + OuterOval(); + translate([0, (blockdepth + hoopthick)/2 + hinnerrad]) + square([blockwidth, + blockdepth + hoopthick], + center=true); + } + Oval(hcentredist, hinnerrad); + } +} + +module RoundEdges(){ + intersection(){ + VExtrude() + OuterOval(); + + for (xi=[-1,1]) { + hull(){ + for (yi=[-1,1]) { + translate([xi * (hcentredist/2 + hinnerrad), + houterrad, + yi * (height/2 - roundedgedia / 4 * sqrt(2))]) + rotate([90,0,0]) + cylinder(r=roundedgedia/2, h=houterrad*2, $fn=20); + } + } + } + } +} + +module Positive(){ + difference(){ + VExtrude() + Hoop(); + + rotate([90,0,0]) + translate([0,0,-50]) + cylinder(r=screwholedia/2, h=100); + } + + RoundEdges(); +} + +module Ribs(){ + imax = ceil(height*2 / ribheight); + for (i=[-imax:imax]) { + hull(){ + translate([-ribdepth/2, + ribheight*i, + 0]) + polygon([[0, 0], + [ribdepth, -ribheight], + [ribdepth, +ribheight]]); + translate([50, 0]) + square([1, height*2], center=true); + } + } +} + +module Division(cutmore) { + mirror([0,0,1]) { + translate([0, 0, -cutmore*feedzgap/2]) { + translate([-ziglen + -cutmore*feedxgap/2, -100, 0]) + cube([100, 100, 50]); + } + } + translate([blockwidth/2 - blockoverlapcnr + -cutmore*backxgap/2, + -1, + -50]) + cube([100, 100, 100]); + + translate([ziglen + -cutmore*feedxgap/2, + -50, + -50]) + cube([100, 51, 100]); + + translate([50, + hinnerrad/2 + houterrad/2 + blockdepth/2 + -cutmore*ribsgap/2, + 0]) + rotate([-90,0,90]) + linear_extrude(height=100) + Ribs(); +} + +module SDemo(){ + //difference(){ + % Positive(); + // Division(0); + //} + Division(-1); +} + +module A(){ + difference(){ + Positive(); + Division(+1); + } +} + +module B(){ + intersection(){ + Positive(); + Division(-1); + } +} + +module Demo(){ + color("red") A(); + color("blue") B(); +} + +module APrint(){ ////toplevel + rotate([0,180,0]) + A(); +} + +module BPrint(){ ////toplevel + B(); +} + +module Kit(){ ////toplevel + translate([0, hinnerrad, 0]) + APrint(); + rotate([0,0,180]) + BPrint(); +} + +//Ribs(); +//Demo(); + +//A(); +//B(); +//%Division(+1); + +//Hoop(); + +//Demo(); +//BPrint(); + +//Kit(); diff --git a/quacks-ingredients-L1.scad b/quacks-ingredients-L1.scad new file mode 100644 index 0000000..5a85e58 --- /dev/null +++ b/quacks-ingredients-L1.scad @@ -0,0 +1,5 @@ +// autogenerated by quacks-ingredients-updates-levels - do not edit +$phase=1; +module Token_L(){ Token_L1(); } +//// toplevels-from: +include diff --git a/quacks-ingredients-L2.scad b/quacks-ingredients-L2.scad new file mode 100644 index 0000000..6c7a087 --- /dev/null +++ b/quacks-ingredients-L2.scad @@ -0,0 +1,5 @@ +// autogenerated by quacks-ingredients-updates-levels - do not edit +$phase=2; +module Token_L(){ Token_L2(); } +//// toplevels-from: +include diff --git a/quacks-ingredients-L3.scad b/quacks-ingredients-L3.scad new file mode 100644 index 0000000..b39277d --- /dev/null +++ b/quacks-ingredients-L3.scad @@ -0,0 +1,5 @@ +// autogenerated by quacks-ingredients-updates-levels - do not edit +$phase=3; +module Token_L(){ Token_L3(); } +//// toplevels-from: +include diff --git a/quacks-ingredients-L4.scad b/quacks-ingredients-L4.scad new file mode 100644 index 0000000..9f86e2a --- /dev/null +++ b/quacks-ingredients-L4.scad @@ -0,0 +1,5 @@ +// autogenerated by quacks-ingredients-updates-levels - do not edit +$phase=4; +module Token_L(){ Token_L4(); } +//// toplevels-from: +include diff --git a/quacks-ingredients-L5.scad b/quacks-ingredients-L5.scad new file mode 100644 index 0000000..2f35fd3 --- /dev/null +++ b/quacks-ingredients-L5.scad @@ -0,0 +1,5 @@ +// autogenerated by quacks-ingredients-updates-levels - do not edit +$phase=5; +module Token_L(){ Token_L5(); } +//// toplevels-from: +include diff --git a/quacks-ingredients-counts b/quacks-ingredients-counts new file mode 100755 index 0000000..5377cbc --- /dev/null +++ b/quacks-ingredients-counts @@ -0,0 +1,111 @@ +#!/usr/bin/perl -w + +use strict; + +use Data::Dumper; +use POSIX; + +our $which = shift @ARGV; + +sub xdata ($) { + my ($cb) = @_; + return unless $which eq 'Base'; + foreach my $count (qw(1 2 3)) { + foreach my $nspots (qw(0 1 2 3 4)) { + $_ = $cb->($count,$nspots)."\t".$_; + } + } +} + +$_=; 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)) { + $_=; 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 <error and die $!; + +__DATA__ +White Green Blue Red Yellow Purple Black Orange Orange6 Loco WhiteSpare +21+6 15+10 14+8 12+6 13+6 15+8 18+8 20+12 1+0 +9+3 10+5 10+5 8+5 6+5 1+0 +5+2 1+0 + 13+5 10+5 10+5 10+5 + 0+20 0+25 diff --git a/quacks-ingredients-counts.scad b/quacks-ingredients-counts.scad new file mode 100644 index 0000000..c2a8d08 --- /dev/null +++ b/quacks-ingredients-counts.scad @@ -0,0 +1,1826 @@ +// autogenerated - do not edit +// update script is quacks-ingredients-updates-levels +// source is quacks-ingredients-counts +module Base_1xFour(){ ////toplevel +// Base 1xFour total= 1 rowsz=1 nrows=1 + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ 0.0, 0.0 ]) Token_L(); + }; +} +module Base_1xOne(){ ////toplevel +// Base 1xOne total= 1 rowsz=1 nrows=1 + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ 0.0, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 4; + }; +} +module Base_1xThree(){ ////toplevel +// Base 1xThree total= 1 rowsz=1 nrows=1 + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 3; + translate(token_pitch * [ 0.0, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 4; + }; +} +module Base_1xTwo(){ ////toplevel +// Base 1xTwo total= 1 rowsz=1 nrows=1 + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ 0.0, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 4; + }; +} +module Base_1xZero(){ ////toplevel +// Base 1xZero total= 1 rowsz=1 nrows=1 + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 0; + translate(token_pitch * [ 0.0, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 1 + 1.00, 1 + 0.50 ]); + $nspots = 4; + }; +} +module Base_2xFour(){ ////toplevel +// Base 2xFour total= 2 rowsz=2 nrows=1 + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ -0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.0 ]) Token_L(); + }; +} +module Base_2xOne(){ ////toplevel +// Base 2xOne total= 2 rowsz=2 nrows=1 + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 4; + }; +} +module Base_2xThree(){ ////toplevel +// Base 2xThree total= 2 rowsz=2 nrows=1 + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 3; + translate(token_pitch * [ -0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 4; + }; +} +module Base_2xTwo(){ ////toplevel +// Base 2xTwo total= 2 rowsz=2 nrows=1 + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ -0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 4; + }; +} +module Base_2xZero(){ ////toplevel +// Base 2xZero total= 2 rowsz=2 nrows=1 + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 0; + translate(token_pitch * [ -0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 1 + 0.50 ]); + $nspots = 4; + }; +} +module Base_3xFour(){ ////toplevel +// Base 3xFour total= 3 rowsz=2 nrows=2 + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ -0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -0.5 ]) Token_L(); + }; +} +module Base_3xOne(){ ////toplevel +// Base 3xOne total= 3 rowsz=2 nrows=2 + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 4; + }; +} +module Base_3xThree(){ ////toplevel +// Base 3xThree total= 3 rowsz=2 nrows=2 + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 3; + translate(token_pitch * [ -0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 4; + }; +} +module Base_3xTwo(){ ////toplevel +// Base 3xTwo total= 3 rowsz=2 nrows=2 + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ -0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 4; + }; +} +module Base_3xZero(){ ////toplevel +// Base 3xZero total= 3 rowsz=2 nrows=2 + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 0; + translate(token_pitch * [ -0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 4; + }; +} +module Base_Black(){ ////toplevel +// Base Black total=18 rowsz=5 nrows=4 + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -2.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -2.0, -0.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 1.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -0.5 ]) Token_L(); + translate(token_pitch * [ -1.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -1.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 0.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 0.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 0.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 1.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 1.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 1.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 1.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 4; + }; +} +module Base_Blue(){ ////toplevel +// Base Blue total=34 rowsz=6 nrows=6 + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -2.5, -2.5 ]) Token_L(); + translate(token_pitch * [ -2.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -2.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -2.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -2.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -2.5, 2.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -2.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 2.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -2.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -1.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ -0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 2.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -2.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 2.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ 1.5, -2.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 2.5 ]) Token_L(); + translate(token_pitch * [ 2.5, -2.5 ]) Token_L(); + translate(token_pitch * [ 2.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 2.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 2.5, 0.5 ]) Token_L(); + }; +} +module Base_Green(){ ////toplevel +// Base Green total=38 rowsz=7 nrows=6 + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 6 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 6 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -3.0, -2.5 ]) Token_L(); + translate(token_pitch * [ -3.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -3.0, -0.5 ]) Token_L(); + translate(token_pitch * [ -3.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -3.0, 1.5 ]) Token_L(); + translate(token_pitch * [ -3.0, 2.5 ]) Token_L(); + translate(token_pitch * [ -2.0, -2.5 ]) Token_L(); + translate(token_pitch * [ -2.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -2.0, -0.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 1.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 2.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -2.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 6 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ -1.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -1.0, 1.5 ]) Token_L(); + translate(token_pitch * [ -1.0, 2.5 ]) Token_L(); + translate(token_pitch * [ 0.0, -2.5 ]) Token_L(); + translate(token_pitch * [ 0.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 0.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 0.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 0.0, 2.5 ]) Token_L(); + translate(token_pitch * [ 1.0, -2.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 6 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 6 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ 1.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 1.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 1.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 1.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 1.0, 2.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -2.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 2.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 2.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 2.0, 2.5 ]) Token_L(); + translate(token_pitch * [ 3.0, -2.5 ]) Token_L(); + translate(token_pitch * [ 3.0, -1.5 ]) Token_L(); + }; +} +module Base_Orange(){ ////toplevel +// Base Orange total=20 rowsz=5 nrows=4 + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -2.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -2.0, -0.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 1.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -0.5 ]) Token_L(); + translate(token_pitch * [ -1.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -1.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 0.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 0.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 0.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 1.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 1.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 1.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 1.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 2.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 2.0, 1.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 4; + }; +} +module Base_Purple(){ ////toplevel +// Base Purple total=15 rowsz=4 nrows=4 + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -1.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 4; + }; +} +module Base_Red(){ ////toplevel +// Base Red total=30 rowsz=6 nrows=5 + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -2.5, -2.0 ]) Token_L(); + translate(token_pitch * [ -2.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -2.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -2.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -2.5, 2.0 ]) Token_L(); + translate(token_pitch * [ -1.5, -2.0 ]) Token_L(); + translate(token_pitch * [ -1.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 2.0 ]) Token_L(); + translate(token_pitch * [ -0.5, -2.0 ]) Token_L(); + translate(token_pitch * [ -0.5, -1.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ -0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 2.0 ]) Token_L(); + translate(token_pitch * [ 0.5, -2.0 ]) Token_L(); + translate(token_pitch * [ 0.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 1.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 2.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ 1.5, -2.0 ]) Token_L(); + translate(token_pitch * [ 1.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 1.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 2.0 ]) Token_L(); + translate(token_pitch * [ 2.5, -2.0 ]) Token_L(); + translate(token_pitch * [ 2.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 2.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 2.5, 1.0 ]) Token_L(); + translate(token_pitch * [ 2.5, 2.0 ]) Token_L(); + }; +} +module Base_White(){ ////toplevel +// Base White total=35 rowsz=6 nrows=6 + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -2.5, -2.5 ]) Token_L(); + translate(token_pitch * [ -2.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -2.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -2.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -2.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -2.5, 2.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -2.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 2.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -2.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 2.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -2.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ 0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 2.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -2.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 2.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 3; + translate(token_pitch * [ 2.5, -2.5 ]) Token_L(); + translate(token_pitch * [ 2.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 2.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 2.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 2.5, 1.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 4; + }; +} +module Base_WhiteSpare(){ ////toplevel +// Base WhiteSpare total= 3 rowsz=2 nrows=2 + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -0.5, -0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ -0.5, 0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 3; + translate(token_pitch * [ 0.5, -0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 4; + }; +} +module Base_Yellow(){ ////toplevel +// Base Yellow total=29 rowsz=6 nrows=5 + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -2.5, -2.0 ]) Token_L(); + translate(token_pitch * [ -2.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -2.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -2.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -2.5, 2.0 ]) Token_L(); + translate(token_pitch * [ -1.5, -2.0 ]) Token_L(); + translate(token_pitch * [ -1.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 2.0 ]) Token_L(); + translate(token_pitch * [ -0.5, -2.0 ]) Token_L(); + translate(token_pitch * [ -0.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ -0.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 2.0 ]) Token_L(); + translate(token_pitch * [ 0.5, -2.0 ]) Token_L(); + translate(token_pitch * [ 0.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 1.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ 0.5, 2.0 ]) Token_L(); + translate(token_pitch * [ 1.5, -2.0 ]) Token_L(); + translate(token_pitch * [ 1.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 1.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 2.0 ]) Token_L(); + translate(token_pitch * [ 2.5, -2.0 ]) Token_L(); + translate(token_pitch * [ 2.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 2.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 2.5, 1.0 ]) Token_L(); + }; +} +// Base total_count=252 total_real_count=219 +// Base max_rowsz=7 max_nrows=6 +module All_Black(){ ////toplevel +// All Black total=26 rowsz=6 nrows=5 + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -2.5, -2.0 ]) Token_L(); + translate(token_pitch * [ -2.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -2.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -2.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -2.5, 2.0 ]) Token_L(); + translate(token_pitch * [ -1.5, -2.0 ]) Token_L(); + translate(token_pitch * [ -1.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 2.0 ]) Token_L(); + translate(token_pitch * [ -0.5, -2.0 ]) Token_L(); + translate(token_pitch * [ -0.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 2.0 ]) Token_L(); + translate(token_pitch * [ 0.5, -2.0 ]) Token_L(); + translate(token_pitch * [ 0.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 1.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 2.0 ]) Token_L(); + translate(token_pitch * [ 1.5, -2.0 ]) Token_L(); + translate(token_pitch * [ 1.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 1.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 2.0 ]) Token_L(); + translate(token_pitch * [ 2.5, -2.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 5 + 0.50 ]); + $nspots = 4; + }; +} +module All_Blue(){ ////toplevel +// All Blue total=52 rowsz=8 nrows=7 + union(){ + Frame($phase, token_pitch * [ 8 + 1.00, 7 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 8 + 1.00, 7 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -3.5, -3.0 ]) Token_L(); + translate(token_pitch * [ -3.5, -2.0 ]) Token_L(); + translate(token_pitch * [ -3.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -3.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -3.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -3.5, 2.0 ]) Token_L(); + translate(token_pitch * [ -3.5, 3.0 ]) Token_L(); + translate(token_pitch * [ -2.5, -3.0 ]) Token_L(); + translate(token_pitch * [ -2.5, -2.0 ]) Token_L(); + translate(token_pitch * [ -2.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -2.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -2.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -2.5, 2.0 ]) Token_L(); + translate(token_pitch * [ -2.5, 3.0 ]) Token_L(); + translate(token_pitch * [ -1.5, -3.0 ]) Token_L(); + translate(token_pitch * [ -1.5, -2.0 ]) Token_L(); + translate(token_pitch * [ -1.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 2.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 3.0 ]) Token_L(); + translate(token_pitch * [ -0.5, -3.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 8 + 1.00, 7 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ -0.5, -2.0 ]) Token_L(); + translate(token_pitch * [ -0.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 2.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 3.0 ]) Token_L(); + translate(token_pitch * [ 0.5, -3.0 ]) Token_L(); + translate(token_pitch * [ 0.5, -2.0 ]) Token_L(); + translate(token_pitch * [ 0.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 1.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 2.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 3.0 ]) Token_L(); + translate(token_pitch * [ 1.5, -3.0 ]) Token_L(); + translate(token_pitch * [ 1.5, -2.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 8 + 1.00, 7 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 8 + 1.00, 7 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ 1.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 1.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 2.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 3.0 ]) Token_L(); + translate(token_pitch * [ 2.5, -3.0 ]) Token_L(); + translate(token_pitch * [ 2.5, -2.0 ]) Token_L(); + translate(token_pitch * [ 2.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 2.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 2.5, 1.0 ]) Token_L(); + translate(token_pitch * [ 2.5, 2.0 ]) Token_L(); + translate(token_pitch * [ 2.5, 3.0 ]) Token_L(); + translate(token_pitch * [ 3.5, -3.0 ]) Token_L(); + translate(token_pitch * [ 3.5, -2.0 ]) Token_L(); + translate(token_pitch * [ 3.5, -1.0 ]) Token_L(); + }; +} +module All_Green(){ ////toplevel +// All Green total=58 rowsz=8 nrows=8 + union(){ + Frame($phase, token_pitch * [ 8 + 1.00, 8 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 8 + 1.00, 8 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -3.5, -3.5 ]) Token_L(); + translate(token_pitch * [ -3.5, -2.5 ]) Token_L(); + translate(token_pitch * [ -3.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -3.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -3.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -3.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -3.5, 2.5 ]) Token_L(); + translate(token_pitch * [ -3.5, 3.5 ]) Token_L(); + translate(token_pitch * [ -2.5, -3.5 ]) Token_L(); + translate(token_pitch * [ -2.5, -2.5 ]) Token_L(); + translate(token_pitch * [ -2.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -2.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -2.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -2.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -2.5, 2.5 ]) Token_L(); + translate(token_pitch * [ -2.5, 3.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -3.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -2.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 2.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 3.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -3.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 8 + 1.00, 8 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ -0.5, -2.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 2.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 3.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -3.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -2.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 2.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 3.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 8 + 1.00, 8 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 8 + 1.00, 8 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ 1.5, -3.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -2.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 2.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 3.5 ]) Token_L(); + translate(token_pitch * [ 2.5, -3.5 ]) Token_L(); + translate(token_pitch * [ 2.5, -2.5 ]) Token_L(); + translate(token_pitch * [ 2.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 2.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 2.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 2.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 2.5, 2.5 ]) Token_L(); + translate(token_pitch * [ 2.5, 3.5 ]) Token_L(); + translate(token_pitch * [ 3.5, -3.5 ]) Token_L(); + translate(token_pitch * [ 3.5, -2.5 ]) Token_L(); + }; +} +module All_Loco(){ ////toplevel +// All Loco total=25 rowsz=5 nrows=5 + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 0; + translate(token_pitch * [ -2.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -2.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 1.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 2.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 1.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 2.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 4; + }; +} +module All_Orange(){ ////toplevel +// All Orange total=32 rowsz=6 nrows=6 + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -2.5, -2.5 ]) Token_L(); + translate(token_pitch * [ -2.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -2.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -2.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -2.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -2.5, 2.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -2.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 2.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -2.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 2.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -2.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 2.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -2.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 2.5 ]) Token_L(); + translate(token_pitch * [ 2.5, -2.5 ]) Token_L(); + translate(token_pitch * [ 2.5, -1.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 6 + 1.00, 6 + 0.50 ]); + $nspots = 4; + }; +} +module All_Orange6(){ ////toplevel +// All Orange6 total=20 rowsz=5 nrows=4 + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 0; + translate(token_pitch * [ -2.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -2.0, -0.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 1.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -0.5 ]) Token_L(); + translate(token_pitch * [ -1.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -1.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 0.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 0.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 0.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 1.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 1.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 1.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 1.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 2.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 2.0, 1.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 4; + }; +} +module All_Purple(){ ////toplevel +// All Purple total=23 rowsz=5 nrows=5 + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -2.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -2.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 1.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 2.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 1.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 4; + }; +} +module All_Red(){ ////toplevel +// All Red total=46 rowsz=7 nrows=7 + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -3.0, -3.0 ]) Token_L(); + translate(token_pitch * [ -3.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -3.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -3.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -3.0, 1.0 ]) Token_L(); + translate(token_pitch * [ -3.0, 2.0 ]) Token_L(); + translate(token_pitch * [ -3.0, 3.0 ]) Token_L(); + translate(token_pitch * [ -2.0, -3.0 ]) Token_L(); + translate(token_pitch * [ -2.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -2.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 1.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 2.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 3.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -3.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ -1.0, 1.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 2.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 3.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -3.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 3.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -3.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -1.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ 1.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 3.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -3.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 3.0 ]) Token_L(); + translate(token_pitch * [ 3.0, -3.0 ]) Token_L(); + translate(token_pitch * [ 3.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 3.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 3.0, 0.0 ]) Token_L(); + }; +} +module All_White(){ ////toplevel +// All White total=46 rowsz=7 nrows=7 + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -3.0, -3.0 ]) Token_L(); + translate(token_pitch * [ -3.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -3.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -3.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -3.0, 1.0 ]) Token_L(); + translate(token_pitch * [ -3.0, 2.0 ]) Token_L(); + translate(token_pitch * [ -3.0, 3.0 ]) Token_L(); + translate(token_pitch * [ -2.0, -3.0 ]) Token_L(); + translate(token_pitch * [ -2.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -2.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 1.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 2.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 3.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -3.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 1.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 2.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 3.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -3.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 2.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ 0.0, 3.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -3.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 3.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -3.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 3; + translate(token_pitch * [ 2.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 3.0 ]) Token_L(); + translate(token_pitch * [ 3.0, -3.0 ]) Token_L(); + translate(token_pitch * [ 3.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 3.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 3.0, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 4; + }; +} +module All_WhiteSpare(){ ////toplevel +// All WhiteSpare total= 3 rowsz=2 nrows=2 + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -0.5, -0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ -0.5, 0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 3; + translate(token_pitch * [ 0.5, -0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 2 + 1.00, 2 + 0.50 ]); + $nspots = 4; + }; +} +module All_Yellow(){ ////toplevel +// All Yellow total=45 rowsz=7 nrows=7 + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -3.0, -3.0 ]) Token_L(); + translate(token_pitch * [ -3.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -3.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -3.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -3.0, 1.0 ]) Token_L(); + translate(token_pitch * [ -3.0, 2.0 ]) Token_L(); + translate(token_pitch * [ -3.0, 3.0 ]) Token_L(); + translate(token_pitch * [ -2.0, -3.0 ]) Token_L(); + translate(token_pitch * [ -2.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -2.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 1.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 2.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 3.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -3.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 1.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ -1.0, 2.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 3.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -3.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 3.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -3.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -2.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 7 + 1.00, 7 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ 1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 3.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -3.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 3.0 ]) Token_L(); + translate(token_pitch * [ 3.0, -3.0 ]) Token_L(); + translate(token_pitch * [ 3.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 3.0, -1.0 ]) Token_L(); + }; +} +// All total_count=376 total_real_count=353 +// All max_rowsz=8 max_nrows=8 +module Witches_Black(){ ////toplevel +// Witches Black total= 8 rowsz=3 nrows=3 + union(){ + Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]); + $nspots = 4; + }; +} +module Witches_Blue(){ ////toplevel +// Witches Blue total=18 rowsz=5 nrows=4 + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -2.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -2.0, -0.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 1.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -0.5 ]) Token_L(); + translate(token_pitch * [ -1.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -1.0, 1.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ 0.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 0.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 0.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 1.0, -1.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ 1.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 1.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 1.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -0.5 ]) Token_L(); + }; +} +module Witches_Green(){ ////toplevel +// Witches Green total=20 rowsz=5 nrows=4 + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -2.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -2.0, -0.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 1.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -0.5 ]) Token_L(); + translate(token_pitch * [ -1.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -1.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 0.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 0.0, -0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ 0.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 1.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 1.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 1.0, 0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ 1.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 2.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 2.0, 1.5 ]) Token_L(); + }; +} +module Witches_Loco(){ ////toplevel +// Witches Loco total=25 rowsz=5 nrows=5 + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 0; + translate(token_pitch * [ -2.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -2.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 1.0 ]) Token_L(); + translate(token_pitch * [ -2.0, 2.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -2.0 ]) Token_L(); + translate(token_pitch * [ -1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 1.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 2.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -2.0 ]) Token_L(); + translate(token_pitch * [ 2.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 2.0, 2.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 5 + 0.50 ]); + $nspots = 4; + }; +} +module Witches_Orange(){ ////toplevel +// Witches Orange total=12 rowsz=4 nrows=3 + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -1.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -0.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 1.0 ]) Token_L(); + translate(token_pitch * [ 0.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 1.0 ]) Token_L(); + translate(token_pitch * [ 1.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 1.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]); + $nspots = 4; + }; +} +module Witches_Orange6(){ ////toplevel +// Witches Orange6 total=20 rowsz=5 nrows=4 + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 0; + translate(token_pitch * [ -2.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -2.0, -0.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -2.0, 1.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -1.5 ]) Token_L(); + translate(token_pitch * [ -1.0, -0.5 ]) Token_L(); + translate(token_pitch * [ -1.0, 0.5 ]) Token_L(); + translate(token_pitch * [ -1.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 0.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 0.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 0.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 1.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 1.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 1.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 1.0, 1.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -1.5 ]) Token_L(); + translate(token_pitch * [ 2.0, -0.5 ]) Token_L(); + translate(token_pitch * [ 2.0, 0.5 ]) Token_L(); + translate(token_pitch * [ 2.0, 1.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 1; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 5 + 1.00, 4 + 0.50 ]); + $nspots = 4; + }; +} +module Witches_Purple(){ ////toplevel +// Witches Purple total= 8 rowsz=3 nrows=3 + union(){ + Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 0.0 ]) Token_L(); + translate(token_pitch * [ -1.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.0, 1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, -1.0 ]) Token_L(); + translate(token_pitch * [ 1.0, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]); + $nspots = 2; + }; + union(){ + Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 3 + 1.00, 3 + 0.50 ]); + $nspots = 4; + }; +} +module Witches_Red(){ ////toplevel +// Witches Red total=16 rowsz=4 nrows=4 + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -1.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ -0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ 0.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 1.5 ]) Token_L(); + }; +} +module Witches_White(){ ////toplevel +// Witches White total=11 rowsz=4 nrows=3 + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -1.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -1.5, 1.0 ]) Token_L(); + translate(token_pitch * [ -0.5, -1.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ -0.5, 1.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ 0.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.0 ]) Token_L(); + translate(token_pitch * [ 0.5, 1.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]); + $nspots = 3; + translate(token_pitch * [ 1.5, -1.0 ]) Token_L(); + translate(token_pitch * [ 1.5, 0.0 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 3 + 0.50 ]); + $nspots = 4; + }; +} +module Witches_Yellow(){ ////toplevel +// Witches Yellow total=16 rowsz=4 nrows=4 + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 0; + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 1; + translate(token_pitch * [ -1.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -1.5, -0.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -1.5, 1.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -1.5 ]) Token_L(); + translate(token_pitch * [ -0.5, -0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 2; + translate(token_pitch * [ -0.5, 0.5 ]) Token_L(); + translate(token_pitch * [ -0.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 0.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 0.5, 0.5 ]) Token_L(); + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 3; + }; + union(){ + Frame($phase, token_pitch * [ 4 + 1.00, 4 + 0.50 ]); + $nspots = 4; + translate(token_pitch * [ 0.5, 1.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -1.5 ]) Token_L(); + translate(token_pitch * [ 1.5, -0.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 0.5 ]) Token_L(); + translate(token_pitch * [ 1.5, 1.5 ]) Token_L(); + }; +} +// Witches total_count=154 total_real_count=134 +// Witches max_rowsz=5 max_nrows=5 diff --git a/quacks-ingredients-demos.scad b/quacks-ingredients-demos.scad new file mode 100644 index 0000000..1f02c60 --- /dev/null +++ b/quacks-ingredients-demos.scad @@ -0,0 +1,39 @@ +// -*- C -*- + +include + +sandingframe_gap = 0.3; // to be added to radius +sandingframe_nw = 5; +sandingframe_nl = 6; +sandingframe_th = thick*2/3; + +module Demo(){ ////toplevel + $nspots = 3; + color("red") { Token_L3(); } + color("white") { Token_L1(); Token_L5(); } + color("black") { Token_L2(); Token_L4(); } +} + +module SandingFrame(){ ////toplevel + stridel = token_dia + 5; + nl = sandingframe_nl; + nw = sandingframe_nw; + stridew = stridel * cos(30); + stride = [stridel,stridew]; + + linear_extrude(height = sandingframe_th) { + difference(){ + translate((token_dia/2 + stridel) * 0.5 * [-1,-1]) + square([ stridel * (nl + 0.5), + stridew * nw + stridel * 0.5 ]); + for (j = [0 : nw-1]) { + eo = j % 2; + for (i = [0 : nl-1-0.5*eo]) { + translate([stridel * (i + 0.5 * eo), + stridew * j]) + circle(r = token_dia/2 + sandingframe_gap); + } + } + } + } +} diff --git a/quacks-ingredients-make-copy-gcodes b/quacks-ingredients-make-copy-gcodes new file mode 100755 index 0000000..fd34367 --- /dev/null +++ b/quacks-ingredients-make-copy-gcodes @@ -0,0 +1,135 @@ +#!/bin/bash +# +# usage: +# ./quacks-ingredients-make-copy-gcodes Tests_L +# Uses +# quacks-ingredients-L$l,Tests_L.auto.stl +# to make +# quacks-ingredients-L$l,Tests_L.auto.gcode +# and then edits them and copies them to the SD card as +# PREPARED/Q$l.G + +set -e + +f=$1 +shift + +lhs=quacks-ingredients-L + +gh () { + g=$lhs$l.auto.gcode + h=/media/sd/PREPARED/Q$l.G +} + +for l in 1 2 3 4 5; do + gh + qi=quacks-L$l.auto.ini + + cp quacks.ini $qi + + case $l in + 2|4|5) + perl -i~ -pe ' + s/^(retraction_hop *= *.*)/retraction_hop = 0.6/m + ' $qi + ;; + esac + + cura -i $qi -s $lhs$l,$f.auto.stl -o $g + case $l in + 1|2|3|4) + perl -i~ -pe 's/^/;/ if m/^M140 S0\b.*\n/' $g + ;; + esac + + perl -i~ -ne ' + $l =~ s/^/;/ if m/^M400/; + $l .= "G91\nG1 Z5\nG90\n" if m/^M84/; + print $l or die $!; + $l = $_; + END { print $l or die $!; } + ' $g +done + +for l in 2 4 5; do + gh + : perl -i~ -0777 -ne ' + @l = split m{^(?=;LAYER:\d+\n)}m, $_; + foreach my $i (0..$#l) { + $l[$i] =~ + s{ + ( ^G1 \ Z\S+ \s*\n + | ^\;LAYER:\d+ \s*\n + (?: ^M10[67].* \s*\n )? + ) + ^G0 \ F(\d+) \ ( X\S+ \ Y\S+ ) + \ Z(\S+) \s*\n + }{ + die "$& $2" unless $2 > 9000; + $1 . + "G1 Z$4\n". + "G0 F$2 $3\n". + "G1 Z$4\n" + }mxe or $i==0 + or die "$l[$i] $i"; + $l[$i] =~ + s{ + ^G1 \ Z([0-9.]+) \s*\n + ( ^G0 \ F(\d+) \ X\S+ \ Y\S+ \s*\n + (?: ; .* \s*\n )? + ^G1 \ Z([0-9.]+) \s*\n ) + }{ + die "$& $3" unless $1 >= $4; + die "$& $3" unless $3 > 9000; + my $z = $i == $#l ? $1 : $4 + 0.5; + "G0 F$3\n". + "G1 Z$z\n" . + $2 + }gmxe or $i==0 or die "$l[$i] $i"; + } + print or die $! foreach @l; + ' $g +done + +exec 3>${lhs}234.auto.gcode +for l in 2 3; do + gh + perl -pe 's/^/;/ if m/^M104 S0\b/ || (m/^M84/..0)' $g >&3 +done +for l in 4; do + gh + cat $g >&3 +done + +copies="3:5 2:234 1:1" +copyls="" +for copy in $copies; do + l=-P-${copy%:*} + ci=$lhs${copy#*:}.auto.gcode + copyls+=" $l" + gh + rm -f $g + ln -vs $ci $g +done + +umount /media/sd >/dev/null 2>&1 ||: +mount /media/sd + +for l in 1 2 3 4 5 234 $copyls; do + gh + cp $g $h +done + +sleep 0.5 + +umount /media/sd +mount /media/sd + +for l in 1 2 3 4 5 234 $copyls; do + gh + cmp $g $h + ls -l $h +done +sleep 0.5 + +umount /media/sd diff --git a/quacks-ingredients-update-levels b/quacks-ingredients-update-levels new file mode 100755 index 0000000..395770d --- /dev/null +++ b/quacks-ingredients-update-levels @@ -0,0 +1,28 @@ +#!/bin/sh +set -e + +for l in 1 2 3 4 5; do + f=quacks-ingredients-L$l.scad + cat >$f.tmp < +END + mv -f $f.tmp $f +done + +f=quacks-ingredients-counts.scad +cat >$f.tmp <>$f.tmp +done +mv -f $f.tmp $f + +egrep '^// [A-Z][a-z]* *[A-Za-z]' $f | sort diff --git a/quacks-ingredients.scad b/quacks-ingredients.scad new file mode 100644 index 0000000..6925a1b --- /dev/null +++ b/quacks-ingredients.scad @@ -0,0 +1,145 @@ +// -*- C -*- + +// +// git clean -xdff +// ./quacks-ingredients-update-levels +// make autoincs +// +// make -j8 quacks-stls 2>&1 | tee log +// OR EG +// make -j8 quacks-ingredients-L{1,2,3,4,5},Base_Yellow.auto.stl +// +// ./quacks-ingredients-make-copy-gcodes Base_Yellow etc. +// +// Print Q-P-1 in spots, Q-P-2 in main colour, Q-P-3 in spots +// +// For colours which only have zero-spot counters, print only Q-P-2 + +token_dia = 20; +spot_dia = 4.3; +spot_gap = spot_dia / 3.0; + +thick = 3.0; + +multicolour_gap = 0.075; // each side +initial_layer_thick = 0.400; +initial_layer_width = 0.750; +final_layer_thick = 0.500; +multicolour_post = 4; + +$fs=0.1; +$fa=1; + +// calculated + +token_pitch = token_dia + 3; + +// autoadjusted + +$spots_absent = false; +$spots_plusgap = false; + +module Spots_Extrude_Lower(){ + d = $spots_plusgap ? 1 : 0; + translate([0,0,-d]) + linear_extrude(height= initial_layer_thick + d) + children(0); +} + +module Spots_Extrude_Upper(){ + d = $spots_plusgap ? 1 : 0; + translate([0,0, thick + d]) + mirror([0,0, 1]) + linear_extrude(height= final_layer_thick + d) + children(0); +} + +module SpotAt(condition, xy) { + if (condition == !$spots_absent) { + translate(xy * (spot_gap + spot_dia) * sqrt(0.5)) + circle(r= spot_dia/2 + + ($spots_plusgap ? multicolour_gap : 0)); + } +} + +module Token_Spots(){ + SpotAt(($nspots % 2) > 0, [0,0]); + SpotAt($nspots >= 2, [ 1, 1]); + SpotAt($nspots >= 2, [-1,-1]); + SpotAt($nspots >= 4, [ 1,-1]); + SpotAt($nspots >= 4, [-1, 1]); +} + +module Token_Spots_All(){ + $nspots = 5; + Token_Spots(); +} + +module Token_L1(){ + Spots_Extrude_Lower() + Token_Spots(); +} + +module Token_L2(){ + $spots_absent = true; + Spots_Extrude_Lower() + Token_Spots(); +} + +module Token_L3(){ + $spots_plusgap = true; + difference(){ + linear_extrude(height=thick) + circle(r=token_dia/2); + Spots_Extrude_Lower() Token_Spots_All(); + Spots_Extrude_Upper() Token_Spots_All(); + } +} + +module Token_L4(){ + $spots_absent = true; + Spots_Extrude_Upper() + Token_Spots(); +} + +module Token_L5(){ + Spots_Extrude_Upper() + Token_Spots(); +} + +module Frame(phase, base_sz) { + zs = [ initial_layer_thick, + initial_layer_thick, + thick, + thick, + thick ]; + + sz = base_sz + phase * initial_layer_width * 2 * [1,1]; + linear_extrude(height= initial_layer_thick) { + difference(){ + square(center=true, sz + initial_layer_width * 2 * [1,1]); + square(center=true, sz); + } + } + // Priming tower + translate([-base_sz[0]/2, (2.8-phase)*(multicolour_post*1.7)]) + linear_extrude(height= zs[phase-1]) + square(multicolour_post); +} + +module Tests(){ + for ($nspots = [1,2,3,4]) { + translate(($nspots - 2) * token_pitch * [1,0]) + children(); + } +} + +module Tests_L() { ////toplevel + Frame($phase, token_dia * [ 6, 1.5 ]); + Tests() Token_L(); +} + +//// toplevels-from: +include + +//Demo(); diff --git a/quacks.ini b/quacks.ini new file mode 100644 index 0000000..8a4f7cc --- /dev/null +++ b/quacks.ini @@ -0,0 +1,266 @@ +[profile] +layer_height = 0.14 +wall_thickness = 1 +retraction_enable = True +solid_layer_thickness = 1 +fill_density = 20 +perimeter_before_infill = True +nozzle_size = 0.5 +print_speed = 50 +print_temperature = 0 +print_temperature2 = 0 +print_temperature3 = 0 +print_temperature4 = 0 +print_temperature5 = 0 +print_bed_temperature = 0 +support = None +platform_adhesion = None +support_dual_extrusion = Both +wipe_tower = False +wipe_tower_volume = 15 +ooze_shield = False +filament_diameter = 2.85 +filament_diameter2 = 0 +filament_diameter3 = 0 +filament_diameter4 = 0 +filament_diameter5 = 0 +filament_flow = 100.0 +retraction_speed = 10 +retraction_amount = 1.5 +retraction_dual_amount = 16.5 +retraction_min_travel = 1.5 +retraction_combing = All +retraction_minimal_extrusion = 0.005 +retraction_hop = 0.1 +bottom_thickness = 0.425 +layer0_width_factor = 125 +object_sink = 0.0 +overlap_dual = 0.15 +travel_speed = 175 +bottom_layer_speed = 15 +infill_speed = 40 +solidarea_speed = 30 +inset0_speed = 30 +insetx_speed = 35 +fill_angle = 45 +cool_min_layer_time = 20 +fan_enabled = True +skirt_line_count = 1 +skirt_gap = 3.0 +skirt_minimal_length = 250 +fan_full_height = 0.5 +fan_speed = 75 +fan_speed_max = 100 +cool_min_feedrate = 10 +cool_head_lift = False +solid_top = True +solid_bottom = True +fill_overlap = 15 +support_type = Lines +support_angle = 60 +support_fill_rate = 30 +support_xy_distance = 1.5 +support_z_distance = 0.1 +spiralize = False +simple_mode = False +brim_line_count = 10 +raft_margin = 5.0 +raft_line_spacing = 3.0 +raft_base_thickness = 0.3 +raft_base_linewidth = 1.0 +raft_interface_thickness = 0.27 +raft_interface_linewidth = 0.4 +raft_airgap_all = 0.0 +raft_airgap = 0.5 +raft_surface_layers = 2 +raft_surface_thickness = 0.27 +raft_surface_linewidth = 0.4 +fix_horrible_union_all_type_a = True +fix_horrible_union_all_type_b = False +fix_horrible_use_open_bits = False +fix_horrible_extensive_stitching = False +plugin_config = +object_center_x = -1 +object_center_y = -1 +simplemodesettings = +simplemodeprofile = Standard +simplemodematerial = PLA +simplemodematerialtype = Beginner + +[alterations] +start.gcode = ;Sliced at: {day} {date} {time} + ;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {fill_density} + ;Print time: {print_time} + ;Filament used: {filament_amount}m {filament_weight}g + ;Filament cost: {filament_cost} + ;M190 R{print_bed_temperature} ;Uncomment to add your own bed temperature line + ;M109 R{print_temperature} ;Uncomment to add your own temperature line + G21 ;metric values + G90 ;absolute positioning + M82 ;set extruder to absolute mode + M107 ;start with the fan off + G28 X0 Y0 ;move X/Y to min endstops + G28 Z0 ;move Z to min endstops + G1 Z15.0 F{travel_speed};move the platform down 15mm + G92 E0 ; zero the extruded length + G1 F200 E0 ; extrude 3mm of feed stock + G92 E0 ; zero the extruded length again + G1 F{travel_speed} ; set travel speed + M203 X192 Y208 Z3 ; speed limits + M117 Printing... ; send message to LCD +end.gcode = M400 + M104 S0 ; hotend off + M140 S0 ; heated bed heater off (if you have it) + M107 ; fans off + G91 ; relative positioning + G1 E-1 F300 ; retract the filament a bit before lifting the nozzle, to release some of the pressure + G1 Z+0.5 E-5 X-20 Y-20 F3000 ; move Z up a bit and retract filament even more + G90 ; absolute positioning + G1 X0 Y250 ; move to cooling position + M84 ; steppers off + G90 ; absolute positioning + M117 TAZ Ready. + ;{profile_string} +start2.gcode = ;Sliced at: {day} {date} {time} + ;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {fill_density} + ;Print time: {print_time} + ;Filament used: {filament_amount}m {filament_weight}g + ;Filament cost: {filament_cost} + ;M190 R{print_bed_temperature} ;Uncomment to add your own bed temperature line + ;M104 S{print_temperature} ;Uncomment to add your own temperature line + ;M109 T1 S{print_temperature2} ;Uncomment to add your own temperature line + ;M109 T0 S{print_temperature} ;Uncomment to add your own temperature line + G21 ;metric values + G90 ;absolute positioning + M107 ;start with the fan off + G28 X0 Y0 ;move X/Y to min endstops + G28 Z0 ;move Z to min endstops + G1 Z15.0 F{travel_speed} ;move the platform down 15mm + T1 ;Switch to the 2nd extruder + G92 E0 ;zero the extruded length + G1 F200 E10 ;extrude 10mm of feed stock + G92 E0 ;zero the extruded length again + G1 F200 E-{retraction_dual_amount} + T0 ;Switch to the first extruder + G92 E0 ;zero the extruded length + G1 F200 E10 ;extrude 10mm of feed stock + G92 E0 ;zero the extruded length again + G1 F{travel_speed} + ;Put printing message on LCD screen + M117 Printing... +end2.gcode = ;End GCode + M104 T0 S0 ;extruder heater off + M104 T1 S0 ;extruder heater off + M140 S0 ;heated bed heater off (if you have it) + G91 ;relative positioning + G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure + G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more + G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way + M84 ;steppers off + G90 ;absolute positioning + ;{profile_string} +start3.gcode = ;Sliced at: {day} {date} {time} + ;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {fill_density} + ;Print time: {print_time} + ;Filament used: {filament_amount}m {filament_weight}g + ;Filament cost: {filament_cost} + ;M190 R{print_bed_temperature} ;Uncomment to add your own bed temperature line + ;M104 S{print_temperature} ;Uncomment to add your own temperature line + ;M109 T1 S{print_temperature2} ;Uncomment to add your own temperature line + ;M109 T0 S{print_temperature} ;Uncomment to add your own temperature line + G21 ;metric values + G90 ;absolute positioning + M107 ;start with the fan off + G28 X0 Y0 ;move X/Y to min endstops + G28 Z0 ;move Z to min endstops + G1 Z15.0 F{travel_speed} ;move the platform down 15mm + T2 ;Switch to the 2nd extruder + G92 E0 ;zero the extruded length + G1 F200 E10 ;extrude 10mm of feed stock + G92 E0 ;zero the extruded length again + G1 F200 E-{retraction_dual_amount} + T1 ;Switch to the 2nd extruder + G92 E0 ;zero the extruded length + G1 F200 E10 ;extrude 10mm of feed stock + G92 E0 ;zero the extruded length again + G1 F200 E-{retraction_dual_amount} + T0 ;Switch to the first extruder + G92 E0 ;zero the extruded length + G1 F200 E10 ;extrude 10mm of feed stock + G92 E0 ;zero the extruded length again + G1 F{travel_speed} + ;Put printing message on LCD screen + M117 Printing... +end3.gcode = ;End GCode + M104 T0 S0 ;extruder heater off + M104 T1 S0 ;extruder heater off + M104 T2 S0 ;extruder heater off + M140 S0 ;heated bed heater off (if you have it) + G91 ;relative positioning + G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure + G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more + G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way + M84 ;steppers off + G90 ;absolute positioning + ;{profile_string} +start4.gcode = ;Sliced at: {day} {date} {time} + ;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {fill_density} + ;Print time: {print_time} + ;Filament used: {filament_amount}m {filament_weight}g + ;Filament cost: {filament_cost} + ;M190 R{print_bed_temperature} ;Uncomment to add your own bed temperature line + ;M104 S{print_temperature} ;Uncomment to add your own temperature line + ;M109 T2 S{print_temperature2} ;Uncomment to add your own temperature line + ;M109 T1 S{print_temperature2} ;Uncomment to add your own temperature line + ;M109 T0 S{print_temperature} ;Uncomment to add your own temperature line + G21 ;metric values + G90 ;absolute positioning + M107 ;start with the fan off + G28 X0 Y0 ;move X/Y to min endstops + G28 Z0 ;move Z to min endstops + G1 Z15.0 F{travel_speed} ;move the platform down 15mm + T3 ;Switch to the 4th extruder + G92 E0 ;zero the extruded length + G1 F200 E10 ;extrude 10mm of feed stock + G92 E0 ;zero the extruded length again + G1 F200 E-{retraction_dual_amount} + T2 ;Switch to the 3th extruder + G92 E0 ;zero the extruded length + G1 F200 E10 ;extrude 10mm of feed stock + G92 E0 ;zero the extruded length again + G1 F200 E-{retraction_dual_amount} + T1 ;Switch to the 2nd extruder + G92 E0 ;zero the extruded length + G1 F200 E10 ;extrude 10mm of feed stock + G92 E0 ;zero the extruded length again + G1 F200 E-{retraction_dual_amount} + T0 ;Switch to the first extruder + G92 E0 ;zero the extruded length + G1 F200 E10 ;extrude 10mm of feed stock + G92 E0 ;zero the extruded length again + G1 F{travel_speed} + ;Put printing message on LCD screen + M117 Printing... +end4.gcode = ;End GCode + M104 T0 S0 ;extruder heater off + M104 T1 S0 ;extruder heater off + M104 T2 S0 ;extruder heater off + M104 T3 S0 ;extruder heater off + M140 S0 ;heated bed heater off (if you have it) + G91 ;relative positioning + G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure + G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more + G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way + M84 ;steppers off + G90 ;absolute positioning + ;{profile_string} +support_start.gcode = +support_end.gcode = +cool_start.gcode = +cool_end.gcode = +replace.csv = +preswitchextruder.gcode = ;Switch between the current extruder and the next extruder, when printing with multiple extruders. + ;This code is added before the T(n) +postswitchextruder.gcode = ;Switch between the current extruder and the next extruder, when printing with multiple extruders. + ;This code is added after the T(n) + diff --git a/question-question.fig b/question-question.fig new file mode 100644 index 0000000..6d4e5b9 --- /dev/null +++ b/question-question.fig @@ -0,0 +1,11 @@ +#FIG 3.2 Produced by xfig version 3.2.6a +Landscape +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3780 3870 675 675 3780 3870 4455 3870 +4 0 0 34 -1 14 64 0.0000 4 645 645 3465 4185 ?\001 diff --git a/question-token.scad b/question-token.scad new file mode 100644 index 0000000..2bd7bc6 --- /dev/null +++ b/question-token.scad @@ -0,0 +1,25 @@ +// -*- C -*- + +tokenrad=13; +tokenthick=1.9; + +joinwidth=1.0; + +circlerad=15; + +module Letter() { + translate([-circlerad,-circlerad]) + import("question-question.dxf", convexity=100); +} + +module Token() { ////toplevel + rotate([0,180,0]) + linear_extrude(height=tokenthick) union(){ + difference(){ + %circle(tokenrad); + Letter(); + } + } +} + +Token(); diff --git a/ring-tests.scad b/ring-tests.scad new file mode 100644 index 0000000..c3bd34f --- /dev/null +++ b/ring-tests.scad @@ -0,0 +1,43 @@ +// -*- C -*- + +thick = 3.5; +width = 5; + +centre_dia = 21; + +// calculated + +ellipse_width = width / 0.90; + +module Profile(){ + intersection(){ + scale([thick, ellipse_width]) circle(r=0.5, $fn=50); + square([thick*2, width], center=true); + } +} + +module Ring(dia){ + rotate_extrude($fn=100){ + translate([dia/2 + thick/2, 0]){ + Profile(); + } + } +} + +//Profile(); +//Ring(centre_dia); + +module Tests(){ + min_dia = 19; + max_dia = 22; + dia_step = .25; + n_dias = (max_dia - min_dia)/dia_step; + for (i= [ 0 : n_dias ]) { + echo(i); + rotate([0,0, i*360 / (n_dias+2)]) + translate([80,0,0]) + Ring(min_dia + (max_dia-min_dia)*(i/n_dias)); + } +} + +Tests(); diff --git a/rope-adjuster.scad b/rope-adjuster.scad new file mode 100644 index 0000000..fa4f591 --- /dev/null +++ b/rope-adjuster.scad @@ -0,0 +1,77 @@ +// -*- C -*- + +include + +hole_dia = 10; +around_hole = 4; +thick = 3; +lever_len = 50; + +teeth_n = 4; +teeth_bite = 4; +teeth_pitch = 4; +teeth_gap = 3; +teeth_back = 1; +teeth_height = 12; +teeth_chamfer = 3; + +// calculated + +teeth_x_mid = lever_len/2 - hole_dia/2 - teeth_bite - teeth_gap*1.5; +teeth_height_total = teeth_height + teeth_chamfer; + +module Circles(r) { + for (x = [-1,+1] * 0.5 * lever_len) { + translate([x, 0]) + circle(r); + } +} + +module Plan() { + difference(){ + hull(){ + Circles(hole_dia/2 + around_hole); + } + Circles(hole_dia/2); + translate([ teeth_x_mid - teeth_bite - teeth_back - teeth_gap - hole_dia/2, + 0 ]) + circle(hole_dia/2); + } +} + +module TeethPlan(){ + translate([ + teeth_x_mid, + -0.5 * teeth_n * teeth_pitch, + ]) { + for (m=[0,1]) { + mirror([m,0]) { + translate([ teeth_bite + teeth_gap/2, 0 ]) + rectfromto([ -0.1, 0 ], + [ teeth_back, teeth_n * teeth_pitch ]); + for (i= [ 0: teeth_n-1 ]) { + translate([teeth_gap/2, teeth_pitch*i]) + polygon([[ 0,0 ], + [ teeth_bite, 0 ], + [ teeth_bite, teeth_pitch ]]); + } + } + } + } +} + +module Adjuster(){ + linextr(0,thick) + Plan(); + difference(){ + linextr(thick - 0.1, thick + teeth_height_total) + TeethPlan(); + translate([ teeth_x_mid, 0, thick + teeth_height_total + 1]) + linextr_y_xz(-teeth_pitch * teeth_n, + +teeth_pitch * teeth_n) + rotate(45) + square(sqrt(2) * (teeth_gap/2 + teeth_chamfer + 1), center=true); + } +} + +Adjuster(); diff --git a/rpi-mount.scad b/rpi-mount.scad new file mode 100644 index 0000000..b1b9c12 --- /dev/null +++ b/rpi-mount.scad @@ -0,0 +1,46 @@ +// -*- C -*- + +include + +pi_board_gap = 0.5; +pi_board_support_z = 2; +pi_board_support_wall = 2; + +pi_sz = [ 66.0, 30.5 ] + pi_board_gap * [1,1]; + +pi_nom_sz = [ 65, 30 ]; +pi_nom_centres_in = 3.5; +pi_solder_side_gap = 1.5 * 2; +pi_screw_hole_dia = 2.3; +pi_screw_hole_wall = 2.3; + +pi_sz_z_incl_ribbon = 18.0; + +pi_mount_z_min = 1.75; +pi_mount_z_def = 2.50; + +// calculated, output + +function pi_ribbon_top_z(pi_mount_z= pi_mount_z_def) + = pi_mount_z + pi_sz_z_incl_ribbon; + +module PiMount(pi_mount_z= pi_mount_z_def){ + sxy = pi_nom_sz/2 - [1,1] * pi_nom_centres_in; + for (mx=[0,1]) mirror([mx,0,0]) for (my=[0,1]) mirror([0,my,0]) { + difference(){ + union(){ + linextr(-0.1, pi_mount_z + pi_board_support_z, convexity=1) + rectfromto( pi_nom_sz/2 - 2 * [1,1] * pi_nom_centres_in, + pi_sz/2 + [1,1] * pi_board_support_wall); + linextr(-0.1, pi_mount_z - pi_solder_side_gap, convexity=1) + translate(sxy) + square(center=true, pi_screw_hole_dia + pi_screw_hole_wall*2); + } + linextr(pi_mount_z, pi_mount_z + 5, convexity=1) + rectfromto(-[10,10], pi_sz/2); + translate( sxy ) + linextr(-1, pi_mount_z + 10, convexity=1) + circle(r= pi_screw_hole_dia/2); + } + } +} diff --git a/salter-scale-hook.scad b/salter-scale-hook.scad new file mode 100644 index 0000000..3a8cfa3 --- /dev/null +++ b/salter-scale-hook.scad @@ -0,0 +1,108 @@ +// -*- C -*- + +include + +rod_dia = 8+2; +thick = 8; +screw_dia = 3.5 + 0.75; +screw_head_dia = 8.2 + 1.0; +rod_offset = 14 + 2; +mainheight = 25; +width = 40; +rearthick = 4; +screw_head_depth = 2; + +// calculated + +d = rod_dia/2 + thick/2; +yminc = -d; +ymin = yminc-thick/2; +ymaxc = mainheight; +ymax = mainheight+thick/2; + +cutdepth = rod_offset - rod_dia/2 - rearthick; + +cut_z0 = screw_head_dia/2; +cut_z1 = width/2 - rearthick; + +cutslopez = cutdepth * 0.5; + +module C() { + circle(r = thick/2, $fn=30); +} + +module Profile() { + e = rod_offset; + hull(){ + translate([-d, 0]) C(); + translate([-d,-d]) C(); + } + difference(){ + rectfromto([-d,ymin], [e,0]); + circle(r= rod_dia/2, $fn=50); + } + hull(){ + for (y= [-d, +mainheight]) { + translate([d, y]) C(); + rectfromto([d, y-thick/2], [e, y+thick/2]); + } + } +} + +module CutProfile(){ + hull(){ + for (x = [rod_dia/2 + thick/2, 30]) { + for (y= [yminc,ymaxc] ) { + translate([x,y]) circle(r = (thick-rearthick)/2, $fn=20); + } + } + } +} + +module ProfileDemo(){ + Profile(); + color("red") translate([0,0,1]) CutProfile(); +} + +module Cut(less){ + translate([0,0, cut_z0 + less]) + linear_extrude(height = cut_z1 - cut_z0 - less*2) + CutProfile(); +} + +module ScrewHole(){ + xd = (screw_head_dia-screw_dia)/2; + translate([0,0,-50]) + cylinder(h=100, r= screw_dia/2, $fn=20); + hull(){ + translate([0,0,-xd]) + cylinder(h=1, r= screw_dia/2, $fn=50); + cylinder(h=20, r= screw_head_dia/2, $fn=50); + } +} + +module Hook(){ + difference(){ + translate([0,0, -width/2]) + linear_extrude(height=width) Profile(); + + for (m=[0,1]) { + mirror([0,0,m]) { + hull(){ + Cut(cutslopez); + translate([cutdepth,0,0]) Cut(0); + } + } + } + + translate([rod_dia/2 + screw_head_depth, + ymaxc - screw_head_dia, + 0]) { + rotate([0,-90,0]) + ScrewHole(); + } + } +} + +//ProfileDemo(); +Hook(); diff --git a/scaffold-clamp-cleat.scad b/scaffold-clamp-cleat.scad new file mode 100644 index 0000000..69e8538 --- /dev/null +++ b/scaffold-clamp-cleat.scad @@ -0,0 +1,13 @@ +// -*- C -*- + +// Per cleat print +// VCleatA OR VCleatA +// GeneralB VCleatA +// Pin Pin +// +// These bits are compatible with scaffold-clamp-tensioner + +//// toplevels-from: +include + +module DemoA(){ VCleatARaw(); } diff --git a/scaffold-clamp-common.scad b/scaffold-clamp-common.scad new file mode 100644 index 0000000..79ed1da --- /dev/null +++ b/scaffold-clamp-common.scad @@ -0,0 +1,489 @@ +// -*- C -*- + +include + +tube_dia = 48.3; + +th = 7; + +pin_gap = 1.5; // around + +smooth_r = 15; + +bolt_dia = 5; +bolt_flat = 10 + 1; + +nbolts = 2; + +open_gap = 10; + +hinge_unit = 10; +hinge_z_gap = 1; + +hinge_units = 4; + +bolt_gap = 1.0; // total, on both sides + +// ---------- vhook ---------- + +vhook_th = 14; + +// ---------- cleat ---------- + +cleat_frames = 10; +cleat_curve_r = 200; +cleat_horn_l = 40; +cleat_horn_d_min = [10, 12]; +cleat_horn_d_max = [12, 14]; +cleat_height = 25; +cleat_stem_l = 20; + +cleat_overlap = (1-cos(60)); + +// ---------- hhook ---------- + +hhook_inside = 40; +hhook_th = 4; +hhook_l = 40; + +// ---------- linear bracket ---------- + +linear_bracket_h = 50; +linear_bracket_l = 100; +linear_bracket_t = 15; +linear_bracket_hole_offset = 20; +linear_bracket_hole_dia = 5 + 1.00; + +// ========== defaults ========== + +pin_head_th = th/2; +pin_dia = th; +pin_hole_dia = pin_dia/2; +pin_tail = pin_hole_dia + pin_head_th + hinge_z_gap*3; + +// ========== calculated ========== + +TAU = PI*2; + +hole_dia = th + pin_gap; + +bolt_hole_r = (bolt_dia + bolt_gap)/2; + +main_r = tube_dia/2 + th; + +hinge_gap = pin_gap; + +hinge_o_r = 0.5 * hole_dia + th; + +hinge_x = -0.5 * tube_dia - hinge_o_r; +bolt_x = 0.5 * tube_dia + th + bolt_flat * 0.5; +max_x = bolt_x + max(bolt_hole_r + th, 0.5 * bolt_flat/2); + +flats_y = open_gap/2 + th; + +stride_z = hinge_unit*2 + hinge_z_gap*2; +total_z = hinge_units * stride_z - hinge_z_gap; + +min_z = -total_z/2; +max_z = +total_z/2; + +pin_flatten = pin_dia/2 * (1 - cos(45)); + +bolt_stride = total_z / nbolts; + +// calculated - vhook + +vhook_inside = 15; + +vhook_theta = atan2( smooth_r, main_r ); + +vhook_y0 = -max(main_r, (tube_dia/2 + vhook_th)); +vhook_ctr = vhook_y0 - vhook_inside/2; +vhook_outer_dia = vhook_inside + vhook_th*2; + +// calculated - cleat + +cleat_horn_tl = cleat_horn_l + cleat_stem_l/2; + +vcleat_dz = max(0, + cleat_horn_tl + + cleat_horn_d_min[0]/2 + - cleat_horn_d_min[0]/2 * cleat_overlap + - total_z/2 + ); + +// calculated - hhook + +hhook_outer_dia = hhook_inside + hhook_th*2; + +hhook_ctr = -max(main_r + hhook_inside/2, + tube_dia/2 + hhook_outer_dia/2); + +$fa = 3; +$fs = 0.1; + +module SmoothPlan(){ + offset(r=-smooth_r) offset(delta=smooth_r) children(0); +} + +module TubePlan(){ circle(r = tube_dia/2); } +module MainCirclePlan(){ circle(r = main_r); } + +module PlanWeldMainCircle(){ + intersection(){ + difference(){ + SmoothPlan(){ + union(){ + MainCirclePlan(); + children(0); + } + } + TubePlan(); + } + rotate(-135) square(100); + } +} + +module MainPlan(flatten=false) { + difference(){ + SmoothPlan() + union(){ + translate([hinge_x, 0]) circle(r= hinge_o_r); + MainCirclePlan(); + rectfromto([0, -flats_y], + [max_x, +flats_y]); + } + TubePlan(); + rectfromto([0, -open_gap/2], + [max_x+1, +open_gap/2]); + translate([hinge_x, 0]) { + intersection(){ + circle(r= hole_dia/2); + if (flatten) + translate([ pin_flatten, 0 ]) + square(center=true, [hole_dia, hole_dia + 1]); + } + } + } +} + +module Portion(d=0) { + translate([hinge_x, 0]) circle(r= hinge_o_r + d); + rectfromto([hinge_x*2, 0], + [max_x+10, -(tube_dia/2+th+10)]); +} + +module MainPlanA(flatten){ + intersection(){ + MainPlan(flatten); + Portion(0); + } +} + +module MainPlanB(flatten){ + difference(){ + MainPlan(flatten); + Portion(hinge_gap); + } +} + +module HalfClampXPositive(flatten=false){ + translate([0,0, min_z]) { + linextr(0, total_z) mirror([0,1]) MainPlanB(); + for (i=[0 : hinge_units-1]) { + translate([0,0, stride_z*i]) + linextr(0, hinge_unit) MainPlanA(flatten); + } + } +} + +module HalfClampXNegative(){ + for (j=[0:nbolts-1]) { + translate([ bolt_x, 0, min_z + (j + 0.5) * bolt_stride ]) { + translate([0, -tube_dia/2, 0]) + rotate([-90,0,0]) + cylinder(r= bolt_hole_r, h= tube_dia); + translate([0, -flats_y, 0]) + rotate([90,0,0]) + cylinder(r= bolt_flat/2, h= tube_dia/2); + } + } +} + +module HalfClampX(flatten=false){ + difference(){ + HalfClampXPositive(flatten); + HalfClampXNegative(); + } +} + +// ---------- vhook ---------- + +module VHookProfile() { + translate([0, -vhook_inside/2 - vhook_th/2]) + circle(r = vhook_th/2); +} + +module VHookHookMain(outer=false){ ////toplevel + rotate([0,90,0]) + rotate_extrude(convexity=10) + rotate([0,0,90]) + hull(){ + VHookProfile(); + if (outer) { + translate([0,-vhook_outer_dia]) square(center=true, vhook_th); + } + } +} + +module VHookA(){ ////toplevel + DummyA(); + + translate([0, vhook_ctr, 0]){ + for (m=[0,1]) { + mirror([0, m, 0]) { + linextr(-0.1, vhook_outer_dia/2) + VHookProfile(); + translate([0, -vhook_inside/2 -vhook_th/2, vhook_outer_dia/2]) + sphere(r= vhook_th/2); + } + } + + intersection(){ + VHookHookMain(outer=true); + linextr_y_xz(0, vhook_outer_dia/2) hull(){ + VHookProfile(); + translate([0,-0.1]) square(center=true, [vhook_th, 0.2]); + } + } + + intersection(){ + VHookHookMain(); + translate([0,0, -vhook_outer_dia]) + cube(center=true, vhook_outer_dia*2); + } + } + + //translate([0, vhook_y0, 50]) rotate([0,0,-90]) color("black") cube(10); + // translate([0,0,-150]) rotate([0,0,180 + theta]) color("blue") cube(100); +} + +module VHookPlanDemo(){ + MainPlanA(); + translate([0, vhook_ctr, 5]) + for (m=[0,1]) { + mirror([0,m]) + color("blue") VHookProfile(); + } +} + +// ---------- cleat ---------- + +function cleat_frame_theta(s) = s * cleat_horn_tl / cleat_curve_r * 360/TAU; +function cleat_frame_z(s) = cleat_curve_r * (1 - cos(cleat_frame_theta(s))); +function cleat_frame_x(s) = cleat_curve_r * sin(cleat_frame_theta(s)); +function cleat_frame_r(s) = ( cleat_horn_d_min * s + + cleat_horn_d_max * (1-s) ) * 0.5; + +module CleatFrameSphere(r) { + scale([1, r[1]/r[0], 1]) + sphere(r= r[0]); +} + +module CleatFrame(s) { + r = cleat_frame_r(s); + translate([cleat_frame_x(s), 0, cleat_frame_z(s)]) + rotate([0, 90, 0]) + CleatFrameSphere(r); +} + + +module CleatHorn(){ + for (si=[0 : cleat_frames-2]) { + s0 = si / (cleat_frames-1); + s1 = (si+1) / (cleat_frames-1); + hull(){ + CleatFrame(s0); + CleatFrame(s1); + } + } +} + +module CleatBase(){ + frames = cleat_frames/2; + se = cleat_stem_l/2 / cleat_horn_tl; + r = cleat_frame_r(se); + + hull(){ + for (s = [-se, se]) { + for (z= [-(cleat_height + tube_dia/2), + cleat_frame_z(s)]) { + translate([cleat_frame_x(s), 0, z]) + linear_extrude(height=0.1) + scale([1, r[1]/r[0]]) + circle(r=r[0]); + } + } + } +} + +module VCleat(){ + intersection(){ + translate([0,0, vcleat_dz]){ + difference(){ + translate([0, -(main_r + cleat_height), 0]) { + rotate([0, -90, 90]) { + CleatBase(); + for (m=[0,1]) { + mirror([m,0,0]) { + CleatHorn(); + } + } + } + } + linextr(-cleat_stem_l, +cleat_stem_l) + circle(r = tube_dia/2 + 0.1); + } + } + translate([0,0, total_z * 0.5]) + cube(center=true, + (main_r + cleat_stem_l)*4 * [1,1,0] + + total_z * [0,0,2]); + } +} + +module VCleatA(){ ////toplevel + DummyA(); + VCleat(); +} + +// ---------- hhook ---------- + +module HHookHookPlan(){ + translate([0, hhook_ctr]){ + difference(){ + circle(r = hhook_outer_dia/2); + circle(r = hhook_inside/2); + rectfromto([+hhook_outer_dia, -hhook_outer_dia], + [0, +hhook_outer_dia]); + } + translate([0, -(hhook_inside/2 + hhook_th/2)]){ + hull(){ + for (x=[-0.1, hhook_l]) { + translate([x,0]) square(center=true, hhook_th); + } + } + } + } +} + +module HHookA(){ ////toplevel + DummyA(); + linextr(min_z, max_z) { + HHookHookPlan(); + } +} + +module HHookPlanDemo(){ + MainPlanA(); + HHookHookPlan(); +} + +// ---------- linear bracket ---------- + +module LinearBracketA(){ ////toplevel + difference(){ + union(){ + HalfClampXPositive(); + mirror([1,0,0]) + linextr_y_xz(-open_gap/2 - linear_bracket_t, -open_gap/2) + rectfromto([0, min_z], + [max_x + linear_bracket_l, min_z + linear_bracket_h]); + } + HalfClampXNegative(); + linextr(-1000,1000) + TubePlan(); + mirror([1,0,0]) + linextr_y_xz(-100,100) { + for (t = [ + [1,1] * linear_bracket_hole_offset, + -[1,1] * linear_bracket_hole_offset + + [linear_bracket_l, linear_bracket_h] + ]) { + translate([ max_x, min_z ] + t) + circle(r= linear_bracket_hole_dia/2); + } + } + } +} + +// ---------- misc ---------- + +module PinSitu(){ ////toplevel + difference(){ + union(){ + translate([0,0, -pin_head_th]) + cylinder(r= pin_dia/2, h = total_z + pin_head_th + pin_tail); + mirror([0,0,1]) + cylinder(r= hinge_o_r - pin_gap, h = pin_head_th); + } + translate([0,0, total_z + pin_tail/2]) + rotate([0,90,0]) + translate([0,0, -pin_dia]) + cylinder(r= pin_hole_dia/2, h=pin_dia*2); + translate([pin_dia/2 * cos(45), -50, -pin_head_th*2]) + cube([50,100, total_z*2]); + } +} + +module Pin(){ ////toplevel + rotate([0,90,0]) { + PinSitu(); + } +} + +module GeneralB(){ ////toplevel + HalfClampX(true); +} + +module DummyA(){ ////toplevel + HalfClampX(); +} + +module PlanDemo(){ ////toplevel + MainPlan(); + translate([0,0,-4]) color("red") Portion(1); + translate([0,0,-2]) color("grey") Portion(0); + + translate([0, tube_dia*1.5]) { + MainPlanB(); + MainPlanA(); + } + + translate([0, -tube_dia*1.5]) { + VHookPlanDemo(); + } + translate([tube_dia*4, 0]) { + HHookPlanDemo(); + } +// translate([max_x - hinge_x + 20, 0]) color("blue") MainPlanA(); +} + +module DemoA(){ DummyA(); } + +module Demo(){ ////toplevel + color("red") rotate([180,0,0]) GeneralB(); + color("blue") DemoA(); + color("orange") translate([hinge_x, 0, min_z - hinge_z_gap]) + rotate([0,0,180]) PinSitu(); +} + +module DemoPair(){ ////toplevel + color("red") rotate([180,0,0]) DemoA(); + color("blue") DemoA(); + color("orange") translate([hinge_x, 0, min_z - hinge_z_gap]) + rotate([0,0,180]) PinSitu(); +} + +//PlanDemo(); +//HalfClamp(); diff --git a/scaffold-clamp-linear-bracket.scad b/scaffold-clamp-linear-bracket.scad new file mode 100644 index 0000000..a5fbe24 --- /dev/null +++ b/scaffold-clamp-linear-bracket.scad @@ -0,0 +1,11 @@ +// -*- C -*- + +// Per linear bracket print +// LinearBracketA +// GeneralB +// Pin + +//// toplevels-from: +include + +module DemoA(){ LinearBracketA(); } diff --git a/scaffold-clamp-straphook.scad b/scaffold-clamp-straphook.scad new file mode 100644 index 0000000..1804be9 --- /dev/null +++ b/scaffold-clamp-straphook.scad @@ -0,0 +1,22 @@ +// -*- C -*- + +// Per gym ring strap retainer print +// HHookA +// GeneralB +// Pin + +//// toplevels-from: +include + +th = 3; +hhook_th = 3; +hinge_units = 2; +nbolts = 1; +hinge_unit = 5; +bolt_dia = 3; +bolt_flat = 7 + 1; + +hhook_inside = 35; +hhook_l = 50; + +module DemoA(){ HHookA(); } diff --git a/scaffold-clamp-tensioner.scad b/scaffold-clamp-tensioner.scad new file mode 100644 index 0000000..6207dfb --- /dev/null +++ b/scaffold-clamp-tensioner.scad @@ -0,0 +1,11 @@ +// -*- C -*- + +// Per tensioner print +// VHookA +// GeneralB +// Pin + +//// toplevels-from: +include + +module DemoA(){ VHookA(); } diff --git a/screw-recess-test-number.fig.pl b/screw-recess-test-number.fig.pl new file mode 100755 index 0000000..023b315 --- /dev/null +++ b/screw-recess-test-number.fig.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl + +my $number = shift @ARGV; +die unless $number =~ m/^\d+$/; + +my $fontsz = $number * 6 + 12; + +print <= 0 ? recessdepth_arg : -recessdepth_arg * recessdia; + +function RecessedScrewCutout_totaldepth(recessdia, + recessdepth_arg=RecessedScrewCutout_defaultrecessdepth_flat) = + RecessedScrewCutout_recessdepth(recessdia, recessdepth_arg) + + + 0.5*recessdia + 0.1; + +module RecessedScrewCutout(shaftdia, recessdia, shaftlen, + zbelow=1, + recessdepth_arg=RecessedScrewCutout_defaultrecessdepth_flat) { + // pass recessdepth_arg=-1 for the default for flat heads + // pass recessdepth_arg=-1 for the default for flat heads + recessdepth = RecessedScrewCutout_recessdepth(recessdia, recessdepth_arg); + recesstopz = RecessedScrewCutout_totaldepth(recessdia, recessdepth_arg); + + translate([0,0,-zbelow]) cylinder(r=shaftdia/2, h=shaftlen+zbelow, $fn=20); + RecessScrewCutout_RecessCylinder(recessdia,zbelow, recessdepth); + intersection(){ + cube([recessdia + 1, shaftdia + 0.1, recesstopz*2 + 1], center=true); + translate([0, -recessdia, recesstopz]) + rotate([0,135,0]) cube([recessdia, recessdia*2, 10]); + RecessScrewCutout_RecessCylinder(recessdia,zbelow, recesstopz+1); + } +} + +// nom. shaft +// shaft slop +screw_info_M2 = [2, 1.2]; +screw_info_M3 = [3, 1.2]; +screw_info_M4 = [4, 1.1]; +screw_info_M5 = [5, 1.0]; +screw_info_M6 = [6, 1.2]; + +function screw_shaft_dia_nom(info) = info[0]; +function screw_shaft_dia_use(info) = info[0] + info[1]; +function screw_recess_dia_use(info) = info[0] * 2.50 + 1.0; +function screw_recess_depth(info) = info[0] * 1.00 + 0.50; +function screw_recess_depth_allen(info) = info[0] * 1.55 + 0.50; + +function RecessedScrewCutoutStandard_totaldepth(info) = + RecessedScrewCutout_totaldepth(screw_recess_dia_use(info), + screw_recess_depth(info)); + +function RecessedScrewCutoutStandardAllen_totaldepth(info) = + RecessedScrewCutout_totaldepth(screw_recess_dia_use(info), + screw_recess_depth_allen(info)); + +module RecessedScrewCutoutStandard(info, shaftlen, zbelow=1) { + RecessedScrewCutout(screw_shaft_dia_use(info), + screw_recess_dia_use(info), + shaftlen, zbelow, + screw_recess_depth(info)); +} + +module RecessedScrewCutoutStandardAllen(info, shaftlen, zbelow=1) { + RecessedScrewCutout(screw_shaft_dia_use(info), + screw_recess_dia_use(info), + shaftlen, zbelow, + screw_recess_depth_allen(info)); +} + +tests = [ + screw_info_M2, + screw_info_M3, + screw_info_M4, +2 screw_info_M5, + screw_info_M6 + ]; + +function Test_blocksz(t) = screw_recess_dia_use(t) + 7; + +module OneTestCore(t, h, ymul, labelnumber=false){ + blocksz = Test_blocksz(t); + translate([0, ymul * (blocksz*0.5 - 1.5), 0]) { + difference(){ + translate([-blocksz/2, -blocksz/2, 0]) + cube([blocksz, blocksz, h]); + child(); + } + if (labelnumber) { + rotate([90,0,0]) + translate([-blocksz/4,blocksz/5, blocksz/2-1]) + linear_extrude(height=0.3+1) + import(file=str("screw-recess-test-number-s",t[0],".dxf"), convexity=100); + } + } +} + +module OneTest(t){ + h = RecessedScrewCutoutStandard_totaldepth(t) + 3; + ha = RecessedScrewCutoutStandardAllen_totaldepth(t) + 3; + OneTestCore(t, h, 1){ + RecessedScrewCutoutStandard(t, h+1); + } + OneTestCore(t, ha, -1, true){ + RecessedScrewCutoutStandardAllen(t, ha+1); + } +} + +function Test_x(i) = i<=0 ? 0 : + Test_x(i-1) + Test_blocksz(tests[i-1])/2 + Test_blocksz(tests[i])/2 - 3; + +module Tests(){ + for (i = [0:len(tests)-1]) { + echo(i, Test_x(i)); + translate([Test_x(i), 0, 0]) + OneTest(tests[i]); + } +} + +//OneTest(tests[1]); +Tests(); +//Hole(); +//Holes(); diff --git a/sealing-box.scad.m4 b/sealing-box.scad.m4 new file mode 100644 index 0000000..dbfb220 --- /dev/null +++ b/sealing-box.scad.m4 @@ -0,0 +1,180 @@ +// -*- C -*- + +// This file can be used in two ways: +// +// A. Rectangular boxes +// 1. include +// 2. assign() values to (xxx these should be $ variables) +// $sealingbox_wallth +// $sealingbox_sz[0] (outer dimension) +// $sealingbox_sz[1] (outer dimension) +// $sealingbox_sz[2] (inner dimension) +// $sealingbox_ceilth +// $sealingbox_floorth +// $sealingbox_wallth +// 3. use the modules +// SealingBox_RectBox +// SealingBox_RectLid +// (origin is notional outside corner, but at level of +// inside of base; box extends to positive x,y,z) +// +// B. Complicated shapes, but harder work +// 1. Be a .m4 file and m4_include sealing-box.scad.m4 +// 2. Define your own BoxDoShapeSomething like BoxDoShapeRect +// 3. Invoke BoxUseShape +// 4. Use the Box and Lid modules generated +// +// Other settings +// $sealingbox_cnrrad + +$sealingbox_cnrrad = 10; +$sealingbox_crude = false; +$sealingbox_inner_slop = 0.2; + +m4_define(`BoxLocals',` + xbox = $sealingbox_sz[0]; + ybox = $sealingbox_sz[1]; + zbox = $sealingbox_sz[2]; + wall = $sealingbox_wallth; + floorth = $sealingbox_floorth; + ceilth = $sealingbox_ceilth; + cnrrad = $sealingbox_cnrrad; + + xbox_lin = xbox - cnrrad*2; + ybox_lin = ybox - cnrrad*2; +') + +m4_define(`innertube', `(1.0 + 0.2)') +m4_define(`lidoverlap', `1.5') +m4_define(`lidoverhang', `6') +m4_define(`tubesealrad', `2.0') + +m4_define(`BoxFn',`$fn= $sealingbox_crude ? ($2) : ($1)') + +m4_dnl Box_Part($1=transl_x,$2=transl_y, $3=rot_z,$4=mirror_xy) +m4_dnl $5=kind, $6=kindargs, $7=profile(profileargsargs)) +m4_define(`Box_Part',` + translate([($1),($2)]) + rotate([0,0,($3)]) + mirror([($4),0,0]) + BoxPart_Extrude_$5($6, $7)') m4_dnl + +boxpart_d = 0.01; + +m4_dnl BoxPart_Extrude_Linear(dist, `profile(...);'); +m4_define(`BoxPart_Extrude_Linear',` + rotate([90,0,0]) + translate([0,0, -($1)]) + linear_extrude(height= boxpart_d + ($1)) { + $2 + } +') + +m4_dnl BoxPart_Extrude_Arc(x0_radius, swept_angle, `profile(...);') +m4_dnl arc starting at transl_x, transl_y, moving towards positive +m4_dnl y at first and then bending towards negative x, until +m4_dnl use negative x0_radius to inciate bending towards positive x +m4_dnl swept_angle is reached +m4_dnl x0_radius is the radius of the extruded part at x=0, not of the box +m4_define(`BoxPart_Extrude_Arc',` + translate([-($1),0,0]) + intersection(){ + translate([0,0,-500]) + linear_extrude(height=1000) + scale(500) + mirror([($1)<0, 0,0]) + FArcSegment_mask($2); + rotate_extrude(convexity=10, $fs=1, BoxFn(36,8)) + mirror([($1)<0, 0,0]) + translate([+($1),0,0]){ + $3 + } + } +') + +m4_dnl BoxDoShapeRect(`profile(profileargs)'); +m4_define(`BoxDoShapeRect',` + Box_Part(0, cnrrad, 0,0, Linear,`ybox_lin', `$1' ) + Box_Part(0, ybox-cnrrad, 0,0, Arc,`-cnrrad,90' , `$1' ) + Box_Part(cnrrad, ybox, -90,0, Linear,`xbox_lin', `$1' ) + Box_Part(xbox-cnrrad, ybox, -90,0, Arc,`-cnrrad,90' , `$1' ) + Box_Part(xbox, ybox-cnrrad, -180,0, Linear,`ybox_lin', `$1' ) + Box_Part(xbox, cnrrad, -180,0, Arc,`-cnrrad,90' , `$1' ) + Box_Part(xbox-cnrrad, 0, -270,0, Linear,`xbox_lin', `$1' ) + Box_Part(cnrrad, 0, -270,0, Arc,`-cnrrad,90' , `$1' ) +') + +m4_dnl ' + +module SealingBox_WallProfile(){ + BoxLocals + z = zbox - innertube - tubesealrad; + translate([0, -0.1]) square([wall, z]); + translate([tubesealrad, z]) circle(r=tubesealrad, BoxFn(20,6)); +} + +module SealingBox_FloorProfile(){ + BoxLocals + mirror([0,1]) square([wall, floorth]); +} + +function SealingBox_lidbigger() = lidoverlap + innertube; + +module SealingBox_LidProfile(){ + BoxLocals + rad = tubesealrad + innertube; + morex = wall; + inner_buttress_h = tubesealrad*1.5 + innertube + ceilth; + + difference(){ + translate([0, zbox + ceilth]) mirror([0,1]) { + translate([-SealingBox_lidbigger(), + 0]) + square([lidoverlap + innertube + tubesealrad, + lidoverhang + innertube + ceilth]); + square([tubesealrad*2 + innertube + lidoverlap, + inner_buttress_h]); + } + hull(){ + translate([tubesealrad, + zbox - innertube - tubesealrad]) + for (t=[ [0,0], + [0, -zbox] + ]) { + translate(t) + circle(r= tubesealrad + innertube, BoxFn(20,6)); + } + } + } + translate([tubesealrad*2 + $sealingbox_inner_slop, + zbox + ceilth]) { + mirror([0,1]) { + square([lidoverlap + innertube, + inner_buttress_h]); + } + } +} + +module SealingBox_CeilProfile(){ + BoxLocals + translate([0, zbox]) + square([wall*2, ceilth]); +} + +// BoxDoShape(Basename,BoxDoShapeSomething) +// generates modules BasenameBox and BasenameLid +m4_define(`BoxUseShape',` + module $1Box(){ + BoxLocals + $2(SealingBox_WallProfile();); + hull(){ $2(SealingBox_FloorProfile();); } + } + + module $1Lid(){ + BoxLocals + $2(SealingBox_LidProfile();); + hull(){ $2(SealingBox_CeilProfile();); } + } +') + +BoxUseShape(`SealingBox_Rect',`BoxDoShapeRect') diff --git a/secateurs-clip.scad b/secateurs-clip.scad new file mode 100644 index 0000000..bdb008d --- /dev/null +++ b/secateurs-clip.scad @@ -0,0 +1,22 @@ +// -*- C -*- + +l = 47; +d = 18; + +w = 15; +t = 10; +te = 7; + +linear_extrude(height=15, convexity=4) { + for (m=[0,1]) { + mirror([m,0]) { + polygon([[ -1, 0 ], + [ -1, -t ], + [ l/2+te, -t ], + [ l/2+te, d ], + [ l/2, d ], + [ l/2, 0 ], + ]); + } + } +} diff --git a/sewing-table-end-profile-photo.jpg b/sewing-table-end-profile-photo.jpg new file mode 100644 index 0000000..73f3a20 Binary files /dev/null and b/sewing-table-end-profile-photo.jpg differ diff --git a/sewing-table-end-profile.fig b/sewing-table-end-profile.fig new file mode 100644 index 0000000..2250e94 --- /dev/null +++ b/sewing-table-end-profile.fig @@ -0,0 +1,29 @@ +#FIG 3.2 Produced by xfig version 3.2.6a +Landscape +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +2 5 0 1 0 -1 60 -1 -1 0.000 0 0 -1 0 0 5 + 0 sewing-table-end-profile-photo.jpg + 2655 6615 23743 6615 23743 12787 2655 12787 2655 6615 +2 1 0 1 1 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3780 10170 22635 9990 +2 1 0 2 9 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 22671 9881 22587 6522 +2 1 0 3 14 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3780 8137 22635 7957 +2 1 0 2 9 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4090 9278 4034 6610 +2 2 0 2 28 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1818 8008 5248 8008 5248 14648 1818 14648 1818 8008 +3 1 0 1 6 7 40 -1 -1 0.000 0 0 0 17 + 8544 8080 12341 8071 16290 7965 19845 7830 22214 7727 22560 7582 + 22605 7370 22539 6869 22548 6611 12555 4725 4252 5873 4101 6534 + 4052 7107 4047 7674 4058 7812 4323 7950 4864 8005 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 diff --git a/sewing-table-front-profile-photo.jpg b/sewing-table-front-profile-photo.jpg new file mode 100644 index 0000000..d0dd7f1 Binary files /dev/null and b/sewing-table-front-profile-photo.jpg differ diff --git a/sewing-table-front-profile.fig b/sewing-table-front-profile.fig new file mode 100644 index 0000000..d318174 --- /dev/null +++ b/sewing-table-front-profile.fig @@ -0,0 +1,26 @@ +#FIG 3.2 Produced by xfig version 3.2.6a +Landscape +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +2 5 0 1 0 -1 60 -1 -1 0.000 0 0 -1 0 0 5 + 0 sewing-table-front-profile-photo.jpg + 2250 2115 14794 2115 14794 8172 2250 8172 2250 2115 +2 2 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 5311 3627 1892 3627 1892 8634 5311 8634 5311 3627 +2 1 0 1 1 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3455 7525 12541 7946 +3 1 0 1 28 7 40 -1 -1 0.000 0 0 0 25 + 5678 3664 4889 3652 4306 3643 3820 3620 3256 3600 2800 3591 + 2416 3574 3055 1515 11023 1659 11041 2606 10905 2752 10663 2946 + 10504 3060 10304 3176 10113 3305 9859 3397 9581 3493 9306 3565 + 8734 3613 8367 3618 7996 3636 7520 3639 7119 3651 6713 3663 + 6299 3670 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 diff --git a/sewing-table-jig.scad b/sewing-table-jig.scad new file mode 100644 index 0000000..9302119 --- /dev/null +++ b/sewing-table-jig.scad @@ -0,0 +1,21 @@ +//// toplevels-from:sewing-table.scad +include + +JIG = true; + +tile_th=0.8; +interlock_dia=5; + +jig_pencil_rad = 1; +jig_pencil_slotlen = 10; +jig_min_th = 0.3; +jig_post_hole_slop = 0.5; + +test_tile_th = -0.1; + +test_edge = interlock_dia * 0.5 + interlock_fine + 2; +round_edge_rad = tile_th/2; +frontcurve_z_slop = 15; +rearcurve_z_slop = 20; + +interlock_fine = tile_th/8; diff --git a/sewing-table-rear-profile-photo.jpg b/sewing-table-rear-profile-photo.jpg new file mode 100644 index 0000000..34bf64d Binary files /dev/null and b/sewing-table-rear-profile-photo.jpg differ diff --git a/sewing-table-rear-profile.fig b/sewing-table-rear-profile.fig new file mode 100644 index 0000000..3a6af71 --- /dev/null +++ b/sewing-table-rear-profile.fig @@ -0,0 +1,28 @@ +#FIG 3.2 Produced by xfig version 3.2.6a +Landscape +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +2 2 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3255 -6357 -7303 -6357 -7303 18013 3255 18013 3255 -6357 +2 1 0 1 1 7 50 -1 -1 0.000 0 0 -1 0 0 2 + -2808 6102 -1101 12715 +2 5 0 1 0 -1 60 -1 -1 0.000 0 0 -1 0 0 5 + 0 sewing-table-rear-profile-photo.jpg + 11014 -9675 -5445 -9675 -5445 16370 11014 16370 11014 -9675 +2 1 0 1 14 7 50 -1 -1 0.000 0 0 -1 0 0 2 + -536 -9216 -5067 1826 +3 1 0 1 28 7 40 -1 -1 0.000 0 0 0 26 + 3330 -6390 1755 -5895 495 -5310 -630 -4590 -1440 -3825 -2115 -2790 + -2745 -1710 -3240 -450 -3555 990 -3555 2430 -3420 3780 -2970 5625 + -2610 6930 -2205 8415 -1890 9630 -1485 11160 -1125 12690 -855 14085 + -90 16020 14715 9630 9855 -6975 9270 -8550 8685 -8010 7200 -7560 + 5985 -7200 4365 -6660 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 diff --git a/sewing-table-test.scad b/sewing-table-test.scad new file mode 100644 index 0000000..14b83b9 --- /dev/null +++ b/sewing-table-test.scad @@ -0,0 +1,15 @@ +//// toplevels-from:sewing-table.scad +include +TEST = true; +test_tile_th = 0.67; +test_edge = interlock_dia * 0.5 + interlock_fine + 2; + +leg_n_fins = 2; + +leg_top_thick = 3; +leg_bot_thick = 4; +leg_top_flat_z = 0.5; +leg_fin_top_w = 2; +leg_fin_bot_w = 3; +leg_fin_bot_rad = 15; +leg_bot_mid_dia = 9; diff --git a/sewing-table.scad.m4 b/sewing-table.scad.m4 new file mode 100644 index 0000000..b71e041 --- /dev/null +++ b/sewing-table.scad.m4 @@ -0,0 +1,1193 @@ +// -*- C -*- + +include +include + +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 t.pnm + // lpr t.pnm +} + +//TestPiece1(); +//TestPiece2(); +//Demo(); + +//Machine_NewRearProfile(); +//Machine_NewRearCurve(); +//Machine_NewFrontProfile(); +//Machine_NewEndProfile(); +//Machine_NewEndProfileDemo(); +//Machine_Curves(); +//Machine(); +//FitTest(); +//MachineEnvelope(); diff --git a/shelf-label-holder.scad b/shelf-label-holder.scad new file mode 100644 index 0000000..8353d7e --- /dev/null +++ b/shelf-label-holder.scad @@ -0,0 +1,65 @@ +// -*- C -*- + +prong_nomdepth = 15; +prong_curverad = 30; +prong_thick = 0.7; +prong_maxdepth = 18; + +front_thick = 2.5; + +//nom_shelf = 14.54 + 0.5; +nom_shelf = 20.315 + 0.5; + +interference = 0.75; + +length = 60; + +// calculated + +interference_angle = atan2(interference, prong_nomdepth); + +module ProngElevationUnrotated(){ + intersection(){ + union(){ + translate([ prong_nomdepth, prong_curverad ]) + circle( prong_curverad , $fa=0.5 ); + translate([ -10, 0 ]) + square([ prong_nomdepth + 10, 10 ]); + } + translate([-5, -5]) + square([ prong_maxdepth + 5, prong_thick + 5]); + } +} + +module Elevation(){ + difference(){ + union(){ + rotate(-interference_angle) + ProngElevationUnrotated(); + translate([0, -nom_shelf]) + mirror([0,1]) + rotate(-interference_angle) + ProngElevationUnrotated(); + translate([-10, -nom_shelf - prong_thick/2]) + square([10, nom_shelf + prong_thick]); + } + mirror([1,0]) + translate([ front_thick, -100 ]) + square([ 50, 200 ]); + } +} + +module Main(){ + linear_extrude(height=length) + Elevation(); +} + +module Print(){ + rotate([0,-90,0]) + Main(); +} + +//ProngElevationUnrotated(); +//Elevation(); +Main(); +//Print(); diff --git a/simplephone-case-test.scad b/simplephone-case-test.scad new file mode 100644 index 0000000..eedb932 --- /dev/null +++ b/simplephone-case-test.scad @@ -0,0 +1,3 @@ +//// toplevels-from:sewing-table.scad +include +$test = true; diff --git a/simplephone-case.scad b/simplephone-case.scad new file mode 100644 index 0000000..2cbc5ff --- /dev/null +++ b/simplephone-case.scad @@ -0,0 +1,167 @@ +// -*- C -*- + +psz = [ + 120, + 56 + 5 - 3.75, + 15 + 3, + ]; + +thick = [ + 2, + 2, + 1.5, + ]; + +btn_x = 59.6; +btn_dia = 13; +btn_y = 14.03; + +abtn_x = 46.85; +abtn_sz = [ 11, 13 ]; + +screen_xbot = 79; +screen_sz = [ 35, 46 ]; + +thumb_xbot = 90; +thumb_dia = 25; + +vol_xbot = 86.5; +vol_xtop = 107.5; +vol_depth = 1.0; +vol_zsz = 9; +vol_zoff = 0; + +rail_ysz = 2.5; +rail_zsz = 2.5; + +stay_height = 1.49; + +case_x_less = 0; //case_x_less = 10; + +inner_cnr_rad = 4.0; + +// calculated + +btn_yprop = btn_y / psz[1]; +echo(btn_yprop); + +ym = psz[1]/2; +outer_cnr_rad = inner_cnr_rad + thick[2]; + +x_sliced = outer_cnr_rad * (1-sin(45)); + +$screen = true; + +module RoundedProfile(sz, cnr_rad){ + hull(){ + for (x=[ cnr_rad, sz[0]-cnr_rad ]) + for (y=[ cnr_rad, sz[1]-cnr_rad ]) + translate([x,y]) + circle(r= cnr_rad, $fn=20); + } +} + +module RoundedCube(sz, cnr_rad){ + if ($test) + cube(sz); + else hull(){ + for (x=[ cnr_rad, sz[0]-cnr_rad ]) + for (y=[ cnr_rad, sz[1]-cnr_rad ]) + for (z=[ cnr_rad, sz[2]-cnr_rad ]) + translate([x,y,z]) + sphere(r= cnr_rad, $fn=40); + } +} + +module Stay(xbot, xtop, width, midgap_width) { + translate([ (xbot+xtop)/2, psz[1]/2, psz[2] ]){ + difference(){ + cube([ xtop-xbot, width, stay_height*2 ], center=true); + if (midgap_width > 0) + cube([ 200, midgap_width, 10 ], center=true); + } + } +} + +module Stays(){ + Stay( 76, 82, 10, 0); + Stay(-0.1, 55, 10, 0); + Stay( 113,125, 70, 15); +} + +module Case(){ + difference(){ + mirror([1,0,0]) + translate(-thick + + - [1,0,0] * x_sliced) + RoundedCube(psz + + 2*thick + - [1,0,0] * (thick[0]) + + [1,0,0] * (x_sliced) + - [case_x_less, 0, 0], + outer_cnr_rad); + + for (yp= [ btn_yprop, 1-btn_yprop ]) + translate([ -btn_x, + yp * psz[1], + 0.5 * psz[2] ]) + cylinder(r= btn_dia/2, h=20); + + translate([ -abtn_x, + btn_yprop * psz[1], + psz[2] ]) + cube(concat(abtn_sz, [ thick[2]*3 ]), center=true); + + if ($screen) + mirror([1,0,0]) + translate([ screen_xbot, + (psz[1] - screen_sz[1])/2, + psz[2]-3 ]) + cube(concat(screen_sz, [ thick[2]+6 ])); + + hull(){ + for (x=[ thumb_xbot+thumb_dia/2, psz[0]+10 ]) + translate([ -x, + ym, + -thick[2]-1 ]) + cylinder(r= thumb_dia/2, + h= thick[2] + 2, + $fn= 20); + } + + mirror([1,0,0]) + translate([ (vol_xbot+vol_xtop)/2, 0, psz[2]/2 + vol_zoff ]) + cube([ vol_xtop-vol_xbot, vol_depth*2, vol_zsz ], center=true); + + translate([ thick[0], -10, -10 ]) + cube([ 10, psz[1]+20, psz[2]+20 ]); + + //translate([-50,-50,10]) cube([100,100,100]); + + mirror([1,0,0]) + difference(){ + RoundedCube(psz + [1,0,0], + inner_cnr_rad); + + Stays(); + + if (0) for (m=[0,1]) { + translate([0,ym,0]) mirror([0,m,0]) translate([0,-ym,0]) + translate([-1,-1, psz[2]-rail_zsz]) + cube([psz[0]+1, rail_ysz+1, rail_zsz+1]); + } + } + } +} + +module TestLoop(){ + intersection(){ + Case($screen=false); + translate([ -vol_xbot, 0,0 ]) + cube([ 4, 200,200 ], center=true); + } +} + +Case(); +//TestLoop(); +//RoundedCube(psz, inner_cnr_rad); diff --git a/size-tests.m-g b/size-tests.m-g new file mode 100644 index 0000000..fbb666b --- /dev/null +++ b/size-tests.m-g @@ -0,0 +1,213 @@ +; -*- fundamental -*- + +; slic3r originally produced this, with these comments, and then we edited: +; layer_height = 0.4 +; perimeters = 3 +; solid_layers = 3 +; fill_density = 0.4 +; perimeter_speed = 30 +; infill_speed = 60 +; travel_speed = 130 +; scale = 1 +; nozzle_diameter = 0.59 +; filament_diameter = 1.77 +; extrusion_multiplier = 1 +; single wall width = 0.71mm +; first layer single wall width = 0.60mm + +M190 S65 ; wait for bed temperature to be reached +M104 S215 ; set temperature +G28 ; home all axes +M109 S215 ; wait for temperature to be reached +G90 ; use absolute coordinates +G21 ; set units to millimeters +G92 E0 +M82 ; use absolute distances for extrusion + +!zprint=0.3 + +; "skirt" - prep extruder +!draw 5,5 100,5 101,5.5 101,6.0 100,6.5 5,6.5 + +; edge ticks for global motions sizing + +!edge_ticks(){ +!draw 20,10 10,10 10,20 +!draw 10,65 10,75 +!draw 10,120 10,130 20,130 +!draw 65,130 75,130 +!draw 120,130 130,130 130,120 +!draw 130,75 130,65 +!draw 130,20 130,10 120,10 +!draw 75,10 65,10 +!} + +;!edge_ticks() + +!outer_square(){ + !draw @0.000,@0.000 @5.000,@0.000 @5.000,@5.000 @0.000,@5.000 @0.000,@0.000 +!} +!inner_square(){ + !draw @0.500,@0.500 @4.500,@0.500 @4.500,@4.500 @0.500,@4.500 @0.500,@0.500 +!} +!outer_vlines(){ + !draw @0.000,@0.000 @0.000,@5.000 + !draw @5.000,@0.000 @5.000,@5.000 +!} +!inner_vlines(){ + !draw @0.500,@0.500 @0.500,@4.500 + !draw @4.500,@0.500 @4.500,@4.500 +!} +!outer_longrect(){ + !draw @0.000,@0.000 @5.000,@0.000 @5.000,@20.000 @0.000,@20.000 @0.000,@0.000 +!} +!inner_longrect(){ + !draw @0.500,@0.500 @4.500,@0.500 @4.500,@19.500 @0.500,@19.500 @0.500,@0.500 +!} + +!org_oblong(){ +; original test oblong + +!draw 68.498,79.498 \ +71.502,79.498 \ +71.502,82.502 \ +68.498,82.502 \ +68.498,79.588 \ +*67.899,78.899 \ +72.101,78.899 \ +72.101,83.101 \ +67.899,83.101 \ +67.899,78.989 \ +*67.300,78.300 \ +72.700,78.300 \ +72.700,83.700 \ +67.300,83.700 \ +67.300,78.390 \ + +!} + +!squares_tests(){ + +!orgy=40 + +!orgx=30 +!outer_longrect() + +!orgx=50 +!inner_longrect() +!outer_longrect() + +!orgx=90 +!inner_square() +!outer_square() + +!orgx=110 +!outer_square() + +!orgy=60 +!outer_vlines() + +!orgx=90 +!inner_vlines() +!outer_vlines() + +!orgy=80 +!outer_square() +!inner_square() + +!orgx=110 +!inner_square() + +!orgy=100 +!inner_vlines() + +!orgx=90 +!outer_vlines() +!inner_vlines() + +!orgx=50 +!orgy=80 +!outer_longrect() +!inner_longrect() + +!orgx=30 +!inner_longrect() + +!} + +!feedrate_tests(){ + +!orgx=20 +!extruderate=0.045 +!feedrate_test1() + +!orgx=30 +!extruderate=0.060 +!feedrate_test1() + +!orgx=40 +!extruderate=0.080 +!feedrate_test1() + +!orgx=50 +!extruderate=0.100 +!feedrate_test1() + +!orgx=60 +!extruderate=0.125 +!feedrate_test1() + +!orgx=70 +!extruderate=0.150 +!feedrate_test1() + +!orgx=80 +!extruderate=0.200 +!feedrate_test1() + +!} + +!feedrate_test1(){ + +!orgy=20 +!outer_longrect() + +!orgy=50 +!inner_longrect() +!outer_longrect() + +!orgy=80 +!outer_longrect() +!inner_longrect() + +!} + +!layer(){ + +;-------------------- + +;!squares_tests() +;!org_oblong() +!feedrate_tests() + +!} + +!layer() + +M104 S210 ; set temperature +M140 S60 ; set bed temperature + +!zprint=0.7 +!layer() + +!zprint=1.1 +!layer() + +M83 ; extruder relative +G1 F1000 ;1000mm/min extrusion +G1 E-10; retract +G1 X0 Y0 F10000 +G28 X0 Y0 +M104 S0 ; turn off temperature +M140 S0 ; turn off bed +M84 ; disable motors diff --git a/sleepphone-cable-box.scad b/sleepphone-cable-box.scad new file mode 100644 index 0000000..836f07d --- /dev/null +++ b/sleepphone-cable-box.scad @@ -0,0 +1,248 @@ +// -*- C -*- + +include + +wall = 0.75 * [1,1,1]; +wall_bot = 1.0; + +phone = [ 76.40, 30.96, 6.00 ]; // includes socket +phone_button_z = 6.58; +minwall = 0.50; + +expose = 4.00; + +cutout_dia = 7; +cutout_between = 5; + +button_dz = 1.35; +button_dz_centre = 1.35 + 0.75; +button_dz_outer = 1.35; + +button_dy_outer = 28.42; +button_dy_inner = 19.05; +button_dy_centre = 5.65; + +nrbutton_brace_x = 37.5; + +phone_slop = 0.5 * [1,1,0] + + 0.5 * [0,0,1]; + +led = [25.9, 9.44]; // y is from edge +led_dia = 4.4; + +// next values include slop +plug_maxw = 10.95 + 0.35; +plug_minw= 6.53 + 0.35; +plug_sllen= 6.50; +plug_totlen = 84.90 + 1.5 - 2.0; // to maxw, including phone + +plug_h = 6.5; +plug_tooth_h = 0.5; +plug_tooth_dy = 0.5; + +keeper_prong = 2; +keeper_stalk_basewidth = 12; +keeper_stalk_len = 70; +keeper_stalk_gap = 1; +keeper_stalk_thick = wall_bot; + +keeper_stalk_base_reinforce_len = 5; +keeper_stalk_base_reinforce_thick = 2.0; + +// calculated + +top_z = max( phone[2] + wall[2], + phone_button_z + minwall ) + + phone_slop[2]; + +plugkeeper_x_maxw = phone[0] - plug_totlen; + +plugkeeper_p_max = [ 0, plug_maxw/2 ]; +plugkeeper_p_min = [ -plug_sllen, plug_minw/2 ];; +plugkeeper_d_u = unitvector2d( + clockwise2d( + vecdiff2d( plugkeeper_p_max, plugkeeper_p_min ) + ) + ); + +module MainProfileInnerHalf(){ + p = phone + phone_slop; + pbc = p[2] + button_dz_centre; + pbo = p[2] + button_dz_outer; + polygon([[ -2, 0 ], + [ p[1]/2, 0 ], + [ p[1]/2, p[2] ], + [ button_dy_outer/2, p[2] ], + [ button_dy_outer/2, pbo ], + [ button_dy_inner/2, pbo ], + [ button_dy_inner/2, p[2] ], + [ button_dy_centre/2, p[2] ], + [ button_dy_centre/2, pbc ], + [ -2, pbc ]]); +} + +module MainProfile(){ + p = phone + phone_slop; + difference(){ + for (m=[0,1]) mirror([m,0]) { + minkowski(){ + translate([ -wall[1], -wall_bot ]) + square([ wall[1]*2, wall_bot + wall[2] ]); + MainProfileInnerHalf(); + } + } + for (m=[0,1]) mirror([m,0]) { + MainProfileInnerHalf(); + } + } +} + +module BraceProfileInitial(){ + p = phone + phone_slop; + pbo = p[2] + button_dz_outer; + pbc = p[2] + button_dz_centre; + polygon([[ button_dy_outer/2 + 0.2, p[2] ], + [ button_dy_outer/2 + 0.2, pbo + wall[2] ], + [ button_dy_outer/2 , pbo + wall[2] ], + [ button_dy_outer/2 , p[2] ], + ]); +} + +module BraceProfileFinal(){ + p = phone + phone_slop; + pbo = p[2] + button_dz_outer; + pbc = p[2] + button_dz_centre; + polygon([[ -1, p[2] ], + [ -1, pbc + wall[2] ], + [ 0, pbc + wall[2] ], + [ 0, p[2] ] + ]); +} + +module Brace(){ + for (m=[0,1]) mirror([0,m,0]) { + hull(){ + for (e=[0,1]) { + translate([ nrbutton_brace_x + e * phone[1]/2, + 0, 0 ]){ + rotate([ 90,0,90 ]){ + linear_extrude(height= (e==0 ? wall[0] : 0.1)){ + hull(){ + BraceProfileInitial(); + if (e==0) BraceProfileFinal(); + } + } + } + } + } + } + } +} + +module BoxMain(){ + rotate([0,0,90]) rotate([90,0,0]) { + translate([0,0, expose]) + linear_extrude(height = phone[0] + wall[0] - expose, convexity=20) + MainProfile(); + translate([0,0, phone[0]]) + linear_extrude(height = wall[0], convexity=20) + hull() MainProfile(); + } +} + +module PlugKeeperProfileHalf(){ + p_max = plugkeeper_p_max; + p_min = plugkeeper_p_min; + d = plugkeeper_d_u * keeper_prong; + + translate([ plugkeeper_x_maxw, 0 ]) { + polygon([ p_min, + p_max, + p_max + d, + p_min + d ]); + } +} + +module PlugKeeperStalkProfile(){ + hull(){ + for (m=[0,1]) mirror([0,m,0]) PlugKeeperProfileHalf(); + translate([ plugkeeper_x_maxw + keeper_stalk_len, 0,0 ]) + square([ 0.1, keeper_stalk_basewidth ], center=true); + } +} + +module PlugKeeper(){ + for (m=[0,1]) mirror([0,m,0]) { + translate([0,0, -keeper_stalk_thick]) + linear_extrude(height=plug_h + keeper_stalk_thick) + PlugKeeperProfileHalf(); + + translate([0, 0, plug_h - plug_tooth_h]) + linear_extrude(height= plug_tooth_h) + translate(plugkeeper_d_u * -plug_tooth_dy) + PlugKeeperProfileHalf(); + + } +} + +module Box(){ + sidewall_cutout_z = phone[2] + phone_slop[2] + button_dz_outer; + + difference(){ + union(){ + BoxMain(); + Brace(); + } + + translate([ led[0], phone[1]/2 - led[1], 1 ]) + rotate([0,0, 360/8/2]) + cylinder(r = led_dia/2 / cos(360/8/2), h= phone[2]*2, $fn=8); + + for (ys=[-1,+1]) { + translate([ -0.1, ys * keeper_stalk_gap, -wall[2]*2]) + linear_extrude(height = wall[2]*3) + PlugKeeperStalkProfile(); + + translate([ phone[0] + wall[0], + ys * (cutout_between/2 + cutout_dia/2), + -10 ]) + cylinder( r= cutout_dia/2, h = 50, $fn = 20 ); + + translate([expose, ys*phone[1]/2, sidewall_cutout_z/2]) + rotate([90,0,0]) + translate([0,0,-3]) + cylinder( r= sidewall_cutout_z/2 - 0.1, h=6 , $fn=20 ); + } + } + + PlugKeeper(); + + translate([0,0, -keeper_stalk_thick]) + linear_extrude(height = keeper_stalk_thick) + PlugKeeperStalkProfile(); + + translate([ plugkeeper_x_maxw + keeper_stalk_len + + -keeper_stalk_base_reinforce_len/2, + -keeper_stalk_basewidth/2, + 0 ]) + mirror([0,0,1]) + cube([ keeper_stalk_base_reinforce_len, + keeper_stalk_basewidth, + keeper_stalk_base_reinforce_thick ]); +} + +module BoxPrint(){ + // This makes' Cura's support more optimal: specifically, + // it then doesn't seem to touch the back (bottom) wall + translate([0,0,phone[0]]) + rotate([0,90,0]) + Box(); +} + +//MainProfileInnerHalf(); +//MainProfile(); +//translate([0,0,1]) color("black") BraceProfileInitial(); +//translate([0,0,1]) color("black") BraceProfileFinal(); +//Brace(); +//Box(); +BoxPrint(); diff --git a/slic3r-config.ini b/slic3r-config.ini new file mode 100644 index 0000000..c496b91 --- /dev/null +++ b/slic3r-config.ini @@ -0,0 +1,98 @@ +# generated by Slic3r 0.9.7 on Fri Dec 18 00:34:03 2015 +acceleration = 0 +bed_size = 140,140 +bed_temperature = 60 +bottom_solid_layers = 2 +bridge_fan_speed = 100 +bridge_flow_ratio = 1 +bridge_speed = 60 +brim_width = 0 +complete_objects = 0 +cooling = 1 +disable_fan_first_layers = 1 +duplicate = 1 +duplicate_distance = 6 +duplicate_grid = 1,1 +end_gcode = M83 ; extruder relative\nG1 F1000 ;1000mm/min extrusion\nG1 E-10; retract\nG1 X0 Y0 F10000\nG28 X0 Y0\nM104 S0 ; turn off temperature\nM140 S0 ; turn off bed\nM84 ; disable motors +external_perimeter_speed = 100% +extra_perimeters = 1 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extruder_offset = 0x0 +extrusion_axis = E +extrusion_multiplier = 1.0 +extrusion_width = 0 +fan_always_on = 0 +fan_below_layer_time = 60 +filament_diameter = 1.77 +fill_angle = 45 +fill_density = 0.2 +fill_pattern = rectilinear +first_layer_bed_temperature = 60 +first_layer_extrusion_width = 200% +first_layer_height = 0.4 +first_layer_speed = 30% +first_layer_temperature = 205 +g0 = 0 +gap_fill_speed = 20 +gcode_arcs = 0 +gcode_comments = 0 +gcode_flavor = reprap +infill_acceleration = 50 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0 +infill_speed = 60 +layer_gcode = +layer_height = 0.4 +max_fan_speed = 100 +min_fan_speed = 35 +min_print_speed = 10 +min_skirt_length = 0 +notes = +nozzle_diameter = 0.50 +only_retract_when_crossing_perimeters = 1 +output_filename_format = [input_filename_base].gcode +perimeter_acceleration = 25 +perimeter_extruder = 1 +perimeter_extrusion_width = 100% +perimeter_speed = 30 +perimeters = 2 +post_process = +print_center = 70,70 +randomize_start = 1 +retract_before_travel = 2 +retract_length = 4.5 +retract_length_toolchange = 3 +retract_lift = 0.1 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 30 +rotate = 0 +scale = 1 +skirt_distance = 3 +skirt_height = 1 +skirts = 2 +slowdown_below_layer_time = 15 +small_perimeter_speed = 30 +solid_fill_pattern = rectilinear +solid_infill_below_area = 1 +solid_infill_every_layers = 0 +solid_infill_speed = 60 +start_gcode = G28 ; home all axes +support_material = 0 +support_material_angle = 0 +support_material_extruder = 1 +support_material_extrusion_width = 0 +support_material_pattern = rectilinear +support_material_spacing = 2.5 +support_material_speed = 60 +support_material_threshold = 45 +temperature = 205 +threads = 8 +top_solid_infill_speed = 50 +top_solid_layers = 2 +travel_speed = 130 +use_relative_e_distances = 0 +vibration_limit = 0 +z_offset = 0 diff --git a/smallfilamentclip.scad b/smallfilamentclip.scad new file mode 100644 index 0000000..724d4cf --- /dev/null +++ b/smallfilamentclip.scad @@ -0,0 +1,40 @@ +include +include + +rad=7.7; +smrad=2.0; +h=3.5; +w=2.5; +teethw=1.5; + +looprad=2.5; +loopw=w; + +fdia=1.77; +//fdia=3; + +d=0.01; + +module FilamentClip() { + linear_extrude(height=h) { + assign($fn=80) { + FlatArc(0,0, rad-w/2,rad+w/2, 80,361); + } + assign($fn=30) { + FlatArc(0,rad+looprad+w, looprad,looprad+loopw); + } + FlatArc(0, rad-smrad, smrad-w/2,smrad+w/2, -55,91); + FlatArc(rad-smrad, 0, smrad-w/2,smrad+w/2, 145,-1); + } + + for (mir=[0,1]) { + mirror([0,mir,0]) + mirror([1,0,0]) + rotate([0,0,-56]) + translate([rad+w*0.3+teethw*0.3+fdia/2 -0.6, -1.1, 0]) + rotate([0,0,95]) + FilamentTeeth(fdia=fdia); + } +} + +FilamentClip(); diff --git a/splitpin.scad b/splitpin.scad new file mode 100644 index 0000000..9631c95 --- /dev/null +++ b/splitpin.scad @@ -0,0 +1,81 @@ +// -*- C -*- + +include + +tau = 6.28318530718; +function deg2rad(deg) = deg/360 * tau; +function rad2deg(rad) = rad/tau * 360; + +module SplitPin(w=1.5, holeminrad=2.50, thick=3, deviationrad=1.5, + mainlen=15, handlerad=20, handlelen=12) { + spare = holeminrad*2 - deviationrad - w*2; + echo("splitpin spare",spare); + %translate([0,mainlen+handlelen,0]) cylinder(r=spare, h=thick); + %translate([0,mainlen,thick/2]) rotate([90,0,0]) + cylinder(r=holeminrad, h=thick); + + bent_dx = holeminrad; + unbent_dx = bent_dx + deviationrad; + + unbent_subang = atan(unbent_dx / mainlen); + unbent_rad = mainlen / deg2rad(unbent_subang); + + corner_x = unbent_rad * (1 - cos(unbent_subang)); + corner_y = unbent_rad * sin(unbent_subang); + + main_cx = unbent_rad; + +// translate([w*1.5, 0, 0]) { +// translate([corner_x, corner_y, 10]) %cube([10,10,10]); +// translate([bent_dx, 0, 10]) %cube([10,10,10]); +// translate([unbent_dx, 5, 10]) %cube([10,10,10]); +// } + + linear_extrude(height=thick) { + for (xmir=[0,1]) mirror([xmir,0,0]) + FlatArc(0,0, w*0.5, w*1.5, 270-1,360); + translate([w*1.5, 0, 0]) { + FlatArc($fa=1, main_cx,0, unbent_rad, unbent_rad+w, + 180-unbent_subang, 180); + translate([corner_x, corner_y]) rotate([0,0,-unbent_subang]) { + rotate([0,0,10]) + translate([w*0.2,0,0]) + translate([-(w + deviationrad), -0.1]) + square(size=[w + deviationrad, w+0.1]); + FlatArc(-deviationrad + handlerad, w, + handlerad, handlerad+w, + 180-rad2deg(handlelen/handlerad), 180+rad2deg(w/handlerad), + $fa=0.25, $fn=60); + } + } + mirror([1,0,0]) translate([w*1.5, 0, 0]) + FlatArc($fa=1, main_cx,0, unbent_rad, unbent_rad+w, + 180-(unbent_subang + rad2deg((handlelen+w)/unbent_rad)), 180); + } +} + +module SplitPinCavity(w=1.5, holeminrad=2.50, thick=3, deviationrad=1.5, + mainlen=15, slop=0.5, insertby = 5) { + smallgap2 = holeminrad; + biggap2 = smallgap2 + deviationrad + slop; + toegap2 = w*1.5 + slop; + toeend = -mainlen-insertby; + + translate([0,thick/2,0]) rotate([90,0,0]) { + linear_extrude(height = thick + slop*2) { + for (xmir=[0,1]) mirror([xmir,0]) { + polygon([[-0.1, 1], + [(smallgap2+biggap2)/2, 1], + [smallgap2, -insertby], + [biggap2, -insertby], + [toegap2, toeend-1], + [-0.1, toeend-1]]); + } + } + } +} + +SplitPin(); +translate([0,15+5,-10]) + rotate([-90,0,0]) + SplitPinCavity(); diff --git a/sprinkler-spike-receptacle.scad b/sprinkler-spike-receptacle.scad new file mode 100644 index 0000000..c1ac3b5 --- /dev/null +++ b/sprinkler-spike-receptacle.scad @@ -0,0 +1,200 @@ +// -*- C -*- + +main_height = 95; + +spike_web_thick = 2.52 + 0.75; + +spike_top_width = 21.04 + 1.5; + +spike_botpos_height = 9.5; +spike_botpos_width = 11.68 + 0.00; + +topwall_width = 1.5; + +mount_dist = 20; +mount_width = 10; +mount_height = 5; +mount_hole_dia = 4.5; +mount_head_dia = 7.5; +mount_hole_th = 2.5; + +strap_height = main_height * 0.5; + +strap_width = 5.5; +strap_thick = 2.5; +strap_around = 2.5; +strap_fixing_height = 4.0; +strap_fixing_slope = 1.0; + +// calculated + +main_width = spike_top_width + topwall_width*2; + +pos_web_thick = spike_web_thick + topwall_width*2; + +module NegativePlan(){ + x4z = + (spike_top_width - spike_botpos_width) / + (main_height - spike_botpos_height); + + x0 = (spike_botpos_width - x4z * spike_botpos_height)/2; + x1 = spike_top_width/2; + z1 = main_height; + + polygon([[ x0, -5], + [ x0, 0], + [ x1, z1], + [ x1, z1+5], + [-x1, z1+5], + [-x1, z1], + [-x0, 0], + [-x0, -5]]); +} + +module SomeMidRounding(sq_size, z_extra) { + translate([0,0,-z_extra]) + linear_extrude(height= main_height + z_extra*2) + rotate(45) + square( sq_size, center=true ); +} + +module PositiveMidRounding(){ + SomeMidRounding(pos_web_thick*2, 0); +} + +module NegativeMidRounding(){ + SomeMidRounding(spike_web_thick*2.5, 5); +} + +module PositivePlan(){ + w = main_width; + translate([ -w/2, 0 ]) + square([ w, main_height ]); +} + +module MultiplySolidifyPlan(th){ + for (r=[0,90]) { + rotate([0,0,r]) + rotate([90,0,0]) + translate([0,0,-th/2]) + linear_extrude(height=th) + children(0); + } +} + +module MultiplyForFixings(){ + for (r=[0:90:270]) + rotate([0,0,r]) + children(0); +} + +module FixingsPositive(){ + // mount + translate([ -1, + -mount_width/2, + 0 ]) + cube([ mount_dist + mount_width/2 + 1, + mount_width, + mount_height ]); + + // strap + for (m=[0,1]) mirror([0,m,0]) { + translate([main_width/2, 0, strap_height]) { + hull(){ + translate([ -strap_around, + -pos_web_thick/2, + -(strap_thick + strap_around) / strap_fixing_slope ]) + cube([ strap_around, + pos_web_thick/2 - strap_width/2, + 0.5 ]); + translate([ -strap_around, + -(strap_around + strap_width/2), + 0 ]) + cube([ strap_around*2 + strap_thick, + strap_around, + strap_fixing_height ]); + } + mirror([0,1,0]) + translate([ strap_thick, + -strap_width/2, + 0 ]) + cube([ strap_around, + strap_around + strap_width, + strap_fixing_height ]); + } + } +} + +module FixingsNegative(){ + // mount hole + translate([ mount_dist, 0,0 ]) { + translate([0,0, -1]) + cylinder(r= mount_hole_dia/2, h= 20, $fn=20); + translate([0,0, mount_hole_th]) + cylinder(r = mount_head_dia/2, h=20, $fn=20); + } +} + +module Main(){ + difference(){ + union(){ + MultiplySolidifyPlan(pos_web_thick) PositivePlan(); + PositiveMidRounding(); + MultiplyForFixings() FixingsPositive(); + } + MultiplySolidifyPlan(spike_web_thick) NegativePlan(); + NegativeMidRounding(); + MultiplyForFixings() FixingsNegative(); + } +} + +module PlanTest(){ + linear_extrude(height=2.0){ + difference(){ + PositivePlan(); + NegativePlan(); + } + difference(){ + circle(r = spike_botpos_width/2 + 5); + circle(r = spike_botpos_width/2); + translate([-50, 0]) square([100,50]); + } + } + linear_extrude(height=4.0){ + difference(){ + translate([ -main_width/2, 0 ]) square([ main_width, 2 ]); + NegativePlan(); + } + } +} + +module MainFitTest(){ + for (top = [0,1]) { + translate([ top * (mount_dist*2 + mount_width), 0,0 ]){ + intersection(){ + translate([0, 0, (-main_height + 0.5) * top]) + Main(); + translate([-50,-50,0]) + cube([100,100, spike_botpos_height + 1.5]); + } + } + } +} + +module Tests(){ + translate([-mount_dist*3, 0,0]) + PlanTest(); + MainFitTest(); +} + +module StrapFixingTest(){ + intersection(){ + Main(); + translate([ -10, -10, 40 ]) + cube([ 20, 40, 15 ]); + } +} + +//Tests(); +//StrapFixingTest(); +Main(); diff --git a/startech-dell-usb-cable-retainer.scad b/startech-dell-usb-cable-retainer.scad new file mode 100644 index 0000000..122f76c --- /dev/null +++ b/startech-dell-usb-cable-retainer.scad @@ -0,0 +1,87 @@ +// -*- C -*- + +include + +body_depth = 40.15 + 4; +body_height = 16.78 + 0.50; + +back_round_depth = 2.0; +back_round_rad = 8.0; +back_above_height = 5.3; +back_thick = 3.0; + +conn_thick = 6.42 + 2.25; +wire_thick = 6.00 + 0.75; +total_depth = 63.82 - 1.0; + +body_front_overlap = 3; + +prong_higher = 1.5; +prong_depth = 5.0; +prong_width = 2.0; + +base_thick = 4; + +$fa= 3; +$fs= 0.3; + +// calculated + +epp0 = [ 0, -body_height/2 ]; +epp1 = [ 0, -conn_thick/2 ]; +epp2 = epp1 + [ -body_front_overlap, 0 ]; +epp3 = [ +body_depth -total_depth, epp2[1] ]; +epp4 = [ epp3[0], +conn_thick/2 +prong_higher ]; +epp4a = epp4 + prong_higher * [0,-1]; +epp4b = epp4 + prong_higher * [1,0]; +epp5 = epp4 + [ -prong_depth, 0 ]; +epp6 = [ epp5[0], epp0[1] -base_thick ]; +epp7 = [ epp2[0], epp6[1] ]; +epp12 = [ +body_depth +back_round_rad, 0 ]; +epp11 = [ +body_depth +back_round_depth, epp0[1] ]; +epp10 = [ epp11[0], +back_above_height ]; +epp9 = epp10 + [ +back_thick, 0 ]; +epp8 = [ epp9[0], epp7[1] ]; + +y1 = wire_thick/2; +y2 = y1 + prong_width; + +module MainElevation(){ + polygon([epp0, + epp1, + epp3, + epp4a, + epp4b, + epp5, + epp6, + epp8, + epp9, + epp10, + epp11 + ]); + intersection(){ + translate(epp12) circle(r= back_round_rad); + rectfromto(epp8, + epp10 + [-back_round_rad, 0]); + } +} + +module Retainer(){ + difference(){ + linextr_y_xz( -y2, +y2 ) { + MainElevation(); + } + linextr_y_xz( -y1, +y1 ) { + rectfromto( epp7 + [+1, -1], + epp5 + [-1, +1] ); + } + } +} + +module RetainerPrint(){ + rotate([0,0,180]) Retainer(); +} + +//MainElevation(); + +RetainerPrint(); diff --git a/steamer-handle-clip.scad b/steamer-handle-clip.scad new file mode 100644 index 0000000..5001861 --- /dev/null +++ b/steamer-handle-clip.scad @@ -0,0 +1,50 @@ +// -*- C -*- + +include +include + +width = 30 - 2; +cup = 2.5; +jaw = 32.36 - 2.00 - 2.00 - 3.00; +th = 3.0; +l = 15; + +a = cup; +b = width/2; +alpha = atan2(a, b); +c = vectorlen2d([a, b]); +r = a / (1 - cos(2*alpha)); +C = [0, a-r]; + +$fa = 1; + +module HalfBaseline() { + intersection(){ + translate(C + [0, jaw/2]) + circle(r=r); + rectfromto([ -width/2, -1, ], + [ width/2, jaw ]); + } +} + +module Baseline(){ + HalfBaseline(); + mirror([0,1]) HalfBaseline(); +} + +module Plan(){ + difference(){ + offset(delta=th) Baseline(); + Baseline(); + rectfromto([-width, -jaw/2], + [0, jaw/2]); + } +} + +module Thing(){ + linextr(0,l) Plan(); +} + +//HalfPlan(); +//Plan(); +Thing(); diff --git a/stringing-test.scad b/stringing-test.scad new file mode 100644 index 0000000..a75704d --- /dev/null +++ b/stringing-test.scad @@ -0,0 +1,15 @@ +// -*- C -*- + +height = 15; +$fn= 20; + +cylinder(r=7, h=height); + +for (r= [0:4]) { + rotate(r * 72) { + d = 10 * pow(1.5, r); + cube([d, 3, 2]); + translate([d, 0,0]) + cube([7, 7, height]); + } +} diff --git a/summit-lantern-hook.scad b/summit-lantern-hook.scad new file mode 100644 index 0000000..e91573a --- /dev/null +++ b/summit-lantern-hook.scad @@ -0,0 +1,64 @@ +// -*- C -*- + +include + +height = 60; +curl = 10; +width = 85; +sides_depth = 50; +th = 6; +th2 = 4; + +$fa = 3; +$fs = 0.3; + +// calculated + +upper_r = th/2; +upper_ctr_maj_r = curl/2 + upper_r; + +zmin = curl/2 + th; + +module UpperPlan(){ + circle(r = upper_r); +} + +module EndCurl(){ + rotate([90,0,0]) + rotate_extrude(angle=180) + translate([upper_ctr_maj_r, 0]) + UpperPlan(); + translate([-upper_ctr_maj_r, 0,0]) + sphere(r= upper_r); +} + +module Upper(){ + translate([upper_ctr_maj_r, 0, 0]) + linextr(-0.1, height + 0.1) + UpperPlan(); + translate([0, 0, height]) + EndCurl(); +} + +module Lower(){ + rotate([180,0,0]) + EndCurl(); + linextr(-zmin, -zmin + th) { + square(center=true, [th2, width]); + for (m=[0,1]) + mirror([0,m]) + hull() + { + for (x= sides_depth/2 * [-1,+1]) + translate([ x, width/2 - th2/2 ]) + circle(r= th2/2); + } + } +} + +module Hook(){ + Upper(); + Lower(); +} + +Hook(); diff --git a/svg-prep-dxf b/svg-prep-dxf new file mode 100755 index 0000000..a5ecc38 --- /dev/null +++ b/svg-prep-dxf @@ -0,0 +1,178 @@ +#!/bin/bash +us=svg-prep-dxf +usage () { cat <&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 < +include + +// https://en.wikipedia.org/wiki/ISO_metric_screw_thread + +// M6 +thread_nom = 6; +thread_pitch = 1.00; +thread_act = thread_nom - 0.300; +head_size = 10; + +thread_len = 12.5; +base_th = 1.5; + +$test = false; + +// calculated + +base_dia = head_size / cos(30); + +module MachineScrew(){ + translate([0, 0, -0.1]) + render() + metric_thread(diameter=thread_act, pitch=thread_pitch, + leadin=1, internal=false, + test=$test, length=thread_len + 0.1); + linextr(-base_th, 0) + circle(r= base_dia/2, $fn=6); +} + +MachineScrew(); diff --git a/thread-internal-test.scad b/thread-internal-test.scad new file mode 100644 index 0000000..cab40b6 --- /dev/null +++ b/thread-internal-test.scad @@ -0,0 +1,46 @@ +// -*- C -*- + +include +include + +// https://en.wikipedia.org/wiki/ISO_metric_screw_thread + +// M6 +thread_nom = 4; +thread_pitch = 0.70; +thread_act = thread_nom + 0.375; +head_size = 10; + +thread_len = 12.5; +base_th = 1.5; +base_sz = [40, head_size]; + +$test = false; + +// calculated + +base_dia = head_size / cos(30); + +module ScrewThread(){ + translate([0, 0, -0.1]) + render() + metric_thread(diameter=thread_act, pitch=thread_pitch, + leadin=1, internal=true, + test=$test, length=thread_len + 0.1); +} + +module TestThread(){ + difference(){ + union(){ + linextr(-base_th, 0) + square(center=true, base_sz); + + linextr(-base_th, thread_len - 0.1) + circle(r= base_dia/2, $fn=6); + } + + ScrewThread(); + } +} + +TestThread(); diff --git a/threads.scad b/threads.scad deleted file mode 100644 index b2eee23..0000000 --- a/threads.scad +++ /dev/null @@ -1,407 +0,0 @@ -/* - * ISO-standard metric threads, following this specification: - * http://en.wikipedia.org/wiki/ISO_metric_screw_thread - * - * Copyright 2020 Dan Kirshner - dan_kirshner@yahoo.com - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 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. - * - * See . - * - * Version 2.5. 2020-04-11 Leadin option works for internal threads. - * Version 2.4. 2019-07-14 Add test option - do not render threads. - * Version 2.3. 2017-08-31 Default for leadin: 0 (best for internal threads). - * Version 2.2. 2017-01-01 Correction for angle; leadfac option. (Thanks to - * Andrew Allen .) - * Version 2.1. 2016-12-04 Chamfer bottom end (low-z); leadin option. - * Version 2.0. 2016-11-05 Backwards compatibility (earlier OpenSCAD) fixes. - * Version 1.9. 2016-07-03 Option: tapered. - * Version 1.8. 2016-01-08 Option: (non-standard) angle. - * Version 1.7. 2015-11-28 Larger x-increment - for small-diameters. - * Version 1.6. 2015-09-01 Options: square threads, rectangular threads. - * Version 1.5. 2015-06-12 Options: thread_size, groove. - * Version 1.4. 2014-10-17 Use "faces" instead of "triangles" for polyhedron - * Version 1.3. 2013-12-01 Correct loop over turns -- don't have early cut-off - * Version 1.2. 2012-09-09 Use discrete polyhedra rather than linear_extrude () - * Version 1.1. 2012-09-07 Corrected to right-hand threads! - */ - -// Examples. -// -// Standard M8 x 1. -// metric_thread (diameter=8, pitch=1, length=4); - -// Square thread. -// metric_thread (diameter=8, pitch=1, length=4, square=true); - -// Non-standard: long pitch, same thread size. -//metric_thread (diameter=8, pitch=4, length=4, thread_size=1, groove=true); - -// Non-standard: 20 mm diameter, long pitch, square "trough" width 3 mm, -// depth 1 mm. -//metric_thread (diameter=20, pitch=8, length=16, square=true, thread_size=6, -// groove=true, rectangle=0.333); - -// English: 1/4 x 20. -//english_thread (diameter=1/4, threads_per_inch=20, length=1); - -// Tapered. Example -- pipe size 3/4" -- per: -// http://www.engineeringtoolbox.com/npt-national-pipe-taper-threads-d_750.html -// english_thread (diameter=1.05, threads_per_inch=14, length=3/4, taper=1/16); - -// Thread for mounting on Rohloff hub. -//difference () { -// cylinder (r=20, h=10, $fn=100); -// -// metric_thread (diameter=34, pitch=1, length=10, internal=true, n_starts=6); -//} - - -// ---------------------------------------------------------------------------- -function segments (diameter) = min (50, max (ceil (diameter*6), 25)); - - -// ---------------------------------------------------------------------------- -// diameter - outside diameter of threads in mm. Default: 8. -// pitch - thread axial "travel" per turn in mm. Default: 1. -// length - overall axial length of thread in mm. Default: 1. -// internal - true = clearances for internal thread (e.g., a nut). -// false = clearances for external thread (e.g., a bolt). -// (Internal threads should be "cut out" from a solid using -// difference ()). Default: false. -// n_starts - Number of thread starts (e.g., DNA, a "double helix," has -// n_starts=2). See wikipedia Screw_thread. Default: 1. -// thread_size - (non-standard) axial width of a single thread "V" - independent -// of pitch. Default: same as pitch. -// groove - (non-standard) true = subtract inverted "V" from cylinder -// (rather thanadd protruding "V" to cylinder). Default: false. -// square - true = square threads (per -// https://en.wikipedia.org/wiki/Square_thread_form). Default: -// false. -// rectangle - (non-standard) "Rectangular" thread - ratio depth/(axial) width -// Default: 0 (standard "v" thread). -// angle - (non-standard) angle (deg) of thread side from perpendicular to -// axis (default = standard = 30 degrees). -// taper - diameter change per length (National Pipe Thread/ANSI B1.20.1 -// is 1" diameter per 16" length). Taper decreases from 'diameter' -// as z increases. Default: 0 (no taper). -// leadin - 0 (default): no chamfer; 1: chamfer (45 degree) at max-z end; -// 2: chamfer at both ends, 3: chamfer at z=0 end. -// leadfac - scale of leadin chamfer length (default: 1.0 = 1/2 thread). -// test - true = do not render threads (just draw "blank" cylinder). -// Default: false (draw threads). -module metric_thread (diameter=8, pitch=1, length=1, internal=false, n_starts=1, - thread_size=-1, groove=false, square=false, rectangle=0, - angle=30, taper=0, leadin=0, leadfac=1.0, test=false) -{ - // thread_size: size of thread "V" different than travel per turn (pitch). - // Default: same as pitch. - local_thread_size = thread_size == -1 ? pitch : thread_size; - local_rectangle = rectangle ? rectangle : 1; - - n_segments = segments (diameter); - h = (test && ! internal) ? 0 : (square || rectangle) ? local_thread_size*local_rectangle/2 : local_thread_size / (2 * tan(angle)); - - h_fac1 = (square || rectangle) ? 0.90 : 0.625; - - // External thread includes additional relief. - h_fac2 = (square || rectangle) ? 0.95 : 5.3/8; - - tapered_diameter = diameter - length*taper; - - difference () { - union () { - if (! groove) { - if (! test) { - metric_thread_turns (diameter, pitch, length, internal, n_starts, - local_thread_size, groove, square, rectangle, angle, - taper); - } - } - - difference () { - - // Solid center, including Dmin truncation. - if (groove) { - cylinder (r1=diameter/2, r2=tapered_diameter/2, - h=length, $fn=n_segments); - } else if (internal) { - cylinder (r1=diameter/2 - h*h_fac1, r2=tapered_diameter/2 - h*h_fac1, - h=length, $fn=n_segments); - } else { - - // External thread. - cylinder (r1=diameter/2 - h*h_fac2, r2=tapered_diameter/2 - h*h_fac2, - h=length, $fn=n_segments); - } - - if (groove) { - if (! test) { - metric_thread_turns (diameter, pitch, length, internal, n_starts, - local_thread_size, groove, square, rectangle, - angle, taper); - } - } - } - - // Internal thread lead-in: take away from external solid. - if (internal) { - - // "Negative chamfer" z=0 end if leadin is 2 or 3. - if (leadin == 2 || leadin == 3) { - cylinder (r1=diameter/2, r2=diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac, - $fn=n_segments); - } - - // "Negative chamfer" z-max end if leadin is 1 or 2. - if (leadin == 1 || leadin == 2) { - translate ([0, 0, length + 0.05 - h*h_fac1*leadfac]) { - cylinder (r1=tapered_diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac, - r2=tapered_diameter/2, - $fn=n_segments); - } - } - } - } - - if (! internal) { - - // Chamfer z=0 end if leadin is 2 or 3. - if (leadin == 2 || leadin == 3) { - difference () { - cylinder (r=diameter/2 + 1, h=h*h_fac1*leadfac, $fn=n_segments); - - cylinder (r2=diameter/2, r1=diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac, - $fn=n_segments); - } - } - - // Chamfer z-max end if leadin is 1 or 2. - if (leadin == 1 || leadin == 2) { - translate ([0, 0, length + 0.05 - h*h_fac1*leadfac]) { - difference () { - cylinder (r=diameter/2 + 1, h=h*h_fac1*leadfac, $fn=n_segments); - - cylinder (r1=tapered_diameter/2, r2=tapered_diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac, - $fn=n_segments); - } - } - } - } - } -} - - -// ---------------------------------------------------------------------------- -// Input units in inches. -// Note: units of measure in drawing are mm! -module english_thread (diameter=0.25, threads_per_inch=20, length=1, - internal=false, n_starts=1, thread_size=-1, groove=false, - square=false, rectangle=0, angle=30, taper=0, leadin=0, - leadfac=1.0, test=false) -{ - // Convert to mm. - mm_diameter = diameter*25.4; - mm_pitch = (1.0/threads_per_inch)*25.4; - mm_length = length*25.4; - - echo (str ("mm_diameter: ", mm_diameter)); - echo (str ("mm_pitch: ", mm_pitch)); - echo (str ("mm_length: ", mm_length)); - metric_thread (mm_diameter, mm_pitch, mm_length, internal, n_starts, - thread_size, groove, square, rectangle, angle, taper, leadin, - leadfac, test); -} - -// ---------------------------------------------------------------------------- -module metric_thread_turns (diameter, pitch, length, internal, n_starts, - thread_size, groove, square, rectangle, angle, - taper) -{ - // Number of turns needed. - n_turns = floor (length/pitch); - - intersection () { - - // Start one below z = 0. Gives an extra turn at each end. - for (i=[-1*n_starts : n_turns+1]) { - translate ([0, 0, i*pitch]) { - metric_thread_turn (diameter, pitch, internal, n_starts, - thread_size, groove, square, rectangle, angle, - taper, i*pitch); - } - } - - // Cut to length. - translate ([0, 0, length/2]) { - cube ([diameter*3, diameter*3, length], center=true); - } - } -} - - -// ---------------------------------------------------------------------------- -module metric_thread_turn (diameter, pitch, internal, n_starts, thread_size, - groove, square, rectangle, angle, taper, z) -{ - n_segments = segments (diameter); - fraction_circle = 1.0/n_segments; - for (i=[0 : n_segments-1]) { - rotate ([0, 0, i*360*fraction_circle]) { - translate ([0, 0, i*n_starts*pitch*fraction_circle]) { - //current_diameter = diameter - taper*(z + i*n_starts*pitch*fraction_circle); - thread_polyhedron ((diameter - taper*(z + i*n_starts*pitch*fraction_circle))/2, - pitch, internal, n_starts, thread_size, groove, - square, rectangle, angle); - } - } - } -} - - -// ---------------------------------------------------------------------------- -module thread_polyhedron (radius, pitch, internal, n_starts, thread_size, - groove, square, rectangle, angle) -{ - n_segments = segments (radius*2); - fraction_circle = 1.0/n_segments; - - local_rectangle = rectangle ? rectangle : 1; - - h = (square || rectangle) ? thread_size*local_rectangle/2 : thread_size / (2 * tan(angle)); - outer_r = radius + (internal ? h/20 : 0); // Adds internal relief. - //echo (str ("outer_r: ", outer_r)); - - // A little extra on square thread -- make sure overlaps cylinder. - h_fac1 = (square || rectangle) ? 1.1 : 0.875; - inner_r = radius - h*h_fac1; // Does NOT do Dmin_truncation - do later with - // cylinder. - - translate_y = groove ? outer_r + inner_r : 0; - reflect_x = groove ? 1 : 0; - - // Make these just slightly bigger (keep in proportion) so polyhedra will - // overlap. - x_incr_outer = (! groove ? outer_r : inner_r) * fraction_circle * 2 * PI * 1.02; - x_incr_inner = (! groove ? inner_r : outer_r) * fraction_circle * 2 * PI * 1.02; - z_incr = n_starts * pitch * fraction_circle * 1.005; - - /* - (angles x0 and x3 inner are actually 60 deg) - - /\ (x2_inner, z2_inner) [2] - / \ - (x3_inner, z3_inner) / \ - [3] \ \ - |\ \ (x2_outer, z2_outer) [6] - | \ / - | \ /| - z |[7]\/ / (x1_outer, z1_outer) [5] - | | | / - | x | |/ - | / | / (x0_outer, z0_outer) [4] - | / | / (behind: (x1_inner, z1_inner) [1] - |/ | / - y________| |/ - (r) / (x0_inner, z0_inner) [0] - - */ - - x1_outer = outer_r * fraction_circle * 2 * PI; - - z0_outer = (outer_r - inner_r) * tan(angle); - //echo (str ("z0_outer: ", z0_outer)); - - //polygon ([[inner_r, 0], [outer_r, z0_outer], - // [outer_r, 0.5*pitch], [inner_r, 0.5*pitch]]); - z1_outer = z0_outer + z_incr; - - // Give internal square threads some clearance in the z direction, too. - bottom = internal ? 0.235 : 0.25; - top = internal ? 0.765 : 0.75; - - translate ([0, translate_y, 0]) { - mirror ([reflect_x, 0, 0]) { - - if (square || rectangle) { - - // Rule for face ordering: look at polyhedron from outside: points must - // be in clockwise order. - polyhedron ( - points = [ - [-x_incr_inner/2, -inner_r, bottom*thread_size], // [0] - [x_incr_inner/2, -inner_r, bottom*thread_size + z_incr], // [1] - [x_incr_inner/2, -inner_r, top*thread_size + z_incr], // [2] - [-x_incr_inner/2, -inner_r, top*thread_size], // [3] - - [-x_incr_outer/2, -outer_r, bottom*thread_size], // [4] - [x_incr_outer/2, -outer_r, bottom*thread_size + z_incr], // [5] - [x_incr_outer/2, -outer_r, top*thread_size + z_incr], // [6] - [-x_incr_outer/2, -outer_r, top*thread_size] // [7] - ], - - faces = [ - [0, 3, 7, 4], // This-side trapezoid - - [1, 5, 6, 2], // Back-side trapezoid - - [0, 1, 2, 3], // Inner rectangle - - [4, 7, 6, 5], // Outer rectangle - - // These are not planar, so do with separate triangles. - [7, 2, 6], // Upper rectangle, bottom - [7, 3, 2], // Upper rectangle, top - - [0, 5, 1], // Lower rectangle, bottom - [0, 4, 5] // Lower rectangle, top - ] - ); - } else { - - // Rule for face ordering: look at polyhedron from outside: points must - // be in clockwise order. - polyhedron ( - points = [ - [-x_incr_inner/2, -inner_r, 0], // [0] - [x_incr_inner/2, -inner_r, z_incr], // [1] - [x_incr_inner/2, -inner_r, thread_size + z_incr], // [2] - [-x_incr_inner/2, -inner_r, thread_size], // [3] - - [-x_incr_outer/2, -outer_r, z0_outer], // [4] - [x_incr_outer/2, -outer_r, z0_outer + z_incr], // [5] - [x_incr_outer/2, -outer_r, thread_size - z0_outer + z_incr], // [6] - [-x_incr_outer/2, -outer_r, thread_size - z0_outer] // [7] - ], - - faces = [ - [0, 3, 7, 4], // This-side trapezoid - - [1, 5, 6, 2], // Back-side trapezoid - - [0, 1, 2, 3], // Inner rectangle - - [4, 7, 6, 5], // Outer rectangle - - // These are not planar, so do with separate triangles. - [7, 2, 6], // Upper rectangle, bottom - [7, 3, 2], // Upper rectangle, top - - [0, 5, 1], // Lower rectangle, bottom - [0, 4, 5] // Lower rectangle, top - ] - ); - } - } - } -} - - - diff --git a/threads.scad b/threads.scad new file mode 120000 index 0000000..de35975 --- /dev/null +++ b/threads.scad @@ -0,0 +1 @@ +diziet-utils/threads.scad \ No newline at end of file diff --git a/topeak-mtx-tortec-expeditionrack-adapter.scad b/topeak-mtx-tortec-expeditionrack-adapter.scad new file mode 100644 index 0000000..c751f78 --- /dev/null +++ b/topeak-mtx-tortec-expeditionrack-adapter.scad @@ -0,0 +1,502 @@ +// -*- C -*- + +// brk_*: "bracket", the Topeak MTX bracket +// rack_*: the Tortec rack +// adapt_*: the adapter, ie this file + +include + +// strength factor, set to 1 for real prints +//$strf = 0.33; +$strf = 1; + +brk_recess_actual = 5.20; + +rack_rail_dia = 10.40 + 0.30; +rack_width_inner = 115.86 - 1.0; // between insides of rails + +rear_elevation_nominal = 10.04; +// ^ top of rack to bottom of bracket, at rack cross rail (fam) +rear_to_front_distance = 230; // rack cross rail (fam) to very front end +rear_to_cross_rail = 43.05; // bolt centre to rail centre, rail to rear +rear_bolt_to_front_bolt = 155.4; +front_elevation_nominal = 0; // this parameter adjusts rear too somehow? + +cross_rail_distance = 232.09; + +general_gap_y = 1.0; +support_bridge_gap_z = 1.0; + +strap_w = 8.0 + 1.0; +strap_th = 2.5; +strap_barrel_dia = 14; +strap_guide_sz = 1; + +brk_block_xw = 68.5; +brk_block_z = 14.55 - 0.00; + +brk_bolt_dia = 5.0 + 0.5; +brk_nearbolt_recess_dia = 8.86 + 1.5; +brk_nearbolt_recess_depth = 1.09 + 0.25; + +bolt_nut_around = 5; +bolt_nut_around_y_extra = 3; + +brk_bolt_eff_len = 11.78; // inside of recess, to end of bolt +brk_bolt_len_slop = 0.5; +brk_bolt_nut_th = 3.89; +brk_bolt_nut_across_flats = 7.86 + 0.50; + +brk_overall_w = 90.07; + +fit_slope_len = 5; + +// "foreaftmaint" aka "fam" is the hook-like part that stops +// the adapter sliding forwards/backwards along the rails +foreaftmaint_r_slop = 0.0; +foreaftmaint_y_slop = -0.25; +foreaftmaint_top_block_zs = [34.0, 39.0]; // rearwards from bolt hole + +main_sz_y = $strf * 12; +grasp_sz = $strf * 6; +grasp_thin_sz = $strf * 0.5; +beside_strap_sz = $strf * 8; +main_sz_core_z = $strf * 12; + +// "length" in for-aft direction of interaction with rack rail +min_on_rail_sz_z = $strf * 18; + +// when printer produces support +support_around = 1.7; // how far does the support extend around (in XY) +support_remnant = 0.75; // how much frass remains attached (Z height) + +$fa=10; +$fs=1; + +// calculated + +bolt_z = -brk_block_z/2; + +front_to_rear_elevation_change = + rear_elevation_nominal - front_elevation_nominal; + +main_sz_rhs_z = max(min_on_rail_sz_z, beside_strap_sz*2 + strap_w); +main_sz_lhs_z = min_on_rail_sz_z; + +main_sz_x_fam = main_sz_y; + +brk_bottom_y = -brk_recess_actual; +adapt_main_top_y = brk_bottom_y - general_gap_y; + +// on LHS, so -ve +rack_rail_x = -(rack_width_inner/2 + rack_rail_dia/2); +rack_rail_outer_x = -(rack_width_inner/2 + rack_rail_dia); + +grasp_large_r = (rack_rail_dia + grasp_sz)/2; +grasp_small_r = (rack_rail_dia + grasp_thin_sz)/2; +grasp_large_x = rack_rail_outer_x + grasp_large_r; +grasp_small_x = rack_rail_outer_x + grasp_small_r; + +block_x = grasp_large_x + grasp_large_r; +block_y_min = adapt_main_top_y - main_sz_y; + +strap_barrel_x = rack_width_inner/2 + strap_barrel_dia/2; + +rack_shear_ratio = - front_to_rear_elevation_change / rear_to_front_distance; + +front_to_cross_rail = + cross_rail_distance * sqrt(1 - rack_shear_ratio * rack_shear_ratio) + - rear_bolt_to_front_bolt + - rear_to_cross_rail + - sqrt( pow( cross_rail_distance * rack_shear_ratio, 2 ) + - pow( front_to_rear_elevation_change, 2 ) ) + ; + +brk_bolt_nut_top_y = -brk_nearbolt_recess_depth + - brk_bolt_eff_len + brk_bolt_nut_th + brk_bolt_len_slop; + +brk_bolt_nut_r = brk_bolt_nut_across_flats/2 / cos(360/12); + +function elevation_of_bolt_for(z) = rear_elevation_nominal + + front_elevation_nominal + + (z - brk_block_z/2) * rack_shear_ratio; + +function rack_rail_y_of_elevation(elevation_nominal) = + brk_bottom_y - elevation_nominal - general_gap_y - rack_rail_dia/2; + +echo(rack_shear_ratio); + +module GraspElevation(){ + hull(){ + translate([ grasp_large_x, adapt_main_top_y - grasp_large_r ]) + circle(grasp_large_r); + + translate([ grasp_small_x, $rack_rail_y - rack_rail_dia/2 ]) + circle(grasp_small_r); + + translate([ rack_rail_x + grasp_large_r/2, + $rack_rail_y - rack_rail_dia/2 ]) + circle(grasp_small_r); + + translate([ grasp_large_x, $rack_rail_y + rack_rail_dia/2 ]) + circle(grasp_large_r); + + translate([ grasp_large_x + grasp_large_r/2, + $rack_rail_y + rack_rail_dia/2 ]) + circle(grasp_large_r); + } +} + +module BlockElevation(){ + hull(){ + rectfromto([ +block_x, adapt_main_top_y ], + [ -block_x, block_y_min ]); + rectfromto([ -grasp_large_x, adapt_main_top_y ], + [ +grasp_large_x, adapt_main_top_y - 0.1 ]); + } + hull(){ + rectfromto([ +block_x, adapt_main_top_y ], + [ -block_x, block_y_min ]); + rectfromto([ grasp_large_x, block_y_min ], + [ 0, block_y_min + 0.1 ]); + } +} + +module MainExtrude(z){ + linextr(0, z) + children(); +} +module RackShear(){ + s = rack_shear_ratio * $reverse_sign; + multmatrix([ [ 1, 0, 0, 0 ], + [ 0, 1, s , 0 ], + [ 0, 0, 1, 0 ], + [ 0, 0, 0, 1 ] ]) + children(); +} + +module GraspFixingElevation(){ + intersection(){ + union(){ + hull(){ + mirror([1,0]) { + GraspElevation(); + } + translate([ -block_x, block_y_min ] + [0,0.1]*1 ) + circle(0.1); + } + translate([ strap_barrel_x, $strap_barrel_y ]) + circle(strap_barrel_dia/2 + strap_guide_sz); + } + union(){ + rectfromto([0, $rack_rail_y], + [rack_width_inner, 50]); + intersection(){ + translate([ rack_rail_x, $rack_rail_y ]) + circle(r = rack_width_inner/2 - rack_rail_x); + polygon([ [ -block_x-0.1, 0 ], + [ rack_width_inner/2, 0 ], + $rail_fixing_fit_corner, + $rail_fixing_fit_corner + [-1,-1] * fit_slope_len, + [ -grasp_large_x - grasp_large_r*2, block_y_min ], + [ -block_x-0.1 -2, block_y_min +2 ]]); + } + } + } +} + +module StrapBarrelElevation(){ + translate([ strap_barrel_x, $strap_barrel_y ]) + circle(strap_barrel_dia/2); +} + +// Bracket support block, goes up inside bracket +// Z origin is bolt hole +module BrkBlock(){ + difference(){ + linextr( -brk_block_z/2, + +brk_block_z/2 ) { + rectfromto([ -brk_block_xw/2, adapt_main_top_y - 0.1 ], + [ +brk_block_xw/2, 0 ]); + } + linextr_y_xz( -50, 10 ) { + translate([ 0, brk_block_z + bolt_z ]) + square(center=true, + [ main_sz_x_fam + support_around*2, + support_remnant *2 ]); + } + } +} + +// Z origin is bolt hole +module BoltHole(){ + linextr_y_xz( -100, 10 ) + circle(brk_bolt_dia/2); + + linextr_y_xz( -brk_nearbolt_recess_depth, 10) + circle(brk_nearbolt_recess_dia/2); + + linextr_y_xz( -100, brk_bolt_nut_top_y ) { + hull() + for (dz = [0, support_bridge_gap_z]) + translate([0, dz]) + circle( r= brk_bolt_nut_r, $fn = 6 ); + } +} + +module IfFam(){ + if ($foreaftmaint_dz) { + children(); + } +} + +module FamLinextr(){ + IfFam() + linextr_x_yz(-main_sz_x_fam/2, +main_sz_x_fam/2) + rotate(-90) + children(); +} + +module FamGraspElevation(){ + difference(){ + hull(){ + ybot = $rack_rail_y - rack_rail_dia/2 + grasp_large_r + - fit_slope_len * 0.5; + for (y = [ + ybot, + adapt_main_top_y - grasp_large_r + ]) + for (dx= [/*-1,*/ +1] * rack_rail_dia/2) + translate([ -$foreaftmaint_rail_z + dx, y ]) + circle(r= grasp_large_r); + } + if ($foreaftmaint_cutoff) { + translate([ -$foreaftmaint_rail_z, 0 ]) + rectfromto([-100, -100], + [0, 100]); + } + } +} + +module FamStemElevation(){ + hull(){ + rectfromto([ -$foreaftmaint_rail_z + , adapt_main_top_y ], + [ 0, block_y_min]); + translate([ + -$foreaftmaint_rail_z, + $rack_rail_y + + rack_shear_ratio * $foreaftmaint_rail_z * $reverse_sign, + ]) + square([0.1, rack_rail_dia * 0.5], center=true); + } +} + +module Principal(){ + // calculated + $rack_rail_y = rack_rail_y_of_elevation($elevation_nominal); + + $strap_barrel_y = $rack_rail_y + rack_rail_dia/2 + strap_barrel_dia/2; + + $rail_fixing_fit_corner = [ + rack_width_inner/2, + $rack_rail_y - rack_rail_dia/2 + ]; + + $foreaftmaint_rail_z = brk_block_z/2 + $foreaftmaint_dz - foreaftmaint_y_slop; + + translate([0,0,brk_block_z/2]) + mirror([0,0, $reverse_sign > 0 ? 0 : 1]) + translate([0,0,-brk_block_z/2]) + difference(){ + union(){ + MainExtrude(main_sz_lhs_z){ + GraspElevation(); + } + RackShear() MainExtrude(main_sz_rhs_z){ + StrapBarrelElevation(); + } + translate([ 0,0, brk_block_z/2]) { + BrkBlock(); + } + + difference(){ + union(){ + MainExtrude(main_sz_core_z){ + BlockElevation(); + } + if ($strf<1) { + MainExtrude(max(brk_block_z, main_sz_rhs_z)){ + rectfromto([-8, adapt_main_top_y + 0.1], + [+8, block_y_min]); + rectfromto([-block_x -5, adapt_main_top_y], + [-grasp_large_x, block_y_min]); + } + } + RackShear() MainExtrude(main_sz_rhs_z){ + GraspFixingElevation(); + } + } + + translate([0,0, main_sz_rhs_z/2]) linextr(-strap_w/2, +strap_w/2) { + translate([ rack_width_inner/2 - strap_th, 0 ]) + rectfromto([ 0, -50 ], [ 50, 50 ]); + } + } + + FamLinextr(){ + if ($foreaftmaint_top_block) { + rectfromto([ -foreaftmaint_top_block_zs[0] + bolt_z, 0 ], + [ -foreaftmaint_top_block_zs[1] + bolt_z, block_y_min] ); + } + FamGraspElevation(); + } + intersection(){ + union(){ + RackShear() + FamLinextr() + FamGraspElevation(); + FamLinextr() + FamStemElevation(); + } + translate([ 0, + adapt_main_top_y - 50, + $foreaftmaint_rail_z ]) + cube(center=true, 100); + } + + linextr_y_xz( block_y_min - bolt_nut_around_y_extra , adapt_main_top_y ) + intersection(){ + translate([ 0, brk_block_z/2 ]) + circle(r = bolt_nut_around + brk_bolt_nut_r ); + rectfromto([-100, 0], [+100,+100]); + } + } + + RackShear() linextr(-10, main_sz_lhs_z+main_sz_rhs_z) { + for (mx=[0,1]) { + mirror([mx,0]) { + translate([ rack_rail_x, $rack_rail_y ]){ + hull(){ + for (dx = [-rack_rail_dia, 0]) + translate([dx, 0]) + circle(r= rack_rail_dia/2); + } + } + } + } + } + + RackShear() IfFam(){ + // Distance from bolt hole, in backwards direction + cr = rack_rail_dia/2 + foreaftmaint_r_slop; + translate([ 0, $rack_rail_y, $foreaftmaint_rail_z ]) + linextr_x_yz(+rack_rail_x, + -rack_rail_x) { + hull(){ + for (dy=[0,50]) { + translate([-dy,0]) + circle(r= cr); + } + } + hull(){ + for (dd=[[0,0], [-1,-1], [-1,+1]]) { + translate( + [-1, 0] * (rack_rail_dia - fit_slope_len) + + 20 * dd + ) + circle(r= cr); + } + } + } + } + + translate([ 0,0, brk_block_z/2]) BoltHole(); + } +} + +module ForRackForDemo(){ + elevation = elevation_of_bolt_for(rear_to_cross_rail); + rack_rail_y = rack_rail_y_of_elevation(elevation); + + rotate([atan( + front_to_rear_elevation_change / + cross_rail_distance + ), 0,0]) + translate([0, rack_rail_y, brk_block_z/2 + rack_rail_y*rack_shear_ratio]) + children(); +} + +module RackForDemoRails(){ + ForRackForDemo() { + for (m=[0]) mirror([m,0,0]) { + linextr(-(50 + cross_rail_distance), 50 + rear_to_cross_rail) + translate([rack_rail_x, 0]) + circle(r= rack_rail_dia/2); + } + } +} + +module RackForDemoCrosses(){ + ForRackForDemo() { + for (z = [ + rear_to_cross_rail, + rear_to_cross_rail - cross_rail_distance, + ]) { + translate([0,0,z]) + linextr_x_yz(rack_rail_x, -rack_rail_x) + circle(r= rack_rail_dia/2, $fn=8); + } + } +} + +module Front(){ ////toplevel + rotate([180,0,0]) + Principal($reverse_sign = -1, + $foreaftmaint_top_block = false, + $foreaftmaint_cutoff = true, + $elevation_nominal= + elevation_of_bolt_for(rear_to_cross_rail + rear_bolt_to_front_bolt), + $foreaftmaint_dz= front_to_cross_rail); +} + +module Rear(){ ////toplevel + Principal($reverse_sign = +1, + $foreaftmaint_top_block = true, + $foreaftmaint_cutoff = false, + $elevation_nominal= + elevation_of_bolt_for(rear_to_cross_rail), + $foreaftmaint_dz= rear_to_cross_rail); +} + +module SomeDemo(){ + rotate([90, 0, 0]){ + children(); + + color("blue") + translate([ 0, -2, -4 ]) + square(center=true, [ brk_overall_w, 1 ]); + + color("red") + translate([ 0, -brk_nearbolt_recess_depth, -4 ]) + linextr_y_xz(-brk_bolt_eff_len, 0) + circle(r = brk_bolt_dia/2); + + } +} + +module FrontDemo(){ ////toplevel + SomeDemo() rotate([180,0,0]) Front(); +} +module RearDemo(){ ////toplevel + SomeDemo() Rear(); +} +module RearRackDemo(){ ////toplevel + rotate([atan(rack_shear_ratio),0,0]) SomeDemo() { + Rear(); + translate([0, 0, -rear_bolt_to_front_bolt]) + rotate([180,0,0]) Front(); + %RackForDemoRails(); + color("blue") RackForDemoCrosses(); + } +} diff --git a/topeak-seatstay-lock.scad b/topeak-seatstay-lock.scad new file mode 100644 index 0000000..072a557 --- /dev/null +++ b/topeak-seatstay-lock.scad @@ -0,0 +1,147 @@ +// -*- C -*- + +pump_dia = 27 + 0.9; +seatstay_mindia = 14 + 0.5; +seatstay_maxdia = 19 + 0.7; +pump_seatstay_gap = 12.3; +pump_seatstay_delta = 0.1; +pump_ridge_width = 8 + 2.0; + +body_thick = 5; + +pin_workdepth = 16 - 1.0; +pin_width = 11 + 1.0; +pin_thick = 3 + 0.7; +pin_base = 25; + +lock_manouvre_thick = 3.7 + 0.6; +lock_manouvre_len = 18; +lock_hang_width = 17.5; +lock_manouvre_len_smaller = 13; + +body_depth_each = 5; +clatter_gap = 0.5; + +roof_extent = 7; +roof_thick = 2; + +// fudgeish + +cut_rotation = 2; +holes_rotation = 9; +pin_y_offset = 5.5; +pin_x_offset = 0.5; +ridge_rotation = 8.5; +lock_hang_ratio = 7; + +// computed + +body_depth = pin_width + body_depth_each*2; + +module Holes(forbody=false){ + translate([0, -pump_dia/2]); + rotate(-holes_rotation){ + translate([-(pump_seatstay_gap/2 + pump_dia/2), + 0]) { + if (!forbody) + rotate(-ridge_rotation + holes_rotation) + translate([-50, -pump_ridge_width/2]) + square([50, pump_ridge_width]); + circle(r=pump_dia/2, $fn=80); + } + translate([+(pump_seatstay_gap/2 + seatstay_mindia/2), + pump_dia/2 -seatstay_maxdia/2 -pump_seatstay_delta]) { + hull(){ + for (ud=[-1,1]) + translate([0, ud * (seatstay_maxdia-seatstay_mindia)/2]) + circle(r=seatstay_mindia/2, $fn=50); + } + } + } +} + +module BodyPlan(){ + minkowski(){ + circle(body_thick); + hull() + Holes(true); + } +} + +module Body(){ + translate([0,0,body_depth/2])mirror([0,0,1]){ + linear_extrude(height=body_depth){ + difference(){ + BodyPlan(); + Holes(); + } + } + linear_extrude(height=roof_thick){ + difference(){ + hull(){ + BodyPlan(); + translate([0,-roof_extent,0]) BodyPlan(); + } + Holes(); + } + } + } +} + +module Pin(){ + translate([pin_x_offset, pin_y_offset, 0]) rotate([0,90,0]){ + translate([0, 0, -pin_thick/2]) + linear_extrude(height=pin_thick){ + translate([-pin_base/2, 0]) square([pin_base, 50]); + translate([-pin_width/2, -100]) square([pin_width, 101]); + } + hull() for (d=[0,10]) { + translate([d*lock_hang_ratio,-d,0]) + translate([-lock_manouvre_thick/2, + -pin_workdepth-100, + -lock_manouvre_len_smaller]) + cube([lock_manouvre_thick, 100, + lock_manouvre_len + lock_manouvre_len_smaller]); + } + } +} + +module All(){ + difference(){ + Body(); + Pin(); + } +} + +module Piece(pc,interval){ + translate([0,-pc*interval,0]) + intersection(){ + rotate([0,0,pc*180-cut_rotation]) + translate([-200,clatter_gap/2,-200]) cube([400,400,400]); + All(); + } +} + +module PiecePrint(pc){ + rotate([0,0,90]) rotate([0,180,0]) + Piece(pc,4); +} + +module PiecesPrint(){ + PiecePrint(0); + PiecePrint(1); +} + +module Demo(){ + for (pc=[0,1]) + Piece(pc,0); +} + +//Holes(); +//Demo(); +//All(); +//Pin(); +//Pieces(); +PiecesPrint(); +//PiecePrint(0); +//PiecePrint(1); diff --git a/tower-base.scad b/tower-base.scad new file mode 100644 index 0000000..0e097e9 --- /dev/null +++ b/tower-base.scad @@ -0,0 +1,152 @@ +/* -*- C -*- */ + +motorwidth=35.7; +motorheight=34.5; +totalheight=58; + +pillarthick=8; +sidethick=2.5; +archthick=6.5; +frameextra=3.5; +framesplayx=5; +framesplayy=5; +botleftgap=4.5; +botleftstand=0.75; +archoutwards=(pillarthick-archthick)/sqrt(8); + +topgluecubex=18; +topgluecubez=5; +clippairy=16; +clippairdz=-2.5; +topgluecubedy=1; + +dovebasecutcylz=4; +dovebasecutcylr=10; + +d=0.01; + +mw2=motorwidth/2; + +include + +module corner() { + $fn=30; + frameheight= motorheight + frameextra; + slopeheight= totalheight - frameheight; + slopex = (mw2 + archoutwards - framesplayx)/slopeheight; + slopey = (mw2 + archoutwards - framesplayy)/slopeheight; + echo(sqrt(slopex*slopex + slopey*slopey)); + + translate([-mw2,-mw2,0]) union(){ + difference(){ + union(){ + cylinder(r=pillarthick/2, h=frameheight); + translate([0,0,frameheight]) + sphere(r=pillarthick/2); + } + translate([d,d,-1]) + cube([mw2-1,mw2-1,frameheight+pillarthick+2]); + } + intersection(){ + multmatrix + ([ [ 1, 0, slopey, -archoutwards ], + [ 0, 1, slopex, -archoutwards ], + [ 0, 0, 1, frameheight ], + [ 0, 0, 0, 1 ]]) + translate([0,0,-frameextra]) + cylinder(r=archthick/2, + h=slopeheight+frameextra); + union() { + cylinder(r=pillarthick/2, h=frameheight); + translate([-100,-100,frameheight]) + cube([200,200,100]); + } + } + } +} + +module halfside() { + spacesz = (motorwidth - pillarthick/2*2) / 4; + panelheight = spacesz + sidethick; + panelbasez = motorheight+pillarthick/4-panelheight; + translate([0,-mw2,0]) { + translate([-mw2,-sidethick,0]) + cube([motorwidth,sidethick,sidethick]); + difference(){ + translate([-mw2,-sidethick, panelbasez]) + cube([mw2,sidethick,panelheight]); + translate([-mw2+pillarthick/3, -sidethick, panelbasez]) + rotate([0,45,0]) + translate([0,-1,0]) + cube([spacesz * sqrt(2), + sidethick+2, + spacesz * sqrt(2)]); + } + intersection(){ + for (xz=[[-mw2+pillarthick/3-sidethick, 0, + panelbasez+sidethick], + [0, 0, panelbasez + sidethick/sqrt(2)]]) { + translate(xz) + translate([0,-sidethick,0]) + rotate([0,55,0]) + translate([0,0,-sidethick]) + cube([100, sidethick, sidethick]); + } + translate([-mw2,-sidethick,0]) + cube([motorwidth,sidethick, + motorheight+pillarthick]); + } + } +} + +module towerbase() { + difference(){ + union(){ + for (mirx=[0,1]) for (miry=[0,1]) + mirror([mirx,0,0]) mirror([0,miry,0]) corner(); + for (angle=[0,90,180]) { + rotate([0,0,angle]) halfside(); + rotate([0,0,angle]) mirror([1,0,0]) halfside(); + } + } +// multmatrix([[ -1, 0, 0, -mw2 - botleftstand ], +// [ 0, 1, 0, -100 ], +// [ 1, 0, 1, -100 + botleftgap ], +// [ 0, 0, 0, 1 ] ]) +// cube([100,200,100]); + } + translate([clippairy/2,0,totalheight]) { + difference(){ + translate([-clippairy+topgluecubedy/2,-topgluecubex/2,0]) + cube([clippairy-topgluecubedy,topgluecubex,topgluecubez]); + } + translate([0,0,topgluecubez+clippairdz+DoveClip_depth()]) rotate([0,-90,0]) +// DoveClipPair(h=clippairy); + DoveClipPairSane(h=clippairy, count=3); + } +} + +if (towerbase_demo) { + intersection(){ + translate([0,0,-50]) towerbase(); + translate([-100,-100,0]) cube([200,200,32]); + } + + intersection(){ + translate([40,0,-60]) towerbase(); + translate([-100,-100,0]) cube([200,200,32]); + } + + translate([60,-90,0]) { + DoveClipPairSane(h=clippairy, count=3); + mirror([1,0,0]) translate([DoveClip_depth()-0.1,0,0]) cube([20,8,6]); + } + + for (x=[0,20,40]) { + translate([x,-50,0]) DoveClipPin(h=clippairy); + translate([x+10,-50,0]) DoveClipPin(h=clippairy/2); + translate([x+10,-30,0]) DoveClipPin(h=clippairy/2); + } +} else { + towerbase(); +} diff --git a/trackpump-mutlihead-clip.scad b/trackpump-mutlihead-clip.scad new file mode 100644 index 0000000..c5108fd --- /dev/null +++ b/trackpump-mutlihead-clip.scad @@ -0,0 +1,174 @@ +// -*- C -*- + +include + +pump_main_dia = 38; +pump_side_width = 5; +pump_side_thick = 4; +pump_shaft_dia = 14; +baseplate = 3; + +pump_protr_flat = 3; +pump_protr_slope = 0.9; + +hose_inner_dia = 20; +hose_aperture = 11; +hose_side_width = 5; +hose_base_offset = 30; + +hose_side_thick = 6; +hose_side_stalk_width = 6; + +pump_protr_protr = 3; +pump_side_height = 20; + +// calculated +pump_protr_slheight = pump_protr_protr / pump_protr_slope; + +pump_side_outer_rad = pump_side_width + pump_main_dia/2; + +baseplate_width_rad = + sqrt( pow(pump_side_outer_rad, 2) + -pow( pump_main_dia/2 - pump_protr_protr, 2) ); + +xm = baseplate + pump_main_dia/2; + +pump_side_total_height = + pump_side_thick + pump_side_height + pump_protr_slheight + pump_protr_flat; + +$fa=5; + +module PumpSidePlan() { + or = pump_side_outer_rad; + difference(){ + union(){ + intersection(){ + translate([-xm, 0]) circle(r=or); +// translate([-(xm+or), -or]) square([xm+or, or*2]); + } + } + translate([-xm-or, 0]) + square(center=true, [pump_side_width*4, pump_shaft_dia]); + } +} + +module PumpSideElevation(){ + x3 = 0; + x2 = x3 - baseplate; + x1 = x2 - pump_main_dia; + x0 = x1 - pump_side_width; + x2a = x2 - pump_protr_protr; + x4 = x2 + pump_side_width; + + z0 = 0; + z1 = z0 - pump_side_thick; + z2 = z1 - pump_side_height; + z2a = z2 - pump_protr_slheight; + z2b = z2a - pump_protr_flat; + + arcx = x2-x1; + arcy = z1-z2; + + translate([x0,z1]) square([x1-x0, z0-z1]); + + difference(){ + translate([x1,z2]) square([x3-x1, z0-z2]); + translate([x1,z2]) scale([1,arcy/arcx]) circle(r=arcx); + } + + translate([x2,z2a]) square([x4-x2, z0-z2a]); + + hull(){ + translate([x2,z2a]) square([x4-x2, z2-z2a]); + translate([x2a,z2b]) square([x3-x2a, z2a-z2b]); + } +} + +module PumpSide(){ + br = baseplate_width_rad; + brs = hose_side_stalk_width/2; + echo(brs); + + difference(){ + intersection(){ + translate([0,100,0]) + rotate([90,0,0]) + linear_extrude(height=200, convexity=10) + PumpSideElevation(); + union(){ + translate([0,0,-100]) + linear_extrude(height=200, convexity=10) + PumpSidePlan(); + // baseplate + hull(){ + mirror([0,0,1]) + translate([-xm, -brs, 0]) + cube([pump_main_dia/2 + pump_side_width, + brs*2, + 1]); + translate([-xm, -br, -pump_side_total_height]) + cube([xm, + br*2, + pump_protr_flat]); + } + } + } + translate([-(baseplate + pump_main_dia/2), 0, + -(pump_side_thick + pump_side_height)]) + cylinder(r=pump_main_dia/2, h=200); + } + rotate([0,0,180]) + mirror([0,0,1]) + translate([-0, + -br, + pump_side_total_height]) + Commitid_BestCount_M([baseplate + pump_protr_protr, + br*2]); +} + +module HoseSidePlan(){ + ro = hose_inner_dia/2 + hose_side_width; + ri = (hose_inner_dia/2); + st = hose_side_stalk_width/2; + + apx = sqrt( ri*ri - (hose_aperture*hose_aperture)/4 ); + apsq = hose_base_offset + apx - hose_aperture/2; + echo(apx,apsq); + + difference(){ + union(){ + translate([-1, -st]) square([hose_base_offset+1, st*2]); + translate([hose_base_offset, 0]) circle(r= ro); + } + translate([hose_base_offset, 0]) circle(r= hose_inner_dia/2); + translate([apsq, 0]) + rotate(-45) + square([50,50]); + } + + //%translate([hose_base_offset + apx, 0]) square([50,50]); + //%square(center=true, [100, hose_aperture]); +} + +module HoseSide(){ + mirror([0,0,1]) + linear_extrude(height=hose_side_thick, convexity=10) + HoseSidePlan(); +} + +module Clip(){ + PumpSide(); + HoseSide(); +} + +module ClipPrint(){ + rotate([180,0,0]) + Clip(); +} + +//PumpSidePlan(); +//PumpSideElevation(); +//PumpSide(); +//HoseSide(); +//Clip(); +ClipPrint(); diff --git a/trailerhubcap.scad b/trailerhubcap.scad new file mode 100644 index 0000000..4ae4c7d --- /dev/null +++ b/trailerhubcap.scad @@ -0,0 +1,80 @@ +// Copyright (C)2012 Ian Jackson +// Licenced under the GNU General Public Licence, version 3, or +// (at your option) any later version. There is NO WARRANTY. + +maindia = 29.7; +poleholeh = 5.0; +polecovth = 0.4; +poleholerad = 6; + +mainoverlap = 1.5; + +hookbasew = 5.0; +hookfullw = 7; +hookheight = 4.5; +hookwidth = 8; +hookbevelw = 0.75; +hookbevelh = 1.5; + +fingernaildepth = 5; +fingernailheight = 2.5; +fingernailwidth = 6; + +bigrad = maindia/2 + mainoverlap; +mainth = poleholeh + polecovth; +hooklessdepth = hookfullw - hookbasew; + +module base() { + rotate_extrude(convexity=10) + mirror([1,0,0]) + polygon(points=[[-bigrad, 0], + [-bigrad + mainth, -mainth], + [0, -mainth], + [0, -poleholeh], + [-poleholerad, -poleholeh], + [-poleholerad, 0]]); +} + +module fingernails() { + for (ang=[60,180,300]) + rotate([0,0,ang]) + translate([bigrad - fingernaildepth, + -fingernailwidth/2, + -fingernailheight]) + cube([fingernaildepth + 1, fingernailwidth, fingernailheight + 1]); +} + +module hookrim() { + rotate_extrude(convexity=10) + mirror([1,0,0]) + translate([-maindia/2, 0, 0]) + polygon(points=[[hooklessdepth, 0], + [hookfullw, 0], + [hookfullw*0.33, hookheight], + [hookbevelw, hookheight], + [0, hookheight-hookbevelh], + [0, hooklessdepth]]); +} + +module hooktriangles() { + for (ang=[0,120,240]) { + rotate([0,0,ang]) { + translate([0,0,-1]) { + linear_extrude(height=hookheight+2) { + polygon(points=[[0, 0], + [maindia/2 + 1, -hookwidth], + [maindia/2 + 1, +hookwidth]]); + } + } + } + } +} + +difference(){ + base(); + fingernails(); +} +intersection(){ + hookrim(); + hooktriangles(); +} diff --git a/treefoil.scad.pl b/treefoil.scad.pl new file mode 100755 index 0000000..ef9ef26 --- /dev/null +++ b/treefoil.scad.pl @@ -0,0 +1,79 @@ +#!/usr/bin/perl -w + +# Use: +# - support X/Y dist 0.5mm + +use strict; + +our $shape = <<'END'; +xyyZZYYXYxxyzYYZXzzxyXXYXXXZxxxyyXXZyyyzXXzz +zxxYYXXZXzzxyXXYZyyzxZZXZZZYzzzxxZZYxxxyZZyy +yzzXXZZYZyyzxZZXYxxyzYYZYYYXyyyzzYYXzzzxYYxx +END +# simple version (unknotted, [0,1,2]^3): +# YxxyzYYZXzzxyXXYZyyzxZZX +# +# New and less symmetric one that also fits in the 2x2x2 box: +# YXXyzzYxYXZZxzyxYzyyZXZx +# (email 8.9.2022) + +sub o { print @_ or die $!; } + +o <<'END'; +// -*- autogenerated, do not edit -*- + +module Trace() { +END + +my @p = qw(0 0 0); + +$"=','; + +while ($shape =~ s/^\s*(\w)//) { + my $ix = index('xyz', (lc $1)); + my $sign = $1 =~ /[A-Z]/ ? +1 : -1; + my @q = @p; + $q[$ix] += $sign; + o " TraceEdge([@p],[@q]);\n"; + @p = @q; +} + +die @p unless "@p" eq '0,0,0'; + +o "}\n\n"; + +while () { o $_ } + +__DATA__ + +thick = 6; +edgeu = 10; +//edgeu = 15; + +// calculated + +octa_long = thick; +octa_short = octa_long / (1 + sqrt(2)); + +module OctaThing() { + hull(){ + for (r = [[0,0,0], [90,0,0], [0,90,0]]) { + rotate(r) + cube([ octa_short,octa_short, octa_long ], center=true); + } + } +} + +module TraceEdge(p,q) { + hull(){ + for (x=[p,q]) { + translate(x * edgeu) + OctaThing(); + } + } +} + +rotate([0,0,45]) + Trace(); + + diff --git a/tube-crossdrill-jig.scad b/tube-crossdrill-jig.scad new file mode 100644 index 0000000..843a2a6 --- /dev/null +++ b/tube-crossdrill-jig.scad @@ -0,0 +1,138 @@ +// -*- C -*- + +//$fs=0.1; +//$fa=3; +$fs=0.2; +$fa=6; + +rearwallthick = 3; +basethick = 2; +mainframeendthick = 2.5; + +tubedia = 16 + 0.8; +tubetubethick=2; +tubetubetopslop=1; + +boltholedia = 6.5 + 0.5; +boltholeslotshorter = 6; +mainframeholedia = 5 + 1.0; + +// "slot" refers to the things in the base of the drill press stand +backslotedgespace = 59; +slotwidth = 11.5 - 0.5; +backslotmid2screwhole = 17; +slotplugheight = 5.5; +slotplugshorterlen =10; +slotpluglongerlen = 20; + +//slotslope = 11.0 / 18.5; +slotslope = 1 / tan(51); + +// "keepslot" refers to the screws in the wooden jig block +keepslotstartz = 20; +keepslotlen = 18; +keepslotx = backslotedgespace / 2; +keepslotwidth = 4; + +mainframeextraside = 12; +mainframeextrafront = 15; + +rearwallstrengthwidth = 10; +keepslotclear = 10; + +// computed values + +slotslopediag = sqrt(1 + slotslope*slotslope); +slotwidthx = slotwidth * slotslopediag; + +slotxperlen = slotslope / slotslopediag; +slotyperlen = 1 / slotslopediag; + +mainframeholex = backslotedgespace/2 + slotpluglongerlen * slotxperlen + + 0.5 * slotwidth * slotyperlen; + +mainframeholey = -slotpluglongerlen * slotyperlen + + 0.5 * slotwidth * slotxperlen; + +mainframemaxx = mainframeholex + mainframeextraside; +mainframeminy = mainframeholey - mainframeextrafront; +mainframemaxz = keepslotstartz + keepslotlen; + +module MainFrame(){ + for (m=[0,1]) { + mirror([m,0,0]) { + translate([-1, mainframeminy, 0]) + cube([mainframemaxx+1, -mainframeminy, basethick]); + translate([-1, -rearwallthick, 0]) + cube([mainframemaxx+1, rearwallthick, mainframemaxz]); + + for (x=[keepslotx - keepslotclear, mainframemaxx - 0.5]) { + translate([x,0,0]) + rotate([90,0,-90]) + linear_extrude(height=mainframeendthick) + polygon([[-mainframeminy, 0], + [0, mainframemaxz], + [0, 0]]); + } + + translate([backslotedgespace/2, 0, 1]) + mirror([0,0,1]) + linear_extrude(height=slotplugheight+1) + polygon([[0,0], + [slotwidthx, 0], + [slotwidthx + slotplugshorterlen * slotxperlen, + -slotplugshorterlen * slotyperlen], + [slotpluglongerlen * slotxperlen, + -slotpluglongerlen * slotyperlen]]); + translate([-1, + -rearwallthick - boltholeslotshorter + 0.2, + tubedia + tubetubetopslop + tubetubethick + 4]) + cube([keepslotx - keepslotclear + 1, + boltholeslotshorter + 0.5, + rearwallstrengthwidth]); + } + } +} + +module TubeThing(extralen, dia, extraheight, underheight){ + effheight = tubetubetopslop + extraheight + underheight; + len = -mainframeminy + extralen * 2; + translate([0, mainframeminy - extralen, -underheight]) { + translate([0,0, dia/2 + effheight]) + rotate([-90,0,0]) cylinder(h=len, r=dia/2); + translate([-dia/2, 0, 0]) + cube([dia, len, effheight + dia/2]); + } +} + +module Jig(){ + difference(){ + union(){ + MainFrame(); + TubeThing(0, tubedia+tubetubethick*2, -tubetubethick, 0); + } + union(){ + translate([0,0,-0.1]) + TubeThing(10, tubedia, 0, 5); + translate([-boltholedia/2, mainframeminy - 1, -5]) + cube([boltholedia, + -mainframeminy + 1 - rearwallthick - boltholeslotshorter, + mainframemaxz + 10]); + for (m=[0,1]) { + mirror([m,0,0]) { + translate([mainframeholex, mainframeholey, -30]) + cylinder(h=basethick+40, r=mainframeholedia/2); + translate([keepslotx - keepslotwidth/2, + -10, keepslotstartz]) + cube([keepslotwidth, 20, keepslotlen + 10]); + } + } + } + } +} + +//MainFrame(); +//TubeThing(0, tubedia); + +rotate([-90,0,0]) + Jig(); diff --git a/utils.scad b/utils.scad deleted file mode 100644 index e440b5f..0000000 --- a/utils.scad +++ /dev/null @@ -1,51 +0,0 @@ -// -*- C -*- - -// suitable for masking things within radius sqrt(2) only -module FArcSegment_mask(beta) { - for (i=[0 : 0.75 : 3]) { - rotate(i*beta/4) - polygon([[0, 0], - [1, 0], - [cos(beta/4), sin(beta/4)]]); - } -} - -module FArcSegment(xc,yc,inrad,outrad,alpha,delta) { - translate([xc,yc]) { - intersection() { - difference() { - circle(r=outrad, $fn=70); - circle(r=inrad, $fn=70); - } - rotate(alpha) scale(outrad*2) { - FArcSegment_mask(delta); - } - } - } -} - -module rectfromto(a,b) { - ab = b - a; - translate([min(a[0], b[0]), min(a[1], b[1])]) - square([abs(ab[0]), abs(ab[1])]); -} -module circleat(c,r) { translate(c) circle(r); } -module linextr(z0,z1, convexity=20) { - translate([0,0,z0]) - linear_extrude(height=z1-z0, convexity=convexity) - children(); -} - -module linextr_x_yz(x0,x1, convexity=20) { // XY turn into YZ - rotate([90,0,0]) - rotate([0,90,0]) - linextr(x0,x1, convexity=convexity) - children(); -} - -module linextr_y_xz(y0,y1, convexity=20) { // XY turn into YZ - rotate([0,0,180]) - rotate([90,0,0]) - linextr(y0,y1, convexity=convexity) - children(); -} diff --git a/utils.scad b/utils.scad new file mode 120000 index 0000000..b1ed200 --- /dev/null +++ b/utils.scad @@ -0,0 +1 @@ +diziet-utils/utils.scad \ No newline at end of file diff --git a/velux-window-grip.scad b/velux-window-grip.scad new file mode 100644 index 0000000..d59a78f --- /dev/null +++ b/velux-window-grip.scad @@ -0,0 +1,138 @@ +// -*- C -*- + +include + +// MainLoop + +main_thick = 9.0; +main_in_dia = 28.9; + +horn_ext_dia = 20 - 0.5; + +horn_c_x = -4.6; +horn_c_dy= -4; + +blhook_start_ang = 45; +blhook_in_rad = 1.85; +blhook_str_len = 2.9; + +width = 20; + +// Attach + +at_bolt_into = 13.0 + 0.5; +at_tube_dia = 16.7 + 0.5; +at_prong_minw = 4; +at_rear_thick = 4.5; +at_bolt_dia = 5 + 0.5; + +at_rear_width = at_tube_dia; +at_stem_len = main_in_dia/2 * 0.3; + +at_prong_depth = at_bolt_into * 2; +at_gap_width = at_tube_dia * 0.75; + +// computed + +blhook_mid_rad = blhook_in_rad + main_thick/2; +mc_mid_rad = main_in_dia/2 + main_thick/2; + +mc_bl = circle_point([0,0], mc_mid_rad, 270-blhook_start_ang); + +at_block_x = at_tube_dia + at_prong_minw * 2; +at_block_y = at_prong_depth + at_rear_thick; +at_block_z = width; + +at_stem_yy = at_stem_len + mc_mid_rad; + +at_offset_y = at_block_y + at_stem_len + mc_mid_rad; + +$fs=0.05; + +horn_thick = main_thick; + +module MainLoop(){ + intersection(){ + difference(){ + circle(r= main_in_dia/2 + main_thick, $fn=50); + circle(r= main_in_dia/2, $fn=50); + } + polygon([[0,0], + 3*mc_bl, + [0, -100], + [100,-100], + [100,100], + [0,100]]); + } + translate(mc_bl) + circle(main_thick/2); + translate([horn_c_x, mc_mid_rad + horn_c_dy]) + intersection(){ + difference(){ + circle(horn_ext_dia/2); + intersection(){ + circle(horn_ext_dia/2 - horn_thick); + polygon([[-50,-50], + [-50,-horn_c_dy], + [50,-horn_c_dy], + [50,-50]]); + } + } + polygon([[0,0], + [-50,0], + [0,50]]); + } + translate([0,main_in_dia/2]) mirror([1,0]) + square([-horn_c_x + horn_ext_dia/2 * 0.75, main_thick]); + translate(mc_bl){ + translate([-blhook_str_len/2, 0]) + square(center=true, [blhook_str_len, main_thick]); + translate([-blhook_str_len, blhook_mid_rad]){ + intersection(){ + difference(){ + circle(r=blhook_mid_rad + main_thick/2); + circle(r=blhook_mid_rad - main_thick/2); + } + mirror([1,1]) square(50); + } + } + } +} + +module MainLoopTest(){ + linear_extrude(height=1.6) + MainLoop(); +} + +module Attach(){ + difference(){ + translate([0, at_block_y/2, 0]) + cube(center=true, [at_block_x, at_block_y, at_block_z]); + translate([0, at_prong_depth/2-1, 0]) + cube(center=true, [at_gap_width, at_prong_depth+2, at_block_z+1]); + translate([0,-1,0]) + rotate([-90,0,0]) + cylinder(r= at_tube_dia/2, h= at_prong_depth+1); + translate([-50, at_prong_depth-at_bolt_into, 0]) + rotate([0,90,0]) + cylinder(r= at_bolt_dia/2, h= 100); + } + difference(){ + translate([0, at_block_y + at_stem_yy/2 - 0.1, 0]) + cube(center=true, [at_tube_dia, at_stem_yy + 0.2, at_block_z]); + translate([0, at_offset_y, -50]) + cylinder(r = mc_mid_rad, 100); + } +} + +module Combine(){ + rotate([0,0,45]) translate([0,-main_thick/2,0]){ + linear_extrude(height=width) + translate([0,at_offset_y,0]) + MainLoop(); + translate([0,0, width/2]) + Attach(); + } +} + +Combine(); diff --git a/velux-window-grip.slic3r b/velux-window-grip.slic3r new file mode 100644 index 0000000..3a44d19 --- /dev/null +++ b/velux-window-grip.slic3r @@ -0,0 +1 @@ +solid_infill_every_layers = 11 diff --git a/wall-cable-hook.scad b/wall-cable-hook.scad new file mode 100644 index 0000000..3076265 --- /dev/null +++ b/wall-cable-hook.scad @@ -0,0 +1,40 @@ +// -*- C -*- + +circle_inner_rad = 10 + 0.5; + +thick = 3; + +tab_sz = 20; +width = 20; + +screw_hole_dia = 4.5 + 0.5; + +// calculated + +circle_outer_rad = circle_inner_rad + thick; + +module Plan() { + difference(){ + circle(r=circle_outer_rad, $fn=150); + circle(r=circle_inner_rad, $fn=150); + mirror([1,0]) square([50,50]); + } + translate([-circle_outer_rad, -0.1]) + multmatrix([[1,0,0,0], + [-1,1,0,0], + [0,0,1,0], + [0,0,0,1]]) + square([thick, tab_sz + circle_outer_rad + thick]); +} + +module Hook(){ + difference(){ + linear_extrude(height=width) Plan(); + if (false) + translate([-50, circle_outer_rad + tab_sz - width/2, width/2]) + rotate([0,90,0]) + cylinder(r= screw_hole_dia / 2, h=100, $fn=50); + } +} + +Hook(); diff --git a/wardrobe-hook.scad b/wardrobe-hook.scad new file mode 100644 index 0000000..959e967 --- /dev/null +++ b/wardrobe-hook.scad @@ -0,0 +1,192 @@ +// -*- C -*- + +include +include + +tubeslop = 0.5; +tubeheight = 30 + tubeslop; +tubewidth = 15 + tubeslop; +mainthick = 4; + +clipthick = 2; +clipang = 135; + +stemlen = 40; + +topwidth = 20; + +hookinrad = 7.5; +hookcurl = 60; +hookwidth = 4; + +tuberad = tubewidth/2; +bend = atan(tuberad/stemlen); +mainoutrad = tuberad + mainthick; +hookoutrad = hookinrad + hookwidth; +hookcy = -(stemlen - hookoutrad); + +eltop = [topwidth/2, -tuberad + tubeheight + mainthick + 0.1]; +elmid = [topwidth/2, -tuberad]; +ellow = tangent_intersect_b([0,hookcy], hookinrad, elmid); +ellowextra = 180 - tangent_intersect_beta([0,hookcy], hookinrad, elmid); + +module ClipPlan(qbend, qstemleny){ + dy = tubeheight - tuberad*2; + FArcSegment(0, dy, tuberad, mainoutrad, -1, 181); + FArcSegment(0, 0, tuberad, mainoutrad, -qbend, qbend+1); + translate([tuberad, 0]) square(center=false, size=[mainthick,dy]); + FArcSegment(0, 0, tuberad, tuberad + clipthick, 360-clipang, clipang+1); + rotate(-qbend) translate([tuberad, 0]) mirror([0,1]) + square(center=false, size=[mainthick, qstemleny/cos(qbend)]); +} + +module Plan(){ + ClipPlan(bend,stemlen); +} + +module ElevationCore(){ + FArcSegment(0, hookcy, hookinrad, hookoutrad, + 180 - ellowextra, + 90 + hookcurl + ellowextra); + translate([-hookoutrad*sqrt(0.5), + hookcy - hookoutrad*sqrt(0.5) + 0.1]) + mirror([1,0]) + square(center=false, size=[topwidth, stemlen + tubeheight + 20]); + polygon([[-hookoutrad, ellow[1]], + reflect_in_y(eltop), + eltop, + elmid, + ellow]); +} + +// after here is all 3D + +module Primary(){ + intersection(){ + translate([0,0, -(topwidth+10)/2]) + linear_extrude(height=topwidth+10) Plan(); + translate([50,0]) + rotate([0,-90,0]) + linear_extrude(height=100) + ElevationCore(); + } +} + +module PlaneAbove(){ + translate([-100,-100,0]) cube(center=false,size=[200,200,200]); +} + +taperangle = -270 + tangent_intersect_beta([-hookcy, 0], + hookoutrad, + [-eltop[1], -eltop[0]]); +module HookL(){ ////toplevel + difference(){ + rotate([taperangle,0,0]) + translate([0,-eltop[1],0]) + Primary(); + translate([0,0,topwidth/2]) + rotate([taperangle*2,0,0]) + PlaneAbove(); + translate([0,0,-topwidth/2]) + mirror([0,0,1]) PlaneAbove(0); + } +} + +// straight-on version, everything prefixed with s or S + +shookcy = -(stemlen-hookoutrad); +sstemleny = -shookcy; +sbend_raw = tangents_intersect_beta([0,0],tuberad, + [0,shookcy],hookinrad); +sbend = angle_map_range(360-sbend_raw, -180); + +module SPlan(){ + ClipPlan(sbend, sstemleny); + FArcSegment(0,shookcy, hookinrad,hookoutrad, + 270 - hookcurl, + hookcurl + 90 - sbend); +} + +module SElevation(){ + boty = shookcy - hookoutrad - 1; + polygon([[-1, tubeheight], + [topwidth, tubeheight], + [topwidth, elmid[1]], + [hookwidth, shookcy], + [hookwidth, boty], + [-1, boty]]); +} + +module SElevationPlaced(){ + rotate([0,-90,0]) translate([0,0,-100]) linear_extrude(height=200) + SElevation(); +} + +module SHookL(){ ///toplevel + intersection(){ + linear_extrude(height=topwidth) SPlan(); + SElevationPlaced(); + } +} + +// straight-on version, reversed, everything prefixed with t or T + +tjoinrad = mainoutrad * 0.7; +tstem0leny = tuberad - tjoinrad*0.5; +tjoinoutrad = tjoinrad + mainthick; + +thookcy = shookcy; + +tjoin0c = [tuberad - tjoinrad, -tstem0leny]; +tjoin1c = [0, thookcy]; + +tbend_raw = tangents_intersect_beta(tjoin0c, tjoinrad, + tjoin1c, -hookoutrad); +tbend0 = angle_map_range(tbend_raw, 0); +tbend1 = angle_map_range(tbend_raw + 180, -180); + +tbend0p = circle_point(tjoin0c, tjoinrad, tbend_raw); +tbend1p = circle_point(tjoin1c, -hookoutrad, tbend_raw); + +module TPlan(){ + ClipPlan(0, tstem0leny); + FArcSegment(tjoin0c[0],tjoin0c[1], tjoinrad,tjoinoutrad, + tbend0, 360-tbend0); + FArcSegment(0,shookcy, hookinrad,hookoutrad, + tbend1, 270+hookcurl - tbend1); + translate(tbend0p) { + rotate(tbend_raw+180) mirror([1,0]) { + translate([0,-0.1]) square(size=[mainthick, dist2d(tbend0p,tbend1p)+0.2]); + } + } +} + +module THookR(){ ///toplevel + intersection(){ + linear_extrude(height=topwidth) TPlan(); + SElevationPlaced(); + } +} + +// other toplevels etc. + +module HookR(){ ////toplevel + mirror([1,0,0]) HookL(); +} + +module SHookR(){ ////toplevel + mirror([1,0,0]) SHookL(); +} + +module THookL(){ ////toplevel + mirror([1,0,0]) THookR(); +} + +module Demo(){ ////toplevel + translate([-30,tubeheight,0]) HookL(); + translate([ 0,tubeheight,0]) HookR(); + translate([ 30, 0,0]) SHookL(); + translate([ 60, 0,0]) SHookR(); + translate([ 90, 0,0]) THookL(); + translate([120, 0,0]) THookR(); +} diff --git a/warptest.scad b/warptest.scad new file mode 100644 index 0000000..ee5b3b2 --- /dev/null +++ b/warptest.scad @@ -0,0 +1,2 @@ +//rotate([0,0,45]) +cube([3,100,25]); diff --git a/warptest2.scad b/warptest2.scad new file mode 100644 index 0000000..d6a11b8 --- /dev/null +++ b/warptest2.scad @@ -0,0 +1,8 @@ +// -*- C -*- +rotate([90,0,0]) +linear_extrude(height=50){ + polygon([[-3/2, 0], + [-11/2, 8], + [+11/2, 8], + [+3/2, 0]]); +} diff --git a/warptest3.scad b/warptest3.scad new file mode 100644 index 0000000..ee80051 --- /dev/null +++ b/warptest3.scad @@ -0,0 +1,30 @@ +// -*- C -*- + +dy= 145; +dx= 65; + +h1= 8; +h2= 14; +ratio = 0.8; + +module Plan(){ + polygon([[ -dx/2, 0 ], + [ 0, dy/2 ], + [ dx/2, 0 ], + [ 0, -dy/2 ]]); +} + +module Solid(){ + rotate([0,0, -45]) { + hull(){ + linear_extrude(height= h1) { + Plan(); + } + linear_extrude(height= h2) { + scale(ratio) Plan(); + } + } + } +} + +Solid(); diff --git a/wine-vacuum-adapter.scad b/wine-vacuum-adapter.scad new file mode 100644 index 0000000..5ce11a8 --- /dev/null +++ b/wine-vacuum-adapter.scad @@ -0,0 +1,74 @@ +// -*- C -*- + +india_nom = 27.0; +india_slop = 0.63; + +middia_nom = 31.0; +middia_slop = 0.10; + +outdia = 44.0; + +wall = 4; + +htop = 5; +hbot = 7; + +slope = 0.65; + +$fa=3; +$fs=0.1; + +// calculated + +india_use = india_nom + india_slop; +middia_use = middia_nom - middia_slop; + +//echo("MIN WALL", (middia_use - india_use)/2); + +ppA = [middia_use/2, 0]; +ppB = ppA + [0, 1] * htop; +ppC = ppB + [-1,0] * wall; +ppD = [ppC[0], ppA[1] - wall/slope]; +ppE = [india_use/2, ppD[1] - (india_use/2 - ppD[0])/slope]; +ppF = ppE + [0,-1] * (htop + hbot); +ppG = ppF + [1, 0] * wall; +ppK = [outdia/2, ppA[1]]; +ppJ = ppK + [0,-1] * wall; +ppH = [ppG[0], ppJ[1] - (ppJ[0]-ppG[0])/slope]; + +module Plan1() { + polygon([[ india_use/2, -hbot ], + [ outdia/2, -hbot ], + [ outdia/2, 0 ], + [ middia_use/2, 0 ], + [ middia_use/2, htop ], + [ india_use/2, htop ]]); +} + +module Plan3() { + p = [ ppA, + ppB, + ppC, + ppD, + ppE, + ppF, + ppG, + ppH, + ppJ, + ppK ]; + echo(p); + polygon(p); +} + +module Demo(){ + color("blue") translate([0,0,1]) Plan1(); + Plan3(); +} + +module Adapter(){ + rotate_extrude(convexity=5) + Plan1(); +} + +//Demo(); +Adapter(); diff --git a/xeno-drivebay-bracket.scad b/xeno-drivebay-bracket.scad new file mode 100644 index 0000000..2073e05 --- /dev/null +++ b/xeno-drivebay-bracket.scad @@ -0,0 +1,195 @@ +// -*- C -*- + +basel = 16; +basew = 24; +baset = 4.0; + +wallt = 2.5; + +wallh = 42; + +baseholesz = 3.7; +baseholeslot = 6.5; +baseholeslop = -0.5; + +holeslop = 0.5; + +webt = 2.5; + +pad = false; +padw = 12; + +padt = webt; +padl = padw; +padholesz = 3.0; + +wallholeh = 6+14+2; +wallholesz = 3.0; +wallholeslot = 4.5; + +walll = basel + webt + (pad ? padl : -0.1); + +webw = min(basew, pad ? padw : padt); + +module slothole(sz, slot, thick, csunk=true, slop=holeslop) { + hull(){ + for (y = [-slot/2,slot/2]) { + translate([0,y,-0.15]) + cylinder(r1=sz/2 + slop, + r2=sz/2 + (csunk ? thick : 0) + slop, + h=thick+0.30); + } + } +} + +module Bracket(){ + difference(){ + translate([0, -basew, 0]) + cube([basel, basew, baset]); + + translate([basel/2, -(basew+wallt)/2, 0]) + slothole(baseholesz, baseholeslot, baset, slop=baseholeslop); + } + + difference(){ + translate([0.1, 0.3, 0.1]) + rotate([90,0,0]) { + linear_extrude(height=wallt){ + polygon([[0,0], + [0, wallh/2 + wallholesz/2 + wallt + wallt], + [basel, wallh], + [walll, wallh], + [walll, wallh - padt - padt], + [basel + webt, 0]]); + } + } + + translate([basel/2, 0, wallholeh]) + rotate([90,90,0]) + slothole(wallholesz, wallholeslot, wallt, csunk=false); + } + + translate([basel-0.01, 0, 0]) { + rotate([90,0,90]) { + linear_extrude(height=webt+0.02) { + polygon([[-basew, 0], + [-basew, baset], + [-webw, wallh], + [0, wallh], + [0, 0]]); + } + } + } + + if (pad) { + translate([basel+webt, -padw, wallh-padt]) { + difference(){ + cube([padl, padw, padt]); + translate([padl/2, padw/2, -1]) + cylinder(r=padholesz/2 + holeslop, h=padt+2); + } + } + } +} + +module BracketR(){ ////toplevel + rotate([-90,0,0]) Bracket(); +} + +module BracketL(){ ////toplevel + mirror([1,0,0]) BracketR(); +} + +protinnerh = 47; +protinnerw = 53; +protd = 45; +protbaset = 4; +protwallt = 2; +protlidt = protwallt; +protwingd = 28; +protwingw = 23; + +module RearCableProtector(){ + for (x = [-protwallt, protinnerw]) { + translate([x, 0, 0]) { + cube([protwallt, protd, protinnerh+protlidt]); + } + } + translate([-(protwallt-0.1), 0, protinnerh]) + cube([protinnerw + (protwallt-0.1)*2, protd, protlidt]); + for (lr = [1,0]) { + translate([(lr ? -(protwingw + protwallt) : protinnerw), 0, 0]) { + difference(){ + translate([0, 0, 0]) + cube([protwingw, protwingd, protbaset]); + translate([protwingw/2, protwingd/2, 0]) + rotate([0,0, lr ? 45 : -45]) + slothole(baseholesz, baseholeslot, baset, slop=baseholeslop); + } + } + } +} + +module RearCableProtectorT(){ ////toplevel + rotate([90,0,0]) RearCableProtector(); +} + +chabd = 20; +chablidw = 40; +chabinnerh = 11; +chabwallt = 2; +chablidt = 2; +chabwebt = 2.5; +chabbaset = baset; +chabbasew = 20; +chabslot = 3; +chablidholed = 3; +chabwebh = 5; + +module ChannelBracket(){ + translate([0, -chabd, 0]) + cube([chabwallt, chabd, chabinnerh+chablidt]); + translate([-chablidw, -chabd, chabinnerh]) { + difference(){ + cube([chablidw + chabwallt - 0.1, chabd - 0.1, chablidt]); + translate([chablidw/2, chabd/2, -1]) + cylinder(r=chablidholed/2, h=chablidt+2, $fn=20); + } + } + translate([chabwallt-0.1, -chabd, 0]) { + difference(){ + cube([chabbasew, chabd-0.1, chabbaset]); + translate([chabbasew/2, (chabd-chabwebt)/2, 0]) + rotate([0,0,90]) + slothole(baseholesz, chabslot, baset, slop=baseholeslop); + } + } + rotate([90,0,0]) linear_extrude(height=chabwebt) { + polygon([[-chablidw, chabinnerh], + [-chablidw, chablidt+chabinnerh], + [-chabwebh, chablidt+chabinnerh+chabwebh], + [+chabwebh, chablidt+chabinnerh+chabwebh], + [+chabbasew, chabbaset], + [+chabbasew, 0], + [0, 0], + [0, chabinnerh]]); + } +} + +module ChannelBracketT(){ + rotate([-90,0,0]) ChannelBracket(); +} + +module Kit(){ ////toplevel + for (y=[0, -wallh-5]) { + translate([0,y,0]) { + translate([5,0,0]) BracketR(); + BracketL(); + } + } +} + +//Kit(); +//BracketR(); +//RearCableProtectorT(); +//ChannelBracketT(); diff --git a/y-large-axlebar-washer.scad b/y-large-axlebar-washer.scad new file mode 100644 index 0000000..de8553e --- /dev/null +++ b/y-large-axlebar-washer.scad @@ -0,0 +1,15 @@ +// -*- C -*- + +$fa=3; +$fs=0.1; + +r0 = 12 + 0.75; +r1 = 22; +h = 7.36 - 0.20; + +linear_extrude(height=h, convexity=3) { + difference(){ + circle(r = r1/2); + circle(r = r0/2); + } +} diff --git a/yubikey-5c-nano-loop.scad b/yubikey-5c-nano-loop.scad new file mode 100644 index 0000000..654e11c --- /dev/null +++ b/yubikey-5c-nano-loop.scad @@ -0,0 +1,74 @@ +// -*- C -*- + +include + +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();