led_pillar

Documentation: http://frombelow.net/projects/led_pillar/
Clone: git clone https://git.frombelow.net/led_pillar.git
Log | Files | Refs | Submodules | README | LICENSE

commit 4cb92b95c1b8e57c9b8e33099fd1cb79dde8aac9
parent fafde29d9b748343e1443cda457be85334d81e13
Author: Gerd Beuster <gerd@frombelow.net>
Date:   Fri, 12 Feb 2021 17:52:47 +0100

PyIgnition & documentation updated

Diffstat:
A.gitattributes | 2++
M.gitignore | 1+
A.gitmodules | 3+++
ACOPYING | 674+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APyIgnition | 1+
AREADME.md | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DREADME.txt | 25-------------------------
Mmain.py | 1-
Dnotes.txt | 13-------------
Mpatterns.py | 10+++++++---
Dpyignition/PyIgnition.py | 731-------------------------------------------------------------------------------
Dpyignition/constants.py | 29-----------------------------
Dpyignition/gravity.py | 197-------------------------------------------------------------------------------
Dpyignition/interpolate.py | 77-----------------------------------------------------------------------------
Dpyignition/keyframes.py | 61-------------------------------------------------------------
Dpyignition/obstacles.py | 404-------------------------------------------------------------------------------
Dpyignition/particles.py | 200-------------------------------------------------------------------------------
Dpyignition/xml.py | 137-------------------------------------------------------------------------------
Aschematic/led_pillar/led_pillar-cache.lib | 192+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aschematic/led_pillar/led_pillar.pro | 33+++++++++++++++++++++++++++++++++
Aschematic/led_pillar/led_pillar.sch | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtemplates/index.html | 14+++++++-------
22 files changed, 1182 insertions(+), 1885 deletions(-)

