commit 4cb92b95c1b8e57c9b8e33099fd1cb79dde8aac9
parent fafde29d9b748343e1443cda457be85334d81e13
Author: Gerd Beuster <gerd@frombelow.net>
Date: Fri, 12 Feb 2021 17:52:47 +0100
PyIgnition & documentation updated
Diffstat:
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>