diff --git a/.gitattributes b/.gitattributes @@ -0,0 +1,2 @@ +*.pro filter=kicad_project +*.sch filter=kicad_sch diff --git a/.gitignore b/.gitignore @@ -1,2 +1,3 @@ pyignition/__pycache__/ **/*.pyc +schematic/led_pillar/led_pillar.sch-bak diff --git a/.gitmodules b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "PyIgnition"] + path = PyIgnition + url = https://git.frombelow.net/PyIgnition.git/ diff --git a/COPYING b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<https://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<https://www.gnu.org/licenses/why-not-lgpl.html>. diff --git a/PyIgnition b/PyIgnition @@ -0,0 +1 @@ +Subproject commit 63c4b079c422ace2129ae7a81d5f35cb790628ae diff --git a/README.md b/README.md @@ -0,0 +1,98 @@ +This projects shows animations on a strip of WS281x LEDs wrapped +around a pipe. A Raspberry Pi Zero WH computes the animations +and provides a web interface. I got inspiration for this project from +stuff I saw in the Blinkenlights area at some Chaos Communication +Congress. + +## Hardware Setup + +For this project, a WS281x LED strip is wrapped around a cylindrical +object creating a pillar of LEDs. The data line of the LED strip is +connected to ta Raspberry Pi Zero WH (see software configuration below +for setting the GPIO pin). In the video, a 5 m LED strip with 144 +LEDs/m was wrapped around a drain pipe with 7 cm diameter, resulting +in a pillar height of approx. 26.5 cm. + +WARNING: When running at full power, the pillar gets uncomfortably +warm. Therefore I chained a 5 A fuse into the power line. I strongly +recommend you do the same in your design in order to prevent your +construction from intentional or non-intentional overheating! + +EXTRA WARNING: There seems to be a bug in the SPI part of the WS281x +driver. Even when set to low intensity, the driver may set all LEDs to +full power. This may result in a fire hazard. Seriously, chain a fuse +in the power line! + +Due to the 5 A fuse, the whole system draws around 26 W max. Thus the +110 W power supply is ridiculously overpowered. When you rebuild the +system you may want to use a power supply with less power. + +## Software Setup + +Clone the repository including submodules into your home directory: + +`git clone --recurse-submodules https://git.frombelow.net/led_pillar.git` + +Install required packages: + +`apt-get -y install python3-flask` +`apt-get -y install python3-pip python3-rpi.gpio` +`apt-get -y install python3-pygame xvfb` +`pip3 install rpi_ws281x` + +All configuration options are defined by variables at the top of +main.py. Since the LED strip wraps around the pillar, it may be tricky +to determine the correct width and length of the LED field. To find +the correct values, run the system with a reasonable guess and choose +animation "Test Pattern". Adjust the width and height parameters until +the animation approximates a vertical and a horizontal line. + +Fire up the LED pillar manually with + +`sudo FLASK_APP=main.py flask run --host=0.0.0.0` + +To start it automatically, add + +`cd /home/pi/led_pillar/; FLASK_APP=main.py nohup flask run --host=0.0.0.0 &` + +to your `/etc/rc.local`. + +The web interface is accessible at `http://localhost:5000/` + +## Printing a Case + +Directory `case/` contains an OpenSCAD file for 3D printing a case for +the power supply and the Raspberry Pi. + +## BUGS + +When connecting LED strip via SPI (Pin 10), some patterns turn the LED +strip on with full power. Do not use! Chain a 5 A fuse in the power line of +LED strip to prevent overheating! + +## Changelog + +- v1.0 +First public version. + +## Copyright and License + +Copyright © 2015 David Barker for submodule PyIgnition +<https://github.com/animatinator/PyIgnition>. Gerd Beuster updated +PyIgnition for Python 3: <https://git.frombelow.net/PyIgnition/> + +© 2021 Gerd Beuster <gerd@frombelow.net> for everything else. The +following applies to everything but PyIgnition: + +This project is free soft- and hardware: 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 project 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 project. If not, see <http://www.gnu.org/licenses/>. diff --git a/README.txt b/README.txt @@ -1,25 +0,0 @@ -Installation -============ - -The following packages are required: -python3-flask python3-pip python3-rpi.gpio python3-pygame xvfb - -Run as root: -pip3 install rpi_ws281x - -Running -======= - -Start manually with -sudo FLASK_APP=main.py flask run --host=0.0.0.0 - -To start automatically, add -cd /home/pi/20191224_raspi_ws2811/; FLASK_APP=main.py nohup flask run --host=0.0.0.0 & -to rc.local. - -BUGS -==== - -When connecting LED strip via SPI (Pin 10), some patterns turn the LED -strip on with full power. Do not use! Add a 5 A fuse in power line of -LED strip! diff --git a/main.py b/main.py @@ -4,7 +4,6 @@ from multiprocessing import Queue, Process from flask import Flask, render_template, request from rpi_ws281x import PixelStrip, Color import math -import pdb # Patterns import patterns diff --git a/notes.txt b/notes.txt @@ -1,13 +0,0 @@ -sudo sh -c "echo blacklist snd_bcm2835 > /etc/modprobe.d/snd-blacklist.conf" -# In /boot/config.txt auskommentieren: -# dtparam=audio=on -# /boot/cmdline.txt hinzufügen: -spidev.bufsiz=32768 -sudo apt-get install gcc make build-essential python-dev git scons swig -git clone https://github.com/jgarff/rpi_ws281x -cd rpi_ws281x/ -sudo scons -cd python -sudo python setup.py build -sudo python setup.py install - diff --git a/patterns.py b/patterns.py @@ -8,11 +8,15 @@ import time from rpi_ws281x import PixelStrip, Color import random import sys -sys.path.append('pyignition') -import PyIgnition, pygame, os +sys.path.append('PyIgnition') +try: + import PyIgnition, pygame, os +except: + sys.exit("""\ +PyIgnition not found. Please execute +git submodule update --init --recursive""") import subprocess import math -import pdb class Pattern(): diff --git a/pyignition/PyIgnition.py b/pyignition/PyIgnition.py @@ -1,731 +0,0 @@ -### EXESOFT PYIGNITION ### -# Copyright David Barker 2010 -# -# Particle effect manager - - -import particles, gravity, obstacles, constants, sys, pygame, xml -from constants import * - - -class ParticleEffect: - def __init__(self, display, pos = (0, 0), size = (0, 0)): - self.display = display - self.pos = pos - self.size = size - - self.left = pos[0] - self.top = pos[1] - self.right = pos[0] + size[0] - self.bottom = pos[1] + size[1] - - self.particles = [] - self.sources = [] - self.gravities = [] - self.obstacles = [] - - def Update(self): - for source in self.sources: - source.Update() - - for gravity in self.gravities: - gravity.Update() - - for obstacle in self.obstacles: - obstacle.Update() - - for particle in self.particles: - radius = 0.0 # Used for making sure radii don't overlap objects... - - if particle.drawtype == DRAWTYPE_CIRCLE or particle.drawtype == DRAWTYPE_BUBBLE: - radius = particle.radius * (1.0 - RADIUS_PERMITTIVITY) # ...But only set if the particle is actually circular - - # First calculate the forces acting on the particle - totalforce = [0.0, 0.0] - - for gravity in self.gravities: - force = gravity.GetForceOnParticle(particle) - totalforce[0] += force[0] - totalforce[1] += force[1] - - for obstacle in self.obstacles: - force = obstacle.GetForce(particle.pos, particle.velocity, radius) - totalforce[0] += force[0] - totalforce[1] += force[1] - - # Apply the forces to the velocity and update the particle - particle.velocity = [particle.velocity[0] + totalforce[0], particle.velocity[1] + totalforce[1]] - - particle.Update() - - # Resolve collisions - for obstacle in self.obstacles: - if (not obstacle.OutOfRange(particle.pos)) and (obstacle.InsideObject(particle.pos, radius)): - particle.pos = obstacle.GetResolved(particle.pos, radius) - - # Delete dead particles - for particle in self.particles: - if not particle.alive: - self.particles.remove(particle) - - def Redraw(self): - for particle in self.particles: - particle.Draw(self.display) - - for obstacle in self.obstacles: - obstacle.Draw(self.display) - - def CreateSource(self, pos = (0, 0), initspeed = 0.0, initdirection = 0.0, initspeedrandrange = 0.0, initdirectionrandrange = 0.0, particlesperframe = 0, particlelife = -1, genspacing = 0, drawtype = 0, colour = (0, 0, 0), radius = 0.0, length = 0.0, imagepath = None): - newsource = particles.ParticleSource(self, pos, initspeed, initdirection, initspeedrandrange, initdirectionrandrange, particlesperframe, particlelife, genspacing, drawtype, colour, radius, length, imagepath) - self.sources.append(newsource) - return newsource # Effectively a reference - - def CreatePointGravity(self, strength = 0.0, strengthrandrange = 0.0, pos = (0, 0)): - newgrav = gravity.PointGravity(strength, strengthrandrange, pos) - self.gravities.append(newgrav) - return newgrav - - def CreateDirectedGravity(self, strength = 0.0, strengthrandrange = 0.0, direction = [0, 1]): - newgrav = gravity.DirectedGravity(strength, strengthrandrange, direction) - self.gravities.append(newgrav) - return newgrav - - def CreateVortexGravity(self, strength = 0.0, strengthrandrange = 0.0, pos = (0, 0)): - newgrav = gravity.VortexGravity(strength, strengthrandrange, pos) - self.gravities.append(newgrav) - return newgrav - - def CreateCircle(self, pos = (0, 0), colour = (0, 0, 0), bounce = 1.0, radius = 0.0): - newcircle = obstacles.Circle(self, pos, colour, bounce, radius) - self.obstacles.append(newcircle) - return newcircle - - def CreateRectangle(self, pos = (0, 0), colour = (0, 0, 0), bounce = 1.0, width = 0.0, height = 0.0): - newrect = obstacles.Rectangle(self, pos, colour, bounce, width, height) - self.obstacles.append(newrect) - return newrect - - def CreateBoundaryLine(self, pos = (0, 0), colour = (0, 0, 0), bounce = 1.0, normal = [0, 1]): - newline = obstacles.BoundaryLine(self, pos, colour, bounce, normal) - self.obstacles.append(newline) - return newline - - def AddParticle(self, particle): - self.particles.append(particle) - - def GetDrawtypeAsString(self, drawtype): - if drawtype == DRAWTYPE_POINT: - return "point" - elif drawtype == DRAWTYPE_CIRCLE: - return "circle" - elif drawtype == DRAWTYPE_LINE: - return "line" - elif drawtype == DRAWTYPE_SCALELINE: - return "scaleline" - elif drawtype == DRAWTYPE_BUBBLE: - return "bubble" - elif drawtype == DRAWTYPE_IMAGE: - return "image" - else: - return "ERROR: Invalid drawtype" - - def GetStringAsDrawtype(self, string): - if string == "point": - return DRAWTYPE_POINT - elif string == "circle": - return DRAWTYPE_CIRCLE - elif string == "line": - return DRAWTYPE_LINE - elif string == "scaleline": - return DRAWTYPE_SCALELINE - elif string == "bubble": - return DRAWTYPE_BUBBLE - elif string == "image": - return DRAWTYPE_IMAGE - else: - return DRAWTYPE_POINT - - def GetInterpolationtypeAsString(self, interpolationtype): - if interpolationtype == INTERPOLATIONTYPE_LINEAR: - return "linear" - elif interpolationtype == INTERPOLATIONTYPE_COSINE: - return "cosine" - - def GetStringAsInterpolationtype(self, string): - if string == "linear": - return INTERPOLATIONTYPE_LINEAR - elif string == "cosine": - return INTERPOLATIONTYPE_COSINE - else: - return INTERPOLATIONTYPE_LINEAR - - def TranslatePos(self, pos): - return (pos[0] - self.pos[0], pos[1] - self.pos[1]) - - def ConvertXMLTuple(self, string): - # 'string' must be of the form "(value, value, value, [...])" - bracketless = string.replace("(", "").replace(")", "") - strings = bracketless.split(", ") - finaltuple = [] - for string in strings: - temp = string.split(".") - if len(temp) > 1: - finaltuple.append(float(string)) - else: - finaltuple.append(int(string)) - - return tuple(finaltuple) - - def SaveToFile(self, outfilename): - outfile = open(outfilename, 'w') - - outfile.write("<?xml version = \"1.0\"?>\n<?pyignition version = \"%f\"?>\n\n" % PYIGNITION_VERSION) - outfile.write("<effect>\n") - - # Write out sources - for source in self.sources: - outfile.write("\t<source>\n") - - # Write out source variables - outfile.write("\t\t<pos>(%i, %i)</pos>\n" % source.pos) - outfile.write("\t\t<initspeed>%f</initspeed>\n" % source.initspeed) - outfile.write("\t\t<initdirection>%f</initdirection>\n" % source.initdirection) - outfile.write("\t\t<initspeedrandrange>%f</initspeedrandrange>\n" % source.initspeedrandrange) - outfile.write("\t\t<initdirectionrandrange>%f</initdirectionrandrange>\n" % source.initdirectionrandrange) - outfile.write("\t\t<particlesperframe>%i</particlesperframe>\n" % source.particlesperframe) - outfile.write("\t\t<particlelife>%i</particlelife>\n" % source.particlelife) - outfile.write("\t\t<genspacing>%i</genspacing>\n" % source.genspacing) - outfile.write("\t\t<drawtype>%s</drawtype>\n" % self.GetDrawtypeAsString(source.drawtype)) - outfile.write("\t\t<colour>(%i, %i, %i)</colour>\n" % source.colour) - outfile.write("\t\t<radius>%f</radius>\n" % source.radius) - outfile.write("\t\t<length>%f</length>\n" % source.length) - outfile.write("\t\t<imagepath>%s</imagepath>\n" % source.imagepath) - - # Write out source keyframes - outfile.write("\t\t<keyframes>\n") - - for keyframe in source.keyframes: - if keyframe.frame == 0: # Don't bother writing out the first keyframe - continue - - outfile.write("\t\t\t<keyframe frame = \"%i\">\n" % keyframe.frame) - - # Write out keyframed variables - for variable in keyframe.variables.keys(): - if variable == "interpolationtype": - outfile.write("\t\t\t\t<%s>%s</%s>\n" % (variable, self.GetInterpolationtypeAsString(keyframe.variables[variable]), variable)) - else: - outfile.write("\t\t\t\t<%s>%s</%s>\n" % (variable, str(keyframe.variables[variable]), variable)) - - outfile.write("\t\t\t</keyframe>\n") - - outfile.write("\t\t</keyframes>\n") - - # Write out source particle keyframes - outfile.write("\t\t<particlekeyframes>\n") - - for keyframe in source.particlekeyframes: - if keyframe.frame == 0: # Don't bother writing out the first keyframe - continue - - outfile.write("\t\t\t<keyframe frame = \"%i\">\n" % keyframe.frame) - - # Write out keyframed variables - for variable in keyframe.variables.keys(): - if variable == "interpolationtype": - outfile.write("\t\t\t\t<%s>%s</%s>\n" % (variable, self.GetInterpolationtypeAsString(keyframe.variables[variable]), variable)) - else: - outfile.write("\t\t\t\t<%s>%s</%s>\n" % (variable, str(keyframe.variables[variable]), variable)) - - outfile.write("\t\t\t</keyframe>\n") - - outfile.write("\t\t</particlekeyframes>\n") - - outfile.write("\t</source>\n\n") - - # Write out gravities - for gravity in self.gravities: - # Identify type - gtype = gravity.type - - outfile.write("\t<%sgravity>\n" % gtype) - - # Write out gravity variables - outfile.write("\t\t<strength>%f</strength>\n" % gravity.initstrength) - outfile.write("\t\t<strengthrandrange>%f</strengthrandrange>\n" % gravity.strengthrandrange) - if gtype == "directed": - outfile.write("\t\t<direction>(%f, %f)</direction>\n" % tuple(gravity.direction)) - elif gtype == "point" or gtype == "vortex": - outfile.write("\t\t<pos>(%i, %i)</pos>\n" % gravity.pos) - - # Write out gravity keyframes - outfile.write("\t\t<keyframes>\n") - - for keyframe in gravity.keyframes: - if keyframe.frame == 0: # Don't bother writing out the first keyframe - continue - - outfile.write("\t\t\t<keyframe frame = \"%i\">\n" % keyframe.frame) - - # Write out keyframed variables - for variable in keyframe.variables.keys(): - if variable == "interpolationtype": - outfile.write("\t\t\t\t<%s>%s</%s>\n" % (variable, self.GetInterpolationtypeAsString(keyframe.variables[variable]), variable)) - else: - outfile.write("\t\t\t\t<%s>%s</%s>\n" % (variable, str(keyframe.variables[variable]), variable)) - - outfile.write("\t\t\t</keyframe>\n") - - outfile.write("\t\t</keyframes>\n") - - outfile.write("\t</%sgravity>\n\n" % gtype) - - # Write out obstacles - for obstacle in self.obstacles: - # Identify type - otype = obstacle.type - - outfile.write("\t<%s>\n" % otype) - - # Write out obstacle variables - outfile.write("\t\t<pos>(%i, %i)</pos>\n" % obstacle.pos) - outfile.write("\t\t<colour>(%i, %i, %i)</colour>\n" % obstacle.colour) - outfile.write("\t\t<bounce>%f</bounce>\n" % obstacle.bounce) - if otype == "circle": - outfile.write("\t\t<radius>%f</radius>\n" % obstacle.radius) - elif otype == "rectangle": - outfile.write("\t\t<width>%i</width>\n" % obstacle.width) - outfile.write("\t\t<height>%i</height>\n" % obstacle.height) - elif otype == "boundaryline": - outfile.write("\t\t<normal>(%f, %f)</normal>\n" % tuple(obstacle.normal)) - - # Write out obstacle keyframes - outfile.write("\t\t<keyframes>\n") - - for keyframe in obstacle.keyframes: - if keyframe.frame == 0: # Don't bother writing out the first keyframe - continue - - outfile.write("\t\t\t<keyframe frame = \"%i\">\n" % keyframe.frame) - - # Write out keyframed variables - for variable in keyframe.variables.keys(): - if variable == "interpolationtype": - outfile.write("\t\t\t\t<%s>%s</%s>\n" % (variable, self.GetInterpolationtypeAsString(keyframe.variables[variable]), variable)) - else: - outfile.write("\t\t\t\t<%s>%s</%s>\n" % (variable, str(keyframe.variables[variable]), variable)) - - outfile.write("\t\t\t</keyframe>\n") - - outfile.write("\t\t</keyframes>\n") - - outfile.write("\t</%s>\n\n" % otype) - - outfile.write("</effect>") - outfile.close() - - def LoadFromFile(self, infilename): - infile = open(infilename, "r") - - data = xml.XMLParser(infile.read()).Parse() - infile.close() - - for child in data.children: - if child.tag == "source": # Source object - pos = (0, 0) - initspeed = 0.0 - initdirection = 0.0 - initspeedrandrange = 0.0 - initdirectionrandrange = 0.0 - particlesperframe = 0 - particlelife = 0 - genspacing = 0 - drawtype = DRAWTYPE_POINT - colour = (0, 0, 0) - radius = 0.0 - length = 0.0 - imagepath = None - - keyframes = None - particlekeyframes = None - - for parameter in child.children: - if parameter.tag == "pos": - pos = self.ConvertXMLTuple(parameter.inside) - elif parameter.tag == "initspeed": - initspeed = float(parameter.inside) - elif parameter.tag == "initdirection": - initdirection = float(parameter.inside) - elif parameter.tag == "initspeedrandrange": - initspeedrandrange = float(parameter.inside) - elif parameter.tag == "initdirectionrandrange": - initdirectionrandrange = float(parameter.inside) - elif parameter.tag == "particlesperframe": - particlesperframe = int(parameter.inside) - elif parameter.tag == "particlelife": - particlelife = int(parameter.inside) - elif parameter.tag == "genspacing": - genspacing = int(parameter.inside) - elif parameter.tag == "drawtype": - drawtype = self.GetStringAsDrawtype(parameter.inside) - elif parameter.tag == "colour": - colour = self.ConvertXMLTuple(parameter.inside) - elif parameter.tag == "radius": - radius = float(parameter.inside) - elif parameter.tag == "length": - length = float(parameter.inside) - elif parameter.tag == "image": - imagepath = float(parameter.inside) - elif parameter.tag == "keyframes": - keyframes = parameter.children - elif parameter.tag == "particlekeyframes": - particlekeyframes = parameter.children - - newsource = self.CreateSource(pos, initspeed, initdirection, initspeedrandrange, initdirectionrandrange, particlesperframe, particlelife, genspacing, drawtype, colour, radius, length, imagepath) - - for keyframe in keyframes: - frame = int(keyframe.meta['frame']) - variables = {} - - for variable in keyframe.children: - if variable.tag == "pos_x" and variable.inside != "None": - variables['pos_x'] = int(variable.inside) - elif variable.tag == "pos_y" and variable.inside != "None": - variables['pos_y'] = int(variable.inside) - elif variable.tag == "initspeed" and variable.inside != "None": - variables['initspeed'] = float(variable.inside) - elif variable.tag == "initdirection" and variable.inside != "None": - variables['initdirection'] = float(variable.inside) - elif variable.tag == "initspeedrandrange" and variable.inside != "None": - variables['initspeedrandrange'] = float(variable.inside) - elif variable.tag == "initdirectionrandrange" and variable.inside != "None": - variables['initdirectionrandrange'] = float(variable.inside) - elif variable.tag == "particlesperframe" and variable.inside != "None": - variables['particlesperframe'] = int(variable.inside) - elif variable.tag == "genspacing" and variable.inside != "None": - variables['genspacing'] = int(variable.inside) - elif variable.tag == "interpolationtype" and variable.inside != "None": - variables['interpolationtype'] = self.GetStringAsInterpolationtype(variable.inside) - - newframe = newsource.CreateKeyframe(frame = frame) - newframe.variables = variables - - for keyframe in particlekeyframes: - frame = int(keyframe.meta['frame']) - variables = {} - - for variable in keyframe.children: - if variable.tag == "colour_r" and variable.inside != "None": - variables['colour_r'] = int(variable.inside) - elif variable.tag == "colour_g" and variable.inside != "None": - variables['colour_g'] = int(variable.inside) - elif variable.tag == "colour_b" and variable.inside != "None": - variables['colour_b'] = int(variable.inside) - elif variable.tag == "radius" and variable.inside != "None": - variables['radius'] = float(variable.inside) - elif variable.tag == "length" and variable.inside != "None": - variables['length'] = float(variable.inside) - elif variable.tag == "interpolationtype" and variable.inside != "None": - variables['interpolationtype'] = self.GetStringAsInterpolationtype(variable.inside) - - newframe = newsource.CreateParticleKeyframe(frame = frame) - newframe.variables = variables - newsource.PreCalculateParticles() - - elif child.tag == "directedgravity": - strength = 0.0 - strengthrandrange = 0.0 - direction = [0, 0] - - keyframes = None - - for parameter in child.children: - if parameter.tag == "strength": - strength = float(parameter.inside) - elif parameter.tag == "strengthrandrange": - strengthrandrange = float(parameter.inside) - elif parameter.tag == "direction": - direction = self.ConvertXMLTuple(parameter.inside) - elif parameter.tag == "keyframes": - keyframes = parameter.children - - newgrav = self.CreateDirectedGravity(strength, strengthrandrange, direction) - - for keyframe in keyframes: - frame = int(keyframe.meta['frame']) - variables = {} - - for variable in keyframe.children: - if variable.tag == "strength" and variable.inside != "None": - variables['strength'] = float(variable.inside) - elif variable.tag == "strengthrandrange" and variable.inside != "None": - variables['strengthrandrange'] = float(variable.inside) - elif variable.tag == "direction_x" and variable.inside != "None": - variables['direction_x'] = float(variable.inside) - elif variable.tag == "direction_y" and variable.inside != "None": - variables['direction_y'] = float(variable.inside) - elif variable.tag == "interpolationtype" and variable.inside != "None": - variables['interpolationtype'] = self.GetStringAsInterpolationtype(variable.inside) - - newframe = newgrav.CreateKeyframe(frame = frame) - newframe.variables = variables - - elif child.tag == "pointgravity": - strength = 0.0 - strengthrandrange = 0.0 - pos = (0, 0) - - keyframes = None - - for parameter in child.children: - if parameter.tag == "strength": - strength = float(parameter.inside) - elif parameter.tag == "strengthrandrange": - strengthrandrange = float(parameter.inside) - elif parameter.tag == "pos": - pos = self.ConvertXMLTuple(parameter.inside) - elif parameter.tag == "keyframes": - keyframes = parameter.children - - newgrav = self.CreatePointGravity(strength, strengthrandrange, pos) - - for keyframe in keyframes: - frame = int(keyframe.meta['frame']) - variables = {} - - for variable in keyframe.children: - if variable.tag == "strength" and variable.inside != "None": - variables['strength'] = float(variable.inside) - elif variable.tag == "strengthrandrange" and variable.inside != "None": - variables['strengthrandrange'] = float(variable.inside) - elif variable.tag == "pos_x" and variable.inside != "None": - variables['pos_x'] = int(variable.inside) - elif variable.tag == "pos_y" and variable.inside != "None": - variables['pos_y'] = int(variable.inside) - elif variable.tag == "interpolationtype" and variable.inside != "None": - variables['interpolationtype'] = self.GetStringAsInterpolationtype(variable.inside) - - newframe = newgrav.CreateKeyframe(frame = frame) - newframe.variables = variables - - elif child.tag == "vortexgravity": - strength = 0.0 - strengthrandrange = 0.0 - pos = (0, 0) - - keyframes = None - - for parameter in child.children: - if parameter.tag == "strength": - strength = float(parameter.inside) - elif parameter.tag == "strengthrandrange": - strengthrandrange = float(parameter.inside) - elif parameter.tag == "pos": - direction = self.ConvertXMLTuple(parameter.inside) - elif parameter.tag == "keyframes": - keyframes = parameter.children - - newgrav = self.CreateVortexGravity(strength, strengthrandrange, pos) - - for keyframe in keyframes: - frame = int(keyframe.meta['frame']) - variables = {} - - for variable in keyframe.children: - if variable.tag == "strength" and variable.inside != "None": - variables['strength'] = float(variable.inside) - elif variable.tag == "strengthrandrange" and variable.inside != "None": - variables['strengthrandrange'] = float(variable.inside) - elif variable.tag == "pos_x" and variable.inside != "None": - variables['pos_x'] = int(variable.inside) - elif variable.tag == "pos_y" and variable.inside != "None": - variables['pos_y'] = int(variable.inside) - elif variable.tag == "interpolationtype" and variable.inside != "None": - variables['interpolationtype'] = self.GetStringAsInterpolationtype(variable.inside) - - newframe = newgrav.CreateKeyframe(frame = frame) - newframe.variables = variables - - elif child.tag == "circle": - pos = (0, 0) - colour = (0, 0, 0) - bounce = 0.0 - radius = 0.0 - - keyframes = None - - for parameter in child.children: - if parameter.tag == "pos": - pos = self.ConvertXMLTuple(parameter.inside) - elif parameter.tag == "colour": - colour = self.ConvertXMLTuple(parameter.inside) - elif parameter.tag == "bounce": - bounce = float(parameter.inside) - elif parameter.tag == "radius": - radius = float(parameter.inside) - elif parameter.tag == "keyframes": - keyframes = parameter.children - - newobstacle = self.CreateCircle(pos, colour, bounce, radius) - - for keyframe in keyframes: - frame = int(keyframe.meta['frame']) - variables = {} - - for variable in keyframe.children: - if variable.tag == "pos_x" and variable.inside != "None": - variables['pos_x'] = int(variable.inside) - elif variable.tag == "pos_y" and variable.inside != "None": - variables['pos_y'] = int(variable.inside) - elif variable.tag == "colour_r" and variable.inside != "None": - variables['colour_r'] = int(variable.inside) - elif variable.tag == "colour_g" and variable.inside != "None": - variables['colour_g'] = int(variable.inside) - elif variable.tag == "colour_b" and variable.inside != "None": - variables['colour_b'] = int(variable.inside) - elif variable.tag == "bounce" and variable.inside != "None": - variables['bounce'] = float(variable.inside) - elif variable.tag == "radius" and variable.inside != "None": - variables['radius'] = float(variable.inside) - elif variable.tag == "interpolationtype" and variable.inside != "None": - variables['interpolationtype'] = self.GetStringAsInterpolationtype(variable.inside) - - newframe = newobstacle.CreateKeyframe(frame = frame) - newframe.variables = variables - - elif child.tag == "rectangle": - pos = (0, 0) - colour = (0, 0, 0) - bounce = 0.0 - width = 0.0 - height = 0.0 - - keyframes = None - - for parameter in child.children: - if parameter.tag == "pos": - pos = self.ConvertXMLTuple(parameter.inside) - elif parameter.tag == "colour": - colour = self.ConvertXMLTuple(parameter.inside) - elif parameter.tag == "bounce": - bounce = float(parameter.inside) - elif parameter.tag == "width": - width = float(parameter.inside) - elif parameter.tag == "height": - height = float(parameter.inside) - elif parameter.tag == "keyframes": - keyframes = parameter.children - - newobstacle = self.CreateRectangle(pos, colour, bounce, width, height) - - for keyframe in keyframes: - frame = int(keyframe.meta['frame']) - variables = {} - - for variable in keyframe.children: - if variable.tag == "pos_x" and variable.inside != "None": - variables['pos_x'] = int(variable.inside) - elif variable.tag == "pos_y" and variable.inside != "None": - variables['pos_y'] = int(variable.inside) - elif variable.tag == "colour_r" and variable.inside != "None": - variables['colour_r'] = int(variable.inside) - elif variable.tag == "colour_g" and variable.inside != "None": - variables['colour_g'] = int(variable.inside) - elif variable.tag == "colour_b" and variable.inside != "None": - variables['colour_b'] = int(variable.inside) - elif variable.tag == "bounce" and variable.inside != "None": - variables['bounce'] = float(variable.inside) - elif variable.tag == "width" and variable.inside != "None": - variables['width'] = float(variable.inside) - elif variable.tag == "height" and variable.inside != "None": - variables['height'] = float(variable.inside) - elif variable.tag == "interpolationtype" and variable.inside != "None": - variables['interpolationtype'] = self.GetStringAsInterpolationtype(variable.inside) - - newframe = newobstacle.CreateKeyframe(frame = frame) - newframe.variables = variables - - elif child.tag == "boundaryline": - pos = (0, 0) - colour = (0, 0, 0) - bounce = 0.0 - direction = [0.0, 0.0] - - keyframes = None - - for parameter in child.children: - if parameter.tag == "pos": - pos = self.ConvertXMLTuple(parameter.inside) - elif parameter.tag == "colour": - colour = self.ConvertXMLTuple(parameter.inside) - elif parameter.tag == "bounce": - bounce = float(parameter.inside) - elif parameter.tag == "normal": - normal = self.ConvertXMLTuple(parameter.inside) - elif parameter.tag == "keyframes": - keyframes = parameter.children - - newobstacle = self.CreateBoundaryLine(pos, colour, bounce, normal) - - for keyframe in keyframes: - frame = int(keyframe.meta['frame']) - variables = {} - - for variable in keyframe.children: - if variable.tag == "pos_x" and variable.inside != "None": - variables['pos_x'] = int(variable.inside) - elif variable.tag == "pos_y" and variable.inside != "None": - variables['pos_y'] = int(variable.inside) - elif variable.tag == "colour_r" and variable.inside != "None": - variables['colour_r'] = int(variable.inside) - elif variable.tag == "colour_g" and variable.inside != "None": - variables['colour_g'] = int(variable.inside) - elif variable.tag == "colour_b" and variable.inside != "None": - variables['colour_b'] = int(variable.inside) - elif variable.tag == "bounce" and variable.inside != "None": - variables['bounce'] = float(variable.inside) - elif variable.tag == "normal_x" and variable.inside != "None": - variables['normal_x'] = float(variable.inside) - elif variable.tag == "normal_y" and variable.inside != "None": - variables['normal_y'] = float(variable.inside) - elif variable.tag == "interpolationtype" and variable.inside != "None": - variables['interpolationtype'] = self.GetStringAsInterpolationtype(variable.inside) - - newframe = newobstacle.CreateKeyframe(frame = frame) - newframe.variables = variables - - -## Begin testing code -if __name__ == '__main__': - screen = pygame.display.set_mode((800, 600)) - pygame.display.set_caption("PyIgnition demo") - clock = pygame.time.Clock() - test = ParticleEffect(screen, (0, 0), (800, 600)) - testgrav = test.CreatePointGravity(strength = 1.0, pos = (500, 380)) - testgrav.CreateKeyframe(300, strength = 10.0, pos = (0, 0)) - testgrav.CreateKeyframe(450, strength = 10.0, pos = (40, 40)) - testgrav.CreateKeyframe(550, strength = -2.0, pos = (600, 480)) - testgrav.CreateKeyframe(600, strength = -20.0, pos = (600, 0)) - testgrav.CreateKeyframe(650, strength = 1.0, pos = (500, 380)) - anothertestgrav = test.CreateDirectedGravity(strength = 0.04, direction = [1, 0]) - anothertestgrav.CreateKeyframe(300, strength = 1.0, direction = [-0.5, 1]) - anothertestgrav.CreateKeyframe(600, strength = 1.0, direction = [1.0, -0.1]) - anothertestgrav.CreateKeyframe(650, strength = 0.04, direction = [1, 0]) - testsource = test.CreateSource((10, 10), initspeed = 5.0, initdirection = 2.35619449, initspeedrandrange = 2.0, initdirectionrandrange = 1.0, particlesperframe = 5, particlelife = 125, drawtype = DRAWTYPE_SCALELINE, colour = (255, 255, 255), length = 10.0) - testsource.CreateParticleKeyframe(50, colour = (0, 255, 0), length = 10.0) - testsource.CreateParticleKeyframe(75, colour = (255, 255, 0), length = 10.0) - testsource.CreateParticleKeyframe(100, colour = (0, 255, 255), length = 10.0) - testsource.CreateParticleKeyframe(125, colour = (0, 0, 0), length = 10.0) - - test.SaveToFile("PyIgnition test.ppe") - - while True: - for event in pygame.event.get(): - if event.type == pygame.QUIT: - sys.exit() - - screen.fill((0, 0, 0)) - test.Update() - test.Redraw() - pygame.display.update() - clock.tick(20) diff --git a/pyignition/constants.py b/pyignition/constants.py @@ -1,28 +0,0 @@ -### EXESOFT PYIGNITION ### -# Copyright David Barker 2010 -# -# Global constants module - - -# Which version is this? -PYIGNITION_VERSION = 1.0 - -# Drawtype constants -DRAWTYPE_POINT = 100 -DRAWTYPE_CIRCLE = 101 -DRAWTYPE_LINE = 102 -DRAWTYPE_SCALELINE = 103 -DRAWTYPE_BUBBLE = 104 -DRAWTYPE_IMAGE = 105 - -# Interpolation type constants -INTERPOLATIONTYPE_LINEAR = 200 -INTERPOLATIONTYPE_COSINE = 201 - -# Gravity constants -UNIVERSAL_CONSTANT_OF_MAKE_GRAVITY_LESS_STUPIDLY_SMALL = 1000.0 # Well, Newton got one to make it less stupidly large. -VORTEX_ACCELERATION = 0.01 # A tiny value added to the centripetal force exerted by vortex gravities to draw in particles -VORTEX_SWALLOWDIST = 20.0 # Particles closer than this will be swallowed up and regurgitated in the bit bucket - -# Fraction of radius which can go inside an object -RADIUS_PERMITTIVITY = 0.3- \ No newline at end of file diff --git a/pyignition/gravity.py b/pyignition/gravity.py @@ -1,197 +0,0 @@ -### EXESOFT PYIGNITION ### -# Copyright David Barker 2010 -# -# Gravity objects - - -from math import sqrt -import keyframes, interpolate, random -from constants import * - - -def RandomiseStrength(base, range): - return base + (float(random.randrange(int(-range * 100), int(range * 100))) / 100.0) - - -class DirectedGravity: - def __init__(self, strength = 0.0, strengthrandrange = 0.0, direction = [0, 1]): - self.type = "directed" - self.initstrength = strength - self.strength = strength - self.strengthrandrange = strengthrandrange - directionmag = sqrt(direction[0]**2 + direction[1]**2) - self.direction = [direction[0] / directionmag, direction[1] / directionmag] - - self.keyframes = [] - self.CreateKeyframe(0, self.strength, self.strengthrandrange, self.direction) - self.curframe = 0 - - def Update(self): - newvars = interpolate.InterpolateKeyframes(self.curframe, {'strength':self.initstrength, 'strengthrandrange':self.strengthrandrange, 'direction_x':self.direction[0], 'direction_y':self.direction[1]}, self.keyframes) - self.initstrength = newvars['strength'] - self.strengthrandrange = newvars['strengthrandrange'] - self.direction = [newvars['direction_x'], newvars['direction_y']] - - if self.strengthrandrange != 0.0: - self.strength = RandomiseStrength(self.initstrength, self.strengthrandrange) - - self.curframe = self.curframe + 1 - - def GetForce(self, pos): - force = [self.strength * self.direction[0], self.strength * self.direction[1]] - - return force - - def GetForceOnParticle(self, particle): - return self.GetForce(particle.pos) - - def CreateKeyframe(self, frame, strength = None, strengthrandrange = None, direction = [None, None], interpolationtype = INTERPOLATIONTYPE_LINEAR): - return keyframes.CreateKeyframe(self.keyframes, frame, {'strength':strength, 'strengthrandrange':strengthrandrange, 'direction_x':direction[0], 'direction_y':direction[1], 'interpolationtype':interpolationtype}) - - def ConsolidateKeyframes(self): - keyframes.ConsolidateKeyframes(self.keyframes, self.curframe, {'strength':self.initstrength, 'strengthrandrange':self.strengthrandrange, 'direction_x':self.direction[0], 'direction_y':self.direction[1]}) - - def SetStrength(self, newstrength): - self.CreateKeyframe(self.curframe, strength = newstrength) - - def SetStrengthRandRange(self, newstrengthrandrange): - self.CreateKeyframe(self.curframe, strengthrandrange = newstrengthrandrange) - - def SetDirection(self, newdirection): - self.CreateKeyframe(self.curframe, direction = newdirection) - - -class PointGravity: - def __init__(self, strength = 0.0, strengthrandrange = 0.0, pos = (0, 0)): - self.type = "point" - self.initstrength = strength - self.strength = strength - self.strengthrandrange = strengthrandrange - self.pos = pos - - self.keyframes = [] - self.CreateKeyframe(0, self.strength, self.strengthrandrange, self.pos) - self.curframe = 0 - - def Update(self): - newvars = interpolate.InterpolateKeyframes(self.curframe, {'strength':self.initstrength, 'strengthrandrange':self.strengthrandrange, 'pos_x':self.pos[0], 'pos_y':self.pos[1]}, self.keyframes) - self.initstrength = newvars['strength'] - self.strengthrandrange = newvars['strengthrandrange'] - self.pos = (newvars['pos_x'], newvars['pos_y']) - - if self.strengthrandrange != 0.0: - self.strength = RandomiseStrength(self.initstrength, self.strengthrandrange) - else: - self.strength = self.initstrength - - self.curframe = self.curframe + 1 - - def GetForce(self, pos): - distsquared = (pow(float(pos[0] - self.pos[0]), 2.0) + pow(float(pos[1] - self.pos[1]), 2.0)) - if distsquared == 0.0: - return [0.0, 0.0] - - forcemag = (self.strength * UNIVERSAL_CONSTANT_OF_MAKE_GRAVITY_LESS_STUPIDLY_SMALL) / (distsquared) - - # Calculate normal vector from pos to the gravity point and multiply by force magnitude to find force vector - dist = sqrt(distsquared) - dx = float(self.pos[0] - pos[0]) / dist - dy = float(self.pos[1] - pos[1]) / dist - - force = [forcemag * dx, forcemag * dy] - - return force - - def GetForceOnParticle(self, particle): - return self.GetForce(particle.pos) - - def GetMaxForce(self): - return self.strength * UNIVERSAL_CONSTANT_OF_MAKE_GRAVITY_LESS_STUPIDLY_SMALL - - def CreateKeyframe(self, frame, strength = None, strengthrandrange = None, pos = (None, None), interpolationtype = INTERPOLATIONTYPE_LINEAR): - return keyframes.CreateKeyframe(self.keyframes, frame, {'strength':strength, 'strengthrandrange':strengthrandrange, 'pos_x':pos[0], 'pos_y':pos[1], 'interpolationtype':interpolationtype}) - - def ConsolidateKeyframes(self): - keyframes.ConsolidateKeyframes(self.keyframes, self.curframe, {'strength':self.initstrength, 'strengthrandrange':self.strengthrandrange, 'pos_x':self.pos[0], 'pos_y':self.pos[1]}) - - def SetStrength(self, newstrength): - self.CreateKeyframe(self.curframe, strength = newstrength) - - def SetStrengthRandRange(self, newstrengthrandrange): - self.CreateKeyframe(self.curframe, strengthrandrange = newstrengthrandrange) - - def SetPos(self, newpos): - self.CreateKeyframe(self.curframe, pos = newpos) - - -class VortexGravity(PointGravity): - def __init__(self, strength = 0.0, strengthrandrange = 0.0, pos = (0, 0)): - PointGravity.__init__(self, strength, strengthrandrange, pos) - self.type = "vortex" - - self.CreateKeyframe(0, self.strength, self.strengthrandrange, self.pos) - - def Update(self): - newvars = interpolate.InterpolateKeyframes(self.curframe, {'strength':self.initstrength, 'strengthrandrange':self.strengthrandrange, 'pos_x':self.pos[0], 'pos_y':self.pos[1]}, self.keyframes) - self.initstrength = newvars['strength'] - self.strengthrandrange = newvars['strengthrandrange'] - self.pos = (newvars['pos_x'], newvars['pos_y']) - - if self.strengthrandrange != 0.0: - self.strength = RandomiseStrength(self.initstrength, self.strengthrandrange) - else: - self.strength = self.initstrength - - self.curframe = self.curframe + 1 - - def GetForce(self, pos): - try: - self.alreadyshownerror - except: - print("WARNING: VortexGravity relies upon particle velocities as well as positions, and so its \ -force can only be obtained using GetForceOnParticle([PyIgnition particle object]).") - self.alreadyshownerror = True - - return [0.0, 0.0] - - def GetForceOnParticle(self, particle): - # This uses F = m(v^2 / r) (the formula for centripetal force on an object moving in a circle) - # to determine what force should be applied to keep an object circling the gravity. A small extra - # force (self.strength * VORTEX_ACCELERATION) is added in order to accelerate objects inward as - # well, thus creating a spiralling effect. Note that unit mass is assumed throughout. - - distvector = [self.pos[0] - particle.pos[0], self.pos[1] - particle.pos[1]] # Vector from the particle to this gravity - try: - distmag = sqrt(float(distvector[0] ** 2) + float(distvector[1] ** 2)) # Distance from the particle to this gravity - except: - return [0.0, 0.0] # This prevents OverflowErrors - - if distmag == 0.0: - return [0.0, 0.0] - - if distmag <= VORTEX_SWALLOWDIST: - particle.alive = False - - normal = [float(distvector[0]) / distmag, float(distvector[1]) / distmag] # Normal from particle to this gravity - - velocitymagsquared = (particle.velocity[0] ** 2) + (particle.velocity[1] ** 2) # Velocity magnitude squared - forcemag = (velocitymagsquared / distmag) + (self.strength * VORTEX_ACCELERATION) # Force magnitude = (v^2 / r) + radial acceleration - - #velparmag = (particle.velocity[0] * normal[0]) + (particle.velocity[1] * normal[1]) # Magnitude of velocity parallel to normal - #velpar = [normal[0] * velparmag, normal[1] * velparmag] # Vector of velocity parallel to normal - #velperp = [particle.velocity[0] - velpar[0], particle.velocity[1] - velpar[1]] # Vector of velocity perpendicular to normal - # - #fnpar = [-velperp[1], velperp[0]] # Force normal parallel to normal - #fnperp = [velpar[1], -velpar[0]] # Force normal perpendicular to normal - # - #force = [(fnpar[0] + fnperp[0]) * forcemag, (fnpar[1] + fnperp[1]) * forcemag] - - force = [normal[0] * forcemag, normal[1] * forcemag] # Works, but sometimes goes straight to the gravity w/ little spiraling - - return force - - def CreateKeyframe(self, frame, strength = None, strengthrandrange = None, pos = (None, None), interpolationtype = INTERPOLATIONTYPE_LINEAR): - return keyframes.CreateKeyframe(self.keyframes, frame, {'strength':strength, 'strengthrandrange':strengthrandrange, 'pos_x':pos[0], 'pos_y':pos[1], 'interpolationtype':interpolationtype}) - - def ConsolidateKeyframes(self): - keyframes.ConsolidateKeyframes(self.keyframes, self.curframe, {'strength':self.initstrength, 'strengthrandrange':self.strengthrandrange, 'pos_x':self.pos[0], 'pos_y':self.pos[1]}) diff --git a/pyignition/interpolate.py b/pyignition/interpolate.py @@ -1,77 +0,0 @@ -### EXESOFT PYIGNITION ### -# Copyright David Barker 2010 -# -# Utility module for interpolating between keyframed values - - -import math -from constants import * - - -def LinearInterpolate(val1, val2, t): - diff = val2 - val1 - dist = float(diff) * t - - return val1 + dist - -def CosineInterpolate(val1, val2, t): - amplitude = float(val2 - val1) / 2.0 - midpoint = float(val1 + val2) / 2.0 - - return (amplitude * math.cos(math.pi * (1.0 - t))) + midpoint - - -def LinearInterpolateKeyframes(curframe, key1, key2, val1, val2): - if key1 == key2: - return val2 - - factor = float(curframe - key1) / float(key2 - key1) - - return LinearInterpolate(val1, val2, factor) - -def CosineInterpolateKeyframes(curframe, key1, key2, val1, val2): - if key1 == key2: - return val2 - - factor = float(curframe - key1) / float(key2 - key1) - - return CosineInterpolate(val1, val2, factor) - - -def InterpolateKeyframes(curframe, variables, keyframes): - if len(keyframes) == 1: - return keyframes[0].variables - - finalvariables = {} - - if not ('interpolationtype' in variables): - variables['interpolationtype'] = INTERPOLATIONTYPE_LINEAR - - keys = list(variables.keys()) - - for i in range(len(keys)): # Determine current keyframe and next one for this variable - key = keys[i] - curkeyframe = None - nextkeyframe = None - - for i in range(len(keyframes)): - try: - frame = keyframes[i] - if (frame.variables[key] != None): # If the current keyframe has a keyed value for the current variable - if frame.frame <= curframe: # If its frame is below or equal to the current, it is the current keyframe - curkeyframe = i - if (nextkeyframe == None) and (frame.frame > curframe): # If this is the first keyframe with a frame higher than the current, it is the next keyframe - nextkeyframe = i - except KeyError: - pass - - if nextkeyframe == None or key == "interpolationtype": # If there is no next key frame, maintain the value specified by the current one - finalvariables[key] = keyframes[curkeyframe].variables[key] # (Also do this if it is an interpolation type variable; they should only change once their next keyframe has been reached - - else: # Interpolate between the current and next keyframes - if keyframes[nextkeyframe].variables['interpolationtype'] == INTERPOLATIONTYPE_LINEAR: - finalvariables[key] = LinearInterpolateKeyframes(curframe, keyframes[curkeyframe].frame, keyframes[nextkeyframe].frame, keyframes[curkeyframe].variables[key], keyframes[nextkeyframe].variables[key]) - elif keyframes[nextkeyframe].variables['interpolationtype'] == INTERPOLATIONTYPE_COSINE: - finalvariables[key] = CosineInterpolateKeyframes(curframe, keyframes[curkeyframe].frame, keyframes[nextkeyframe].frame, keyframes[curkeyframe].variables[key], keyframes[nextkeyframe].variables[key]) - - return finalvariables diff --git a/pyignition/keyframes.py b/pyignition/keyframes.py @@ -1,61 +0,0 @@ -### EXESOFT PYIGNITION ### -# Copyright David Barker 2010 -# -# Keyframe object and generic keyframe creation function - - -from constants import * -import sys - -def CreateKeyframe(parentframes, frame, variables): - newframe = Keyframe(frame, variables) - - # Look for duplicate keyframes and copy other defined variables - try: - if sys.version_info.major == 2: - oldkey = (keyframe for keyframe in parentframes if keyframe.frame == frame).next() - else: - oldkey = (keyframe for keyframe in parentframes if keyframe.frame == frame).__next__() - except StopIteration: - oldkey = None - - if oldkey != None: - for var in oldkey.variables.keys(): # For every variable defined by the old keyframe - if (var not in newframe.variables or newframe.variables[var] == None) and (oldkey.variables[var] != None): # If a variable is undefined, copy its value to the new keyframe - newframe.variables[var] = oldkey.variables[var] - - # Remove the duplicate keyframe, if it existed - for duplicate in (keyframe for keyframe in parentframes if keyframe.frame == frame): - parentframes.remove(duplicate) - break - - # Append the new keyframe then sort them all by frame - parentframes.append(newframe) - sortedframes = sorted(parentframes, key=lambda keyframe: keyframe.frame) - parentframes[:] = sortedframes - - return newframe # Return a reference to the new keyframe, in case it's needed - -def ConsolidateKeyframes(parentframes, frame, variables): - newframe = Keyframe(frame, variables) - parentframes.append(newframe) - - finallist = [] # A truncated list of keyframes - - # Append all the frames which come after the current one to the final list - for keyframe in parentframes: - if keyframe.frame >= frame: - finallist.append(keyframe) - - # Sort the keyframes and give them to the parent object - sortedframes = sorted(finallist, key=lambda keyframe: keyframe.frame) - parentframes[:] = sortedframes - -class Keyframe: - def __init__(self, frame = 0, variables = {}): - self.frame = frame - self.variables = variables - - if not ('interpolationtype' in self.variables): - self.variables['interpolationtype'] = INTERPOLATIONTYPE_LINEAR - diff --git a/pyignition/obstacles.py b/pyignition/obstacles.py @@ -1,404 +0,0 @@ -### EXESOFT PYIGNITION ### -# Copyright David Barker 2010 -# -# Obstacle objects - -import pygame -from math import sqrt, pow -from gravity import UNIVERSAL_CONSTANT_OF_MAKE_GRAVITY_LESS_STUPIDLY_SMALL -import keyframes, interpolate -from constants import * - - -MAXDIST = 20.0 - - -def dotproduct2d(v1, v2): - return ((v1[0] * v2[0]) + (v1[1] * v2[1])) - -def magnitude(vec): - try: - return sqrt(vec[0] ** 2 + vec[1] ** 2) - except: - return 1.0 - -def magnitudesquared(vec): - try: - return (vec[0] ** 2 + vec[1] ** 2) - except: - return 1.0 - -def normalise(vec): - mag = magnitude(vec) - return [vec[0] / mag, vec[1] / mag] - - -class Obstacle: - def __init__(self, parenteffect, pos, colour, bounce): - self.parenteffect = parenteffect - self.pos = pos - self.colour = colour - self.bounce = bounce - self.maxdist = MAXDIST # The maximum (square-based, not circle-based) distance away for which forces will still be calculated - self.curframe = 0 - self.keyframes = [] - - def Draw(self, display): - pass - - def Update(self): - self.curframe = self.curframe + 1 - - def OutOfRange(self, pos): - return (abs(pos[0] - self.pos[0]) > self.maxdist) or (abs(pos[1] - self.pos[1]) > self.maxdist) - - def InsideObject(self, pos, pradius): - pass - - def GetResolved(self, pos, pradius): # Get a resolved position for a particle located inside the object - pass - - def GetDist(self, pos): - return magnitude([pos[0] - self.pos[0], pos[1] - self.pos[1]]) - - def GetNormal(self, pos): # Gets the normal relevant to a particle at the supplied potision (for example, that of the appropriate side of a squre) - pass - - def GetForceFactor(self, pos, pradius): # Gets the force as a factor of maximum available force (0.0 - 1.0), determined by an inverse cube distance law - pass - - def GetForce(self, pos, velocity, pradius = 0.0): # Gets the final (vector) force - if self.OutOfRange(pos) or self.bounce == 0.0: - return [0.0, 0.0] - - if (pos[0] == self.pos[0]) and (pos[1] == self.pos[1]): - return [0.0, 0.0] - - normal = self.GetNormal(pos) - scalingfactor = -dotproduct2d(normal, velocity) # An integer between 0.0 and 1.0 used to ensure force is maximised for head-on collisions and minimised for scrapes - - if scalingfactor <= 0.0: # The force should never be attractive, so take any negative value of the scaling factor to be zero - return [0.0, 0.0] # A scaling factor of zero always results in zero force - - forcefactor = (self.GetForceFactor(pos, pradius)) - velmag = magnitude(velocity) # Magnitude of the velocity - multiplied in the force equation - - # Force = bounce factor * velocity * distance force factor (0.0 - 1.0) * angle force factor (0.0 - 1.0), along the direction of the normal pointing away from the obstacle - return [normal[0] * forcefactor * velmag * scalingfactor * self.bounce, normal[1] * forcefactor * velmag * scalingfactor * self.bounce] - - def CreateKeyframe(self): - pass - - def SetPos(self, newpos): - self.CreateKeyframe(self.curframe, pos = newpos) - - def SetColour(self, newcolour): - self.CreateKeyframe(self.curframe, colour = newcolour) - - def SetBounce(self, newbounce): - self.CreateKeyframe(self.curframe, bounce = newbounce) - - -class Circle(Obstacle): - def __init__(self, parenteffect, pos, colour, bounce, radius): - Obstacle.__init__(self, parenteffect, pos, colour, bounce) - self.type = "circle" - self.radius = radius - self.radiussquared = self.radius ** 2 - self.maxdist = MAXDIST + self.radius - self.CreateKeyframe(0, self.pos, self.colour, self.bounce, self.radius) - - def Draw(self, display): - offset = self.parenteffect.pos - pygame.draw.circle(display, self.colour, (offset[0] + int(self.pos[0]), offset[1] + int(self.pos[1])), int(self.radius)) - - def Update(self): - newvars = interpolate.InterpolateKeyframes(self.curframe, {'pos_x':self.pos[0], 'pos_y':self.pos[1], 'colour_r':self.colour[0], 'colour_g':self.colour[1], 'colour_b':self.colour[2], 'bounce':self.bounce, 'radius':self.radius}, self.keyframes) - self.pos = (newvars['pos_x'], newvars['pos_y']) - self.colour = (newvars['colour_r'], newvars['colour_g'], newvars['colour_b']) - self.bounce = newvars['bounce'] - self.radius = newvars['radius'] - - Obstacle.Update(self) - - def InsideObject(self, pos, pradius = 0.0): - mag = magnitude([pos[0] - self.pos[0], pos[1] - self.pos[1]]) - return (((mag - pradius) ** 2) < self.radiussquared) - - def GetResolved(self, pos, pradius = 0.0): - if pos == self.pos: # If the position is at this obstacle's origin, shift it up a pixel to avoid divide-by-zero errors - return self.GetResolved([pos[0], pos[1] - 1]) - - vec = [pos[0] - self.pos[0], pos[1] - self.pos[1]] - mag = magnitude(vec) - nor = [vec[0] / mag, vec[1] / mag] - - # Split the pradius into appropriate components by multiplying the normal vector by pradius - pradiusvec = [(nor[0] * pradius), (nor[1] * pradius)] - - correctedvec = [nor[0] * (self.radius), nor[1] * (self.radius)] - - return [self.pos[0] + correctedvec[0] + pradiusvec[0], self.pos[1] + correctedvec[1] + pradiusvec[1]] - - def GetNormal(self, pos): - vec = [pos[0] - self.pos[0], pos[1] - self.pos[1]] - mag = magnitude(vec) - - return [vec[0] / mag, vec[1] / mag] - - def GetForceFactor(self, pos, pradius = 0.0): - nvec = self.GetNormal(pos) - tempradius = self.radius - vec = [tempradius * nvec[0], tempradius * nvec[1]] - newpos = [pos[0] - vec[0], pos[1] - vec[1]] - - distcubed = (abs(pow(float(newpos[0] - self.pos[0]), 3.0)) + abs(pow(float(newpos[1] - self.pos[1]), 3.0))) - (pradius * pradius * pradius) - if distcubed <= 1.0: - return 1.0 - - force = (1.0 / distcubed) - - return force - - def CreateKeyframe(self, frame, pos = (None, None), colour = (None, None, None), bounce = None, radius = None, interpolationtype = INTERPOLATIONTYPE_LINEAR): - return keyframes.CreateKeyframe(self.keyframes, frame, {'pos_x':pos[0], 'pos_y':pos[1], 'colour_r':colour[0], 'colour_g':colour[1], 'colour_b':colour[2], 'bounce':bounce, 'radius':radius, 'interpolationtype':interpolationtype}) - - def ConsolidateKeyframes(self): - keyframes.ConsolidateKeyframes(self.keyframes, self.curframe, {'pos_x':self.pos[0], 'pos_y':self.pos[1], 'colour_r':self.colour[0], 'colour_g':self.colour[1], 'colour_b':self.colour[2], 'bounce':self.bounce, 'radius':self.radius}) - - def SetRadius(self, newradius): - self.CreateKeyframe(self.curframe, radius = newradius) - - -class Rectangle(Obstacle): - def __init__(self, parenteffect, pos, colour, bounce, width, height): - Obstacle.__init__(self, parenteffect, pos, colour, bounce) - self.type = "rectangle" - self.width = width - self.halfwidth = self.width / 2.0 - self.height = height - self.halfheight = height / 2.0 - self.maxdist = max(self.halfwidth, self.halfheight) + MAXDIST - self.CreateKeyframe(0, self.pos, self.colour, self.bounce, self.width, self.height) - - def Draw(self, display): - offset = self.parenteffect.pos - pygame.draw.rect(display, self.colour, pygame.Rect(offset[0] + (self.pos[0] - self.halfwidth), offset[1] + (self.pos[1] - self.halfheight), self.width, self.height)) - - def Update(self): - newvars = interpolate.InterpolateKeyframes(self.curframe, {'pos_x':self.pos[0], 'pos_y':self.pos[1], 'colour_r':self.colour[0], 'colour_g':self.colour[1], 'colour_b':self.colour[2], 'bounce':self.bounce, 'width':self.width, 'height':self.height}, self.keyframes) - self.pos = (newvars['pos_x'], newvars['pos_y']) - self.colour = (newvars['colour_r'], newvars['colour_g'], newvars['colour_b']) - self.bounce = newvars['bounce'] - self.width = newvars['width'] - self.halfwidth = self.width / 2.0 - self.height = newvars['height'] - self.halfheight = self.height / 2.0 - self.maxdist = max(self.halfwidth, self.halfheight) + MAXDIST - - Obstacle.Update(self) - - def InsideObject(self, pos, pradius = 0.0): - return (((pos[0] + pradius) > (self.pos[0] - self.halfwidth)) and ((pos[0] - pradius) < (self.pos[0] + self.halfwidth)) and ((pos[1] + pradius) > (self.pos[1] - self.halfheight)) and ((pos[1] - pradius) < self.pos[1] + self.halfheight)) - - def GetResolved(self, pos, pradius = 0.0): - if pos == self.pos: # If the position is at this obstacle's origin, shift it up a pixel to avoid divide-by-zero errors - return self.GetResolved([pos[0], pos[1] - 1]) - - # Where 'triangles' within the rectangle are referred to, imagine a rectangle with diagonals drawn between its vertices. The four triangles formed by this process are the ones referred to - if pos[0] == self.pos[0]: # If it's directly above the centre of the rectangle - if pos[1] > self.pos[1]: - return [pos[0], self.pos[1] + self.halfheight + pradius] - else: - return [pos[0], self.pos[1] - (self.halfheight + pradius)] - elif pos[1] == self.pos[1]: # If it's directly to one side of the centre of the rectangle - if pos[0] > self.pos[0]: - return [self.pos[0] + self.halfwidth + pradius, pos[1]] - else: - return [self.pos[0] - (self.halfwidth + pradius), pos[1]] - elif abs(float(pos[1] - self.pos[1]) / float(pos[0] - self.pos[0])) > (float(self.height) / float(self.width)): # If it's in the upper or lower triangle of the rectangle - return [pos[0], self.pos[1] + ((self.halfheight + pradius) * ((pos[1] - self.pos[1]) / abs(pos[1] - self.pos[1])))] # Halfheight is multiplied by a normalised version of (pos[1] - self.pos[1]) - thus if (pos[1] - self.pos[1]) is negative, it should be subtracted as the point is in the upper triangle - else: # If it's in the left or right triangle of the rectangle - return [self.pos[0] + ((self.halfwidth + pradius) * ((pos[0] - self.pos[0]) / abs(pos[0] - self.pos[0]))), pos[1]] - - def GetNormal(self, pos): - if pos[1] < (self.pos[1] - self.halfheight): - return [0, -1] - elif pos[1] > (self.pos[1] + self.halfheight): - return [0, 1] - elif pos[0] < (self.pos[0] - self.halfwidth): - return [-1, 0] - elif pos[0] > (self.pos[0] + self.halfwidth): - return [1, 0] - else: - vect = [pos[0] - self.pos[0], pos[1] - self.pos[1]] - mag = magnitude(vect) - return [vect[0] / mag, vect[1] / mag] - - def GetForceFactor(self, pos, pradius = 0.0): - nor = self.GetNormal(pos) - - if nor[0] == 0: - if (pos[0] > (self.pos[0] - self.halfwidth)) and (pos[0] < (self.pos[0] + self.halfwidth)): - r = (abs(pos[1] - self.pos[1]) - self.halfheight) - pradius - else: - return 0.0 - elif nor[1] == 0: - if (pos[1] > (self.pos[1] - self.halfheight)) and (pos[1] < (self.pos[1] + self.halfheight)): - r = (abs(pos[0] - self.pos[0]) - self.halfwidth) - pradius - else: - return 0.0 - else: - return 1.0 - - if r <= 1.0: - return 1.0 - - return (1.0 / pow(float(r), 3.0)) - - def CreateKeyframe(self, frame, pos = (None, None), colour = (None, None, None), bounce = None, width = None, height = None, interpolationtype = INTERPOLATIONTYPE_LINEAR): - return keyframes.CreateKeyframe(self.keyframes, frame, {'pos_x':pos[0], 'pos_y':pos[1], 'colour_r':colour[0], 'colour_g':colour[1], 'colour_b':colour[2], 'bounce':bounce, 'width':width, 'height':height, 'interpolationtype':interpolationtype}) - - def ConsolidateKeyframes(self): - keyframes.ConsolidateKeyframes(self.keyframes, self.curframe, {'pos_x':self.pos[0], 'pos_y':self.pos[1], 'colour_r':self.colour[0], 'colour_g':self.colour[1], 'colour_b':self.colour[2], 'bounce':self.bounce, 'width':self.width, 'height':self.height}) - - def SetWidth(self, newwidth): - self.CreateKeyframe(self.curframe, width = newwidth) - - def SetHeight(self, newheight): - self.CreateKeyframe(self.curframe, height = newheight) - - -class BoundaryLine(Obstacle): - def __init__(self, parenteffect, pos, colour, bounce, normal): - Obstacle.__init__(self, parenteffect, pos, colour, bounce) - self.type = "boundaryline" - self.normal = normalise(normal) - self.edgecontacts = [] - self.hascontacts = False - self.storedw, self.storedh = None, None - self.curframe = 0 - self.CreateKeyframe(0, self.pos, self.colour, self.bounce, self.normal) - - def Draw(self, display): - offset = self.parenteffect.pos - - W = display.get_width() - H = display.get_height() - - if (W != self.storedw) or (H != self.storedh): - self.hascontacts = False - - if not self.hascontacts: - self.storedw, self.storedh = W, H - - edgecontacts = [] # Where the line touches the screen edges - - if self.normal[0] == 0.0: - edgecontacts = [[0, self.pos[1]], [W, self.pos[1]]] - - elif self.normal[1] == 0.0: - edgecontacts = [[self.pos[0], 0], [self.pos[0], H]] - - else: - pdotn = (self.pos[0] * self.normal[0]) + (self.pos[1] * self.normal[1]) - reciprocaln0 = (1.0 / self.normal[0]) - reciprocaln1 = (1.0 / self.normal[1]) - - # Left-hand side of the screen - pointl = [0, 0] - pointl[1] = pdotn * reciprocaln1 - if (pointl[1] >= 0) and (pointl[1] <= H): - edgecontacts.append(pointl) - - # Top of the screen - pointt = [0, 0] - pointt[0] = pdotn * reciprocaln0 - if (pointt[0] >= 0) and (pointt[0] <= W): - edgecontacts.append(pointt) - - # Right-hand side of the screen - pointr = [W, 0] - pointr[1] = (pdotn - (W * self.normal[0])) * reciprocaln1 - if (pointr[1] >= 0) and (pointr[1] <= H): - edgecontacts.append(pointr) - - # Bottom of the screen - pointb = [0, H] - pointb[0] = (pdotn - (H * self.normal[1])) * reciprocaln0 - if (pointb[0] >= 0) and (pointb[0] <= W): - edgecontacts.append(pointb) - - self.edgecontacts = edgecontacts - self.hascontacts = True - - tempedgecontacts = [] - - for contact in self.edgecontacts: - tempedgecontacts.append([offset[0] + contact[0], offset[1] + contact[1]]) - - if len(tempedgecontacts) >= 2: - pygame.draw.aalines(display, self.colour, True, tempedgecontacts) - else: - pass # The line must be completely outwith the boundaries of the display - - def Update(self): - newvars = interpolate.InterpolateKeyframes(self.curframe, {'pos_x':self.pos[0], 'pos_y':self.pos[1], 'colour_r':self.colour[0], 'colour_g':self.colour[1], 'colour_b':self.colour[2], 'bounce':self.bounce, 'normal_x':self.normal[0], 'normal_y':self.normal[1]}, self.keyframes) - self.pos = (newvars['pos_x'], newvars['pos_y']) - self.colour = (newvars['colour_r'], newvars['colour_g'], newvars['colour_b']) - self.bounce = newvars['bounce'] - oldnormal = self.normal[:] - self.normal = [newvars['normal_x'], newvars['normal_y']] - if self.normal != oldnormal: - self.hascontacts = False - - Obstacle.Update(self) - - def OutOfRange(self, pos): - return (self.GetDist(pos) > MAXDIST) - - def InsideObject(self, pos, pradius = 0.0): - if pradius == 0.0: # If the particle has no radius, just test whether its centre position has crossed the line - return (((float(pos[0] - self.pos[0]) * self.normal[0]) + (float(pos[1] - self.pos[1]) * self.normal[1])) <= 0.0) - - radialnormal = [self.normal[0] * pradius, self.normal[1] * pradius] - leftside = [pos[0] + radialnormal[0], pos[1] + radialnormal[1]] - rightside = [pos[0] - radialnormal[0], pos[1] - radialnormal[1]] - - return ((((float(leftside[0] - self.pos[0]) * self.normal[0]) + (float(leftside[1] - self.pos[1]) * self.normal[1])) <= 0.0) - or (((float(rightside[0] - self.pos[0]) * self.normal[0]) + (float(rightside[1] - self.pos[1]) * self.normal[1])) <= 0.0)) - - def GetResolved(self, pos, pradius = 0.0): - if pos == self.pos: # If the position is at this obstacle's origin, shift it up a pixel to avoid divide-by-zero errors - return self.GetResolved([pos[0], pos[1] - 1]) - - dist = abs(self.GetDist(pos, pradius)) - vec = [dist * self.normal[0], dist * self.normal[1]] - - return [pos[0] + vec[0], pos[1] + vec[1]] - - def GetNormal(self, pos): - return self.normal - - def GetDist(self, pos, pradius = 0.0): - v = [float(pos[0] - self.pos[0]), float(pos[1] - self.pos[1])] - return (v[0] * self.normal[0]) + (v[1] * self.normal[1]) - pradius - - def GetForceFactor(self, pos, pradius = 0.0): - r = self.GetDist(pos) - pradius - - if r <= 1.0: - return 1.0 - - return (1.0 / pow(r, 3.0)) - - def CreateKeyframe(self, frame, pos = (None, None), colour = (None, None, None), bounce = None, normal = [None, None], interpolationtype = INTERPOLATIONTYPE_LINEAR): - if (normal != [None, None]) and (abs(magnitudesquared(normal) - 1.0) >= 0.3): - normal = normalise(normal) - return keyframes.CreateKeyframe(self.keyframes, frame, {'pos_x':pos[0], 'pos_y':pos[1], 'colour_r':colour[0], 'colour_g':colour[1], 'colour_b':colour[2], 'bounce':bounce, 'normal_x':normal[0], 'normal_y':normal[1], 'interpolationtype':interpolationtype}) - - def ConsolidateKeyframes(self): - keyframes.ConsolidateKeyframes(self.keyframes, self.curframe, {'pos_x':self.pos[0], 'pos_y':self.pos[1], 'colour_r':self.colour[0], 'colour_g':self.colour[1], 'colour_b':self.colour[2], 'bounce':self.bounce, 'normal_x':self.normal[0], 'normal_y':self.normal[1]}) - - def SetNormal(self, newnormal): - self.CreateKeyframe(self.curframe, normal = newnormal) diff --git a/pyignition/particles.py b/pyignition/particles.py @@ -1,200 +0,0 @@ -### EXESOFT PYIGNITION ### -# Copyright David Barker 2010 -# -# Particle and ParticleSource objects - - -import keyframes, interpolate, random, math, pygame -from constants import * - - -class Particle: - def __init__(self, parent, initpos, velocity, life, drawtype = DRAWTYPE_POINT, colour = (0, 0, 0), radius = 0.0, length = 0.0, image = None, keyframes = []): - self.parent = parent - self.pos = initpos - self.velocity = velocity - self.life = life - self.drawtype = drawtype - self.colour = colour - self.radius = radius - self.length = length - self.image = image - - self.keyframes = [] - self.keyframes.extend(keyframes[:]) - self.curframe = 0 - - self.alive = True - - def Update(self): - self.pos = [self.pos[0] + self.velocity[0], self.pos[1] + self.velocity[1]] - - if self.life != -1 and self.curframe > self.life: - self.alive = False - else: - if self.life == -1 and self.curframe >= len(self.parent.particlecache): # If the particle has infinite life and has passed the last cached frame - self.colour = (self.parent.particlecache[len(self.parent.particlecache) - 1]['colour_r'], self.parent.particlecache[len(self.parent.particlecache) - 1]['colour_g'], self.parent.particlecache[len(self.parent.particlecache) - 1]['colour_b']) - self.radius = self.parent.particlecache[len(self.parent.particlecache) - 1]['radius'] - self.length = self.parent.particlecache[len(self.parent.particlecache) - 1]['length'] - else: # Otherwise, get the values for the current frame - self.colour = (self.parent.particlecache[self.curframe]['colour_r'], self.parent.particlecache[self.curframe]['colour_g'], self.parent.particlecache[self.curframe]['colour_b']) - self.radius = self.parent.particlecache[self.curframe]['radius'] - self.length = self.parent.particlecache[self.curframe]['length'] - - self.curframe = self.curframe + 1 - - def Draw(self, display): - offset = self.parent.parenteffect.pos - - if (self.pos[0] > 10000) or (self.pos[1] > 10000) or (self.pos[0] < -10000) or (self.pos[1] < -10000): - return - - if self.drawtype == DRAWTYPE_POINT: # Point - pygame.draw.circle(display, self.colour, (offset[0] + int(self.pos[0]), offset[1] + int(self.pos[1])), 0) - - elif self.drawtype == DRAWTYPE_CIRCLE: # Circle - pygame.draw.circle(display, self.colour, (offset[0] + int(self.pos[0]), offset[1] + int(self.pos[1])), int(self.radius)) - - elif self.drawtype == DRAWTYPE_LINE: - if self.length == 0.0: - pygame.draw.circle(display, self.colour, (offset[0] + int(self.pos[0]), offset[1] + int(self.pos[1])), 0) - - else: - # Vector = (velocity / mag(velocity)) * length (a line of magnitude 'length' in - # direction of velocity); this is calculated as velocity / (mag(velocity) / length) - # so that parts consistent for both components in the final calculation are only calculated once - velocitymagoverlength = math.sqrt(self.velocity[0]**2 + self.velocity[1]**2) / self.length - - if velocitymagoverlength > 0.0: # Avoid division-by-zero errors by handling lines with zero velocity separately - linevec = [(self.velocity[0] / velocitymagoverlength), (self.velocity[1] / velocitymagoverlength)] - else: - linevec = [self.length, 0.0] # Draw a line pointing to the right - - endpoint = [offset[0] + int(self.pos[0] + linevec[0]), offset[1] + int(self.pos[1] + linevec[1])] - pygame.draw.aaline(display, self.colour, (offset[0] + int(self.pos[0]), offset[1] + int(self.pos[1])), endpoint) - - elif self.drawtype == DRAWTYPE_SCALELINE: # Scaling line (scales with velocity) - endpoint = [offset[0] + int(self.pos[0] + self.velocity[0]), offset[1] + int(self.pos[1] + self.velocity[1])] - pygame.draw.aaline(display, self.colour, (offset[0] + int(self.pos[0]), offset[1] + int(self.pos[1])), endpoint) - - elif self.drawtype == DRAWTYPE_BUBBLE: # Bubble - if self.radius >= 1.0: - pygame.draw.circle(display, self.colour, (offset[0] + int(self.pos[0]), offset[1] + int(self.pos[1])), int(self.radius), 1) - else: # Pygame won't draw circles with thickness < radius, so if radius is smaller than one don't bother trying to set thickness - pygame.draw.circle(display, self.colour, (offset[0] + int(self.pos[0]), offset[1] + int(self.pos[1])), int(self.radius)) - - elif self.drawtype == DRAWTYPE_IMAGE: # Image - size = self.image.get_size() - display.blit(self.image, (offset[0] + int(self.pos[0] - size[1]), offset[1] + int(self.pos[1] - size[1]))) - - def CreateKeyframe(self, frame, colour = (None, None, None), radius = None, length = None): - keyframes.CreateKeyframe(self.keyframes, frame, {'colour_r':colour[0], 'colour_g':colour[1], 'colour_b':colour[2], 'radius':radius, 'length':length}) - - -class ParticleSource: - def __init__(self, parenteffect, pos, initspeed, initdirection, initspeedrandrange, initdirectionrandrange, particlesperframe, particlelife, genspacing, drawtype = 0, colour = (0, 0, 0), radius = 0.0, length = 0.0, imagepath = None): - self.parenteffect = parenteffect - self.pos = pos - self.initspeed = initspeed - self.initdirection = initdirection - self.initspeedrandrange = initspeedrandrange - self.initdirectionrandrange = initdirectionrandrange - self.particlesperframe = particlesperframe - self.particlelife = particlelife - self.genspacing = genspacing - self.colour = colour - self.drawtype = drawtype - self.radius = radius - self.length = length - self.imagepath = imagepath - if self.imagepath == None: - self.image = None - else: - self.image = pygame.image.load(self.imagepath).convert_alpha() - self.drawtype = drawtype - - self.keyframes = [] - self.CreateKeyframe(0, self.pos, self.initspeed, self.initdirection, self.initspeedrandrange, self.initdirectionrandrange, self.particlesperframe, self.genspacing) - self.particlekeyframes = [] - self.particlecache = [] - self.CreateParticleKeyframe(0, colour = self.colour, radius = self.radius, length = self.length) - self.curframe = 0 - - def Update(self): - newvars = interpolate.InterpolateKeyframes(self.curframe, {'pos_x':self.pos[0], 'pos_y':self.pos[1], 'initspeed':self.initspeed, 'initdirection':self.initdirection, 'initspeedrandrange':self.initspeedrandrange, 'initdirectionrandrange':self.initdirectionrandrange, 'particlesperframe':self.particlesperframe, 'genspacing':self.genspacing}, self.keyframes) - self.pos = (newvars['pos_x'], newvars['pos_y']) - self.initspeed = newvars['initspeed'] - self.initdirection = newvars['initdirection'] - self.initspeedrandrange = newvars['initspeedrandrange'] - self.initdirectionrandrange = newvars['initdirectionrandrange'] - self.particlesperframe = newvars['particlesperframe'] - self.genspacing = newvars['genspacing'] - - particlesperframe = self.particlesperframe - - if (self.genspacing == 0) or ((self.curframe % self.genspacing) == 0): - for i in range(0, int(particlesperframe)): - self.CreateParticle() - - self.curframe = self.curframe + 1 - - def CreateParticle(self): - if self.initspeedrandrange != 0.0: - speed = self.initspeed + (float(random.randrange(int(-self.initspeedrandrange * 100.0), int(self.initspeedrandrange * 100.0))) / 100.0) - else: - speed = self.initspeed - if self.initdirectionrandrange != 0.0: - direction = self.initdirection + (float(random.randrange(int(-self.initdirectionrandrange * 100.0), int(self.initdirectionrandrange * 100.0))) / 100.0) - else: - direction = self.initdirection - velocity = [speed * math.sin(direction), -speed * math.cos(direction)] - newparticle = Particle(self, initpos = self.pos, velocity = velocity, life = self.particlelife, drawtype = self.drawtype, colour = self.colour, radius = self.radius, length = self.length, image = self.image, keyframes = self.particlekeyframes) - self.parenteffect.AddParticle(newparticle) - - def CreateKeyframe(self, frame, pos = (None, None), initspeed = None, initdirection = None, initspeedrandrange = None, initdirectionrandrange = None, particlesperframe = None, genspacing = None, interpolationtype = INTERPOLATIONTYPE_LINEAR): - return keyframes.CreateKeyframe(self.keyframes, frame, {'pos_x':pos[0], 'pos_y':pos[1], 'initspeed':initspeed, 'initdirection':initdirection, 'initspeedrandrange':initspeedrandrange, 'initdirectionrandrange':initdirectionrandrange, 'particlesperframe':particlesperframe, 'genspacing':genspacing, 'interpolationtype':interpolationtype}) - - def CreateParticleKeyframe(self, frame, colour = (None, None, None), radius = None, length = None, interpolationtype = INTERPOLATIONTYPE_LINEAR): - newframe = keyframes.CreateKeyframe(self.particlekeyframes, frame, {'colour_r':colour[0], 'colour_g':colour[1], 'colour_b':colour[2], 'radius':radius, 'length':length, 'interpolationtype':interpolationtype}) - self.PreCalculateParticles() - return newframe - - def GetKeyframeValue(self, keyframe): - return keyframe.frame - - def PreCalculateParticles(self): - self.particlecache = [] # Clear the cache - - # If the particle has infinite life, interpolate for each frame up until its last keyframe - if self.particlelife == -1: - particlelife = max(self.particlekeyframes, key = self.GetKeyframeValue).frame - else: # Otherwise, interpolate the particle variables for each frame of its life - particlelife = self.particlelife - - for i in range(0, particlelife + 1): - vars = interpolate.InterpolateKeyframes(i, {'colour_r':0, 'colour_g':0, 'colour_b':0, 'radius':0, 'length':0}, self.particlekeyframes) - self.particlecache.append(vars) - - def ConsolidateKeyframes(self): - keyframes.ConsolidateKeyframes(self.keyframes, self.curframe, {'pos_x':self.pos[0], 'pos_y':self.pos[1], 'initspeed':self.initspeed, 'initdirection':self.initdirection, 'initspeedrandrange':self.initspeedrandrange, 'initdirectionrandrange':self.initdirectionrandrange, 'particlesperframe':self.particlesperframe, 'genspacing':self.genspacing}) - - def SetPos(self, newpos): - self.CreateKeyframe(self.curframe, pos = newpos) - - def SetInitSpeed(self, newinitspeed): - self.CreateKeyframe(self.curframe, initspeed = newinitspeed) - - def SetInitDirection(self, newinitdirection): - self.CreateKeyframe(self.curframe, initdirection = newinitdirection) - - def SetInitSpeedRandRange(self, newinitspeedrandrange): - self.CreateKeyframe(self.curframe, initspeedrandrange = newinitspeedrandrange) - - def SetInitDirectionRandRange(self, newinitdirectionrandrange): - self.CreateKeyframe(self.curframe, initdirectionrandrange = newinitdirectionrandrange) - - def SetParticlesPerFrame(self, newparticlesperframe): - self.CreateKeyframe(self.curframe, particlesperframe = newparticlesperframe) - - def SetGenSpacing(self, newgenspacing): - self.CreateKeyframe(self.curframe, genspacing = newgenspacing) diff --git a/pyignition/xml.py b/pyignition/xml.py @@ -1,137 +0,0 @@ -### EXESOFT XML PARSER ### -# Coopyright David Barker 2010 -# -# Python XML parser - - - -class XMLNode: - def __init__(self, parent, tag, meta, data, inside): - self.tag = tag - self.meta = meta - self.data = data - self.inside = inside - self.parent = parent - self.children = [] - self.parsed = False - - -class XMLParser: - def __init__(self, data): - self.data = data - self.meta = {} - self.root = None - - def ReadMeta(self): - while "<?" in self.data: - index = self.data.find("<?") # Start of tag - startindex = index + 2 # Start of tag inside - endindex = self.data.find("?>", index) # Tag end - - # Get the contents of the angular brackets and split into separate meta tags - metaraw = self.data[startindex:endindex].strip() - separated = metaraw.split("\" ") # Split like so ('|' = split off): - # thingy = "value|" |other = "whatever|" |third = "woo!" - - for splitraw in separated: - split = splitraw.split("=") - - # Add it to the dictionary of meta data - self.meta[split[0].strip()] = split[1].strip().strip('\"') - - # Remove this tag from the stored data - before = self.data[:index] - after = self.data[(endindex + 2):] - self.data = "".join([before, after]) - - def GetTagMeta(self, tag): - meta = {} - - metastart = tag.find(" ") + 1 - metaraw = tag[metastart:] - separated = metaraw.split("\" ") # Split like so ('|' = split off): - # thingy = "value|" |other = "whatever|" |third = "woo!" - - for splitraw in separated: - split = splitraw.split("=") - - # Add it to the dictionary of meta data - meta[split[0].strip()] = split[1].strip().strip('\"') - - return meta - - def StripXML(self): - # Remove comments - while "<!--" in self.data: - index = self.data.find("<!--") - endindex = self.data.find("-->", index) - before = self.data[:index] - after = self.data[(endindex + 3):] - self.data = "".join([before, after]) - - # Remove whitespace - self.data = self.data.replace("\n", "").replace("\t", "") - - def GetChildren(self, node): - pass - - def GetRoot(self): - rootstart = self.data.find("<") - rootstartclose = self.data.find(">", rootstart) - roottagraw = self.data[(rootstart + 1):rootstartclose] - - rootmeta = {} - if len(roottagraw.split("=")) > 1: - rootmeta = self.GetTagMeta(roottagraw) - - roottag = roottagraw.strip() - - rootend = self.data.find("</%s" % roottag) - rootendclose = self.data.find(">", rootend) - rootdata = self.data[rootstart:(rootendclose + 1)].strip() - rootinside = self.data[(rootstartclose + 1):rootend] - - self.root = XMLNode(parent = None, tag = roottag, meta = rootmeta, data = rootdata, inside = rootinside) - - def SearchNode(self, node): - node.parsed = True - - tempdata = node.inside - children = [] - - while "<" in tempdata: - start = tempdata.find("<") - startclose = tempdata.find(">", start) - tagraw = tempdata[(start + 1):startclose] - - meta = {} - if "=" in tagraw: - meta = self.GetTagMeta(tagraw) - - tag = tagraw.split(" ")[0] - - end = tempdata.find("</%s" % tag) - endclose = tempdata.find(">", end) - - data = tempdata[start:(endclose + 1)].strip() - inside = tempdata[(startclose + 1):end] - - newnode = XMLNode(node, tag, meta, data, inside) - children.append(newnode) - - before = tempdata[:start] - after = tempdata[(endclose + 1):] - tempdata = "".join([before, after]) - - node.children = children - - for child in node.children: - self.SearchNode(child) - - def Parse(self): - self.ReadMeta() - self.StripXML() - self.GetRoot() - self.SearchNode(self.root) - - return self.root diff --git a/schematic/led_pillar/led_pillar-cache.lib b/schematic/led_pillar/led_pillar-cache.lib @@ -0,0 +1,192 @@ +EESchema-LIBRARY Version 2.4 +#encoding utf-8 +# +# Connector_Raspberry_Pi_2_3 +# +DEF Connector_Raspberry_Pi_2_3 J 0 40 Y Y 1 F N +F0 "J" -700 1250 50 H V L BNN +F1 "Connector_Raspberry_Pi_2_3" 400 -1250 50 H V L TNN +F2 "" 0 0 50 H I C CNN +F3 "" 0 0 50 H I C CNN +$FPLIST + PinHeader*2x20*P2.54mm*Vertical* + PinSocket*2x20*P2.54mm*Vertical* +$ENDFPLIST +DRAW +S -700 1200 700 -1200 0 1 10 f +S -665 -690 -700 -710 1 1 0 N +S -665 -590 -700 -610 1 1 0 N +S -665 -490 -700 -510 1 1 0 N +S -665 -390 -700 -410 1 1 0 N +S -665 -290 -700 -310 1 1 0 N +S -665 -190 -700 -210 1 1 0 N +S -665 10 -700 -10 1 1 0 N +S -665 110 -700 90 1 1 0 N +S -665 210 -700 190 1 1 0 N +S -665 410 -700 390 1 1 0 N +S -665 510 -700 490 1 1 0 N +S -665 610 -700 590 1 1 0 N +S -665 810 -700 790 1 1 0 N +S -665 910 -700 890 1 1 0 N +S -410 -1165 -390 -1200 1 1 0 N +S -310 -1165 -290 -1200 1 1 0 N +S -210 -1165 -190 -1200 1 1 0 N +S -210 1200 -190 1165 1 1 0 N +S -110 -1165 -90 -1200 1 1 0 N +S -110 1200 -90 1165 1 1 0 N +S -10 -1165 10 -1200 1 1 0 N +S 90 -1165 110 -1200 1 1 0 N +S 90 1200 110 1165 1 1 0 N +S 190 -1165 210 -1200 1 1 0 N +S 190 1200 210 1165 1 1 0 N +S 290 -1165 310 -1200 1 1 0 N +S 700 -790 665 -810 1 1 0 N +S 700 -690 665 -710 1 1 0 N +S 700 -490 665 -510 1 1 0 N +S 700 -390 665 -410 1 1 0 N +S 700 -290 665 -310 1 1 0 N +S 700 -190 665 -210 1 1 0 N +S 700 -90 665 -110 1 1 0 N +S 700 110 665 90 1 1 0 N +S 700 210 665 190 1 1 0 N +S 700 310 665 290 1 1 0 N +S 700 510 665 490 1 1 0 N +S 700 610 665 590 1 1 0 N +S 700 810 665 790 1 1 0 N +S 700 910 665 890 1 1 0 N +X 3V3 1 100 1300 100 D 50 50 1 1 W +X GPIO15/RXD 10 -800 800 100 R 50 50 1 1 B +X GPIO17 11 -800 500 100 R 50 50 1 1 B +X GPIO18/PWM0 12 -800 400 100 R 50 50 1 1 B +X GPIO27 13 -800 -700 100 R 50 50 1 1 B +X GND 14 -200 -1300 100 U 50 50 1 1 W +X GPIO22 15 -800 -200 100 R 50 50 1 1 B +X GPIO23 16 -800 -300 100 R 50 50 1 1 B +X 3V3 17 200 1300 100 D 50 50 1 1 W +X GPIO24 18 -800 -400 100 R 50 50 1 1 B +X MOSI0/GPIO10 19 800 -400 100 L 50 50 1 1 B +X 5V 2 -200 1300 100 D 50 50 1 1 W +X GND 20 -100 -1300 100 U 50 50 1 1 W +X MISO0/GPIO9 21 800 -300 100 L 50 50 1 1 B +X GPIO25 22 -800 -500 100 R 50 50 1 1 B +X SCLK0/GPIO11 23 800 -500 100 L 50 50 1 1 B +X ~CE0~/GPIO8 24 800 -200 100 L 50 50 1 1 B +X GND 25 0 -1300 100 U 50 50 1 1 W +X ~CE1~/GPIO7 26 800 -100 100 L 50 50 1 1 B +X ID_SD/GPIO0 27 800 900 100 L 50 50 1 1 B +X ID_SC/GPIO1 28 800 800 100 L 50 50 1 1 B +X GCLK1/GPIO5 29 800 200 100 L 50 50 1 1 B +X SDA/GPIO2 3 800 600 100 L 50 50 1 1 B +X GND 30 100 -1300 100 U 50 50 1 1 W +X GCLK2/GPIO6 31 800 100 100 L 50 50 1 1 B +X PWM0/GPIO12 32 800 -700 100 L 50 50 1 1 B +X PWM1/GPIO13 33 800 -800 100 L 50 50 1 1 B +X GND 34 200 -1300 100 U 50 50 1 1 W +X GPIO19/MISO1 35 -800 200 100 R 50 50 1 1 B +X GPIO16 36 -800 600 100 R 50 50 1 1 B +X GPIO26 37 -800 -600 100 R 50 50 1 1 B +X GPIO20/MOSI1 38 -800 100 100 R 50 50 1 1 B +X GND 39 300 -1300 100 U 50 50 1 1 W +X 5V 4 -100 1300 100 D 50 50 1 1 W +X GPIO21/SCLK1 40 -800 0 100 R 50 50 1 1 B +X SCL/GPIO3 5 800 500 100 L 50 50 1 1 B +X GND 6 -400 -1300 100 U 50 50 1 1 W +X GCLK0/GPIO4 7 800 300 100 L 50 50 1 1 B +X GPIO14/TXD 8 -800 900 100 R 50 50 1 1 B +X GND 9 -300 -1300 100 U 50 50 1 1 W +ENDDRAW +ENDDEF +# +# Converter_ACDC_IRM-10-5 +# +DEF Converter_ACDC_IRM-10-5 PS 0 20 Y Y 1 F N +F0 "PS" 0 250 50 H V C CNN +F1 "Converter_ACDC_IRM-10-5" 0 -250 50 H V C CNN +F2 "Converter_ACDC:Converter_ACDC_MeanWell_IRM-10-xx_THT" 0 -350 50 H I C CNN +F3 "" 0 -400 50 H I C CNN +ALIAS IRM-10-5 IRM-10-12 IRM-10-15 IRM-10-24 +$FPLIST + Converter*ACDC*MeanWell*IRM*10*THT* +$ENDFPLIST +DRAW +A -185 38 28 -1525 -275 0 1 0 N -210 25 -160 25 +A -135 11 29 292 1508 0 1 0 N -110 25 -160 25 +S -300 200 300 -200 0 1 10 f +P 2 0 1 0 -210 -25 -110 -25 N +P 2 0 1 0 0 -100 0 -150 N +P 2 0 1 0 0 0 0 -50 N +P 2 0 1 0 0 100 0 50 N +P 2 0 1 0 0 200 0 150 N +P 2 0 1 0 110 -25 210 -25 N +P 2 0 1 0 110 25 130 25 N +P 2 0 1 0 150 25 170 25 N +P 2 0 1 0 190 25 210 25 N +X AC/N 1 -400 -100 100 R 50 50 1 1 W +X AC/L 2 -400 100 100 R 50 50 1 1 W +X -Vo 3 400 -100 100 L 50 50 1 1 w +X +Vo 4 400 100 100 L 50 50 1 1 w +ENDDRAW +ENDDEF +# +# Device_Fuse +# +DEF Device_Fuse F 0 0 N Y 1 F N +F0 "F" 80 0 50 V V C CNN +F1 "Device_Fuse" -75 0 50 V V C CNN +F2 "" -70 0 50 V I C CNN +F3 "" 0 0 50 H I C CNN +$FPLIST + *Fuse* +$ENDFPLIST +DRAW +S -30 -100 30 100 0 1 10 N +P 2 0 1 0 0 100 0 -100 N +X ~ 1 0 150 50 D 50 50 1 1 P +X ~ 2 0 -150 50 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# LED_WS2812B +# +DEF LED_WS2812B D 0 10 Y Y 1 F N +F0 "D" 200 225 50 H V R BNN +F1 "LED_WS2812B" 50 -225 50 H V L TNN +F2 "LED_SMD:LED_WS2812B_PLCC4_5.0x5.0mm_P3.2mm" 50 -300 50 H I L TNN +F3 "" 100 -375 50 H I L TNN +$FPLIST + LED*WS2812*PLCC*5.0x5.0mm*P3.2mm* +$ENDFPLIST +DRAW +T 0 90 -165 30 0 0 0 RGB Normal 0 C C +S 200 200 -200 -200 0 1 10 f +P 2 0 1 0 50 -140 70 -140 N +P 2 0 1 0 50 -100 70 -100 N +P 2 0 1 0 185 -140 105 -140 N +P 3 0 1 0 90 -100 50 -140 50 -120 N +P 3 0 1 0 90 -60 50 -100 50 -80 N +P 3 0 1 0 145 -40 145 -140 145 -160 N +P 4 0 1 0 185 -60 105 -60 145 -140 185 -60 N +X VDD 1 0 300 100 D 50 50 1 1 W +X DOUT 2 300 0 100 L 50 50 1 1 O +X VSS 3 0 -300 100 U 50 50 1 1 W +X DIN 4 -300 0 100 R 50 50 1 1 I +ENDDRAW +ENDDEF +# +# Switch_SW_SPST +# +DEF Switch_SW_SPST SW 0 0 Y N 1 F N +F0 "SW" 0 125 50 H V C CNN +F1 "Switch_SW_SPST" 0 -100 50 H V C CNN +F2 "" 0 0 50 H I C CNN +F3 "" 0 0 50 H I C CNN +DRAW +C -80 0 20 0 0 0 N +C 80 0 20 0 0 0 N +P 2 0 0 0 -60 10 60 70 N +X A 1 -200 0 100 R 50 50 1 1 P +X B 2 200 0 100 L 50 50 1 1 P +ENDDRAW +ENDDEF +# +#End Library diff --git a/schematic/led_pillar/led_pillar.pro b/schematic/led_pillar/led_pillar.pro @@ -0,0 +1,33 @@ +update=Date +version=1 +last_client=kicad +[general] +version=1 +RootSch= +BoardNm= +[pcbnew] +version=1 +LastNetListRead= +UseCmpFile=1 +PadDrill=0.600000000000 +PadDrillOvalY=0.600000000000 +PadSizeH=1.500000000000 +PadSizeV=1.500000000000 +PcbTextSizeV=1.500000000000 +PcbTextSizeH=1.500000000000 +PcbTextThickness=0.300000000000 +ModuleTextSizeV=1.000000000000 +ModuleTextSizeH=1.000000000000 +ModuleTextSizeThickness=0.150000000000 +SolderMaskClearance=0.000000000000 +SolderMaskMinWidth=0.000000000000 +DrawSegmentWidth=0.200000000000 +BoardOutlineThickness=0.100000000000 +ModuleOutlineThickness=0.150000000000 +[cvpcb] +version=1 +NetIExt=net +[eeschema] +version=1 +LibDir= +[eeschema/libraries] diff --git a/schematic/led_pillar/led_pillar.sch b/schematic/led_pillar/led_pillar.sch @@ -0,0 +1,164 @@ +EESchema Schematic File Version 4 +EELAYER 30 0 +EELAYER END +$Descr A4 11693 8268 +encoding utf-8 +Sheet 1 1 +Title "" +Date "" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +$Comp +L Switch:SW_SPST SW2 +U 1 1 60298FB5 +P 3050 2700 +F 0 "SW2" H 3050 2935 50 0000 C CNN +F 1 "SW_SPST" H 3050 2844 50 0000 C CNN +F 2 "" H 3050 2700 50 0001 C CNN +F 3 "~" H 3050 2700 50 0001 C CNN + 1 3050 2700 + 1 0 0 -1 +$EndComp +$Comp +L Device:Fuse F1 +U 1 1 60297DDB +P 2550 2700 +F 0 "F1" V 2350 2650 50 0000 L CNN +F 1 "Fuse 5A" V 2450 2550 50 0000 L CNN +F 2 "" V 2480 2700 50 0001 C CNN +F 3 "~" H 2550 2700 50 0001 C CNN + 1 2550 2700 + 0 1 1 0 +$EndComp +$Comp +L LED:WS2812B D2 +U 1 1 602B3E9C +P 2250 3850 +F 0 "D2" H 1550 3900 50 0000 L CNN +F 1 "WS2812B" H 1550 3800 50 0000 L CNN +F 2 "LED_SMD:LED_WS2812B_PLCC4_5.0x5.0mm_P3.2mm" H 2300 3550 50 0001 L TNN +F 3 "https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf" H 2350 3475 50 0001 L TNN + 1 2250 3850 + 1 0 0 -1 +$EndComp +$Comp +L LED:WS2812B D3 +U 1 1 602B51B6 +P 2250 5150 +F 0 "D3" H 1550 5200 50 0000 L CNN +F 1 "WS2812B" H 1550 5100 50 0000 L CNN +F 2 "LED_SMD:LED_WS2812B_PLCC4_5.0x5.0mm_P3.2mm" H 2300 4850 50 0001 L TNN +F 3 "https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf" H 2350 4775 50 0001 L TNN + 1 2250 5150 + 1 0 0 -1 +$EndComp +Text Notes 2150 4500 0 100 ~ 0 +... +Wire Wire Line + 2550 3150 2550 3500 +Wire Wire Line + 2550 3500 1950 3500 +Wire Wire Line + 1950 3500 1950 3850 +Wire Wire Line + 2250 3450 2250 3550 +Wire Wire Line + 2250 2700 2250 2850 +$Comp +L Connector:Raspberry_Pi_2_3 J1 +U 1 1 602D7397 +P 3700 4150 +F 0 "J1" H 4100 5650 50 0000 C CNN +F 1 "Raspberry_Pi_Zero_WH" H 4100 5550 50 0000 C CNN +F 2 "" H 3700 4150 50 0001 C CNN +F 3 "https://www.raspberrypi.org/documentation/hardware/raspberrypi/schematics/rpi_SCH_3bplus_1p0_reduced.pdf" H 3700 4150 50 0001 C CNN + 1 3700 4150 + 1 0 0 -1 +$EndComp +Wire Wire Line + 1950 3150 1950 2800 +Wire Wire Line + 2800 2800 2800 4550 +Wire Wire Line + 2800 4550 2900 4550 +Wire Wire Line + 2250 4150 2250 4400 +Wire Wire Line + 2250 4600 2250 4850 +Wire Wire Line + 2550 4500 2550 3850 +Wire Wire Line + 2550 4500 2400 4500 +Wire Wire Line + 2050 4500 1950 4500 +Wire Wire Line + 1950 4500 1950 5150 +Wire Wire Line + 2800 2800 1950 2800 +$Comp +L LED:WS2812B D1 +U 1 1 602A1B07 +P 2250 3150 +F 0 "D1" H 1550 3200 50 0000 L CNN +F 1 "WS2812B" H 1550 3100 50 0000 L CNN +F 2 "LED_SMD:LED_WS2812B_PLCC4_5.0x5.0mm_P3.2mm" H 2300 2850 50 0001 L TNN +F 3 "https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf" H 2350 2775 50 0001 L TNN + 1 2250 3150 + 1 0 0 -1 +$EndComp +Wire Wire Line + 3250 2700 3600 2700 +Connection ~ 3600 2700 +Wire Wire Line + 3600 2700 3600 2850 +Wire Wire Line + 2850 2700 2700 2700 +Wire Wire Line + 2400 2700 2250 2700 +$Comp +L Converter_ACDC:IRM-10-5 PS1 +U 1 1 602AE4EC +P 2350 1950 +F 0 "PS1" H 2350 2317 50 0000 C CNN +F 1 "110 W" H 2350 2226 50 0000 C CNN +F 2 "Converter_ACDC:Converter_ACDC_MeanWell_IRM-10-xx_THT" H 2350 1600 50 0001 C CNN +F 3 "https://www.meanwell.com/Upload/PDF/IRM-10/IRM-10-SPEC.PDF" H 2350 1550 50 0001 C CNN + 1 2350 1950 + 1 0 0 -1 +$EndComp +Wire Wire Line + 3600 1850 3600 2700 +Wire Wire Line + 1450 2250 1450 5450 +Wire Wire Line + 1450 5450 2250 5450 +Connection ~ 2250 5450 +$Comp +L Switch:SW_SPST SW1 +U 1 1 602F77C0 +P 1750 1850 +F 0 "SW1" H 1750 2085 50 0000 C CNN +F 1 "SW_SPST" H 1750 1994 50 0000 C CNN +F 2 "" H 1750 1850 50 0001 C CNN +F 3 "~" H 1750 1850 50 0001 C CNN + 1 1750 1850 + 1 0 0 -1 +$EndComp +Wire Wire Line + 1950 2050 1450 2050 +Wire Wire Line + 2750 1850 3600 1850 +Wire Wire Line + 1450 2250 2750 2250 +Wire Wire Line + 2750 2250 2750 2050 +Wire Wire Line + 1550 1850 1450 1850 +Wire Wire Line + 2250 5450 3300 5450 +$EndSCHEMATC diff --git a/templates/index.html b/templates/index.html @@ -2,7 +2,7 @@ <html lang="de"> <head> <meta charset="utf-8"> - <title>LED-Säule</title> + <title>LED Pillar</title> <style> button{width:10em;} @media only screen and (max-device-width: 1024px){ @@ -17,7 +17,7 @@ button{font-size : 190%; width:10em;} </style> </head> <body> - <h1>LED-Säule</h1> + <h1>LED Pillar</h1> <form action="/" method="POST"> <div class="slidecontainer"> R <input type="range" min="0" max="255" value="{{ red }}" class="slider" id="slider_red"> @@ -27,13 +27,13 @@ button{font-size : 190%; width:10em;} B <input type="range" min="0" max="255" value="{{ blue }}" class="slider" id="slider_blue"> <label id="label_blue"></label><br /> </div><br /> - <button type="submit" name="mode" value="off">Aus</button><br /><br /> - <button type="submit" name="mode" value="worm">Wurm</button><br /><br /> - <button type="submit" name="mode" value="cellular">Zellulärer Automat</button><br /><br /> - <button type="submit" name="mode" value="flames">Feuer</button><br /><br /> + <button type="submit" name="mode" value="off">OFF</button><br /><br /> + <button type="submit" name="mode" value="worm">Worm</button><br /><br /> + <button type="submit" name="mode" value="cellular">Cellular Automaton</button><br /><br /> + <button type="submit" name="mode" value="flames">Fire</button><br /><br /> <button type="submit" name="mode" value="flow_rgb">Flow (RGB)</button><br /><br /> <button type="submit" name="mode" value="flow_hsv">Flow (HSV)</button><br /><br /> - <button type="submit" name="mode" value="test">Testmuster</button><br /><br /> + <button type="submit" name="mode" value="test">Test Pattern</button><br /><br /> </form> <!-- Update slider on the fly --> <script>