Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNostalghia <b.vehikel@googlemail.com>2009-08-22 16:14:38 (GMT)
committer Nostalghia <b.vehikel@googlemail.com>2009-08-22 16:14:38 (GMT)
commitbd288e7666cca83b06921ee32ddb30b64dadcb89 (patch)
tree56503c46a93d799695150cb512513dc427f752c0
initial commitv1
-rw-r--r--.gitignore29
-rw-r--r--AUTHORS2
-rw-r--r--COPYING675
-rw-r--r--MANIFEST49
-rw-r--r--NEWS2
-rw-r--r--activity/activity-kandid.svg89
-rw-r--r--activity/activity.info7
-rw-r--r--ep_buzzwordconstraint_sugar.py41
-rw-r--r--ep_colorconstraint_bw.py62
-rw-r--r--ep_colorconstraint_gray.py62
-rw-r--r--ep_colorconstraint_none.py67
-rw-r--r--ep_directionconstraint_none.py76
-rw-r--r--ep_formater_html.py345
-rw-r--r--ep_layer_image.py128
-rw-r--r--ep_layer_letterpress.py216
-rw-r--r--ep_layer_markovchain.py157
-rw-r--r--ep_layer_referencepattern.py123
-rw-r--r--ep_merger_alphaimage.py116
-rw-r--r--ep_merger_border.py132
-rw-r--r--ep_merger_flip.py108
-rw-r--r--ep_merger_layermask.py134
-rw-r--r--ep_merger_mask.py131
-rw-r--r--ep_merger_rectangulartile.py112
-rw-r--r--ep_merger_straight.py81
-rw-r--r--ep_positionconstraint_centered.py53
-rw-r--r--ep_positionconstraint_none.py49
-rw-r--r--exon_buzzword.py129
-rw-r--r--exon_color.py135
-rw-r--r--exon_direction.py123
-rw-r--r--exon_position.py118
-rw-r--r--ka_canvas.py42
-rw-r--r--ka_controller.py357
-rw-r--r--ka_debug.py75
-rw-r--r--ka_extensionpoint.py81
-rw-r--r--ka_factory.py73
-rw-r--r--ka_importer.py46
-rw-r--r--ka_incoming.py68
-rw-r--r--ka_random.py163
-rw-r--r--ka_task.py78
-rw-r--r--kandid.glade1531
-rw-r--r--kandid.py80
-rw-r--r--kandidtube.py132
-rw-r--r--locale/en/LC_MESSAGES/net.sourceforge.kandid.mobin0 -> 404 bytes
-rw-r--r--locale/en/activity.linfo2
-rw-r--r--model_allele.py32
-rw-r--r--model_constraintpool.py143
-rw-r--r--model_layer.py167
-rw-r--r--model_locus.py69
-rw-r--r--model_population.py243
-rw-r--r--model_protozoon.py315
-rw-r--r--po/Kandid.pot49
-rw-r--r--po/en.po49
-rwxr-xr-xsetup.py20
-rw-r--r--test_enumerator.py97
-rw-r--r--test_suite.py385
55 files changed, 7848 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b675269
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,29 @@
+# no backup files
+*~
+# no binaries
+*.pyc
+# no archives
+*.xo
+*.tar.bz2
+*.zip
+# no test data
+*.[Ss][Vv][Gg]
+*.[Pp][Nn][Gg]
+*.[Pp][Dd][Ff]
+*.[Jj][Pp][Gg]
+*.[Jj][Pp][Ee][Gg]
+testoutput_*
+# no covarage datas
+coverage.lst
+.coverage
+# no project files
+.project
+.pydevproject
+.classpath
+.directory
+.project
+*.asc
+*.gpg
+# allow logo
+!activity-kandid.svg
+
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..9a6140d
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,2 @@
+Thomas Jourdan <null@null.null>
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..10926e8
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,675 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..95f9f38
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,49 @@
+activity/activity.info
+activity/activity-kandid.svg
+locale/en/activity.linfo
+locale/en/LC_MESSAGES/net.sourceforge.kandid.mo
+po/Kandid.pot
+po/en.po
+activity.py
+ep_buzzwordconstraint_sugar.py
+ep_colorconstraint_bw.py
+ep_colorconstraint_gray.py
+ep_colorconstraint_none.py
+ep_directionconstraint_none.py
+ep_formater_html.py
+ep_layer_image.py
+ep_layer_letterpress.py
+ep_layer_markovchain.py
+ep_layer_referencepattern.py
+ep_merger_alphaimage.py
+ep_merger_border.py
+ep_merger_flip.py
+ep_merger_layermask.py
+ep_merger_mask.py
+ep_merger_rectangulartile.py
+ep_merger_straight.py
+ep_positionconstraint_centered.py
+ep_positionconstraint_none.py
+exon_buzzword.py
+exon_color.py
+exon_direction.py
+exon_position.py
+ka_canvas.py
+ka_controller.py
+ka_debug.py
+ka_extensionpoint.py
+ka_factory.py
+ka_importer.py
+ka_incoming.py
+ka_random.py
+ka_task.py
+kandid.glade
+kandid.py
+kandidtube.py
+model_allele.py
+model_constraintpool.py
+model_layer.py
+model_locus.py
+model_population.py
+model_protozoon.py
+setup.py
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..bdfd335
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,2 @@
++
+
diff --git a/activity/activity-kandid.svg b/activity/activity-kandid.svg
new file mode 100644
index 0000000..c1c4e09
--- /dev/null
+++ b/activity/activity-kandid.svg
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ enable-background="new 0 0 55 55"
+ height="55px"
+ version="1.1"
+ viewBox="0 0 55 55"
+ width="55px"
+ x="0px"
+ xml:space="preserve"
+ y="0px"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="activity-kandi.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
+ id="metadata26"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs24"><inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 27.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="55 : 27.5 : 1"
+ inkscape:persp3d-origin="27.5 : 18.333333 : 1"
+ id="perspective28" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ </defs><sodipodi:namedview
+ inkscape:window-height="1040"
+ inkscape:window-width="1054"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#ffffff"
+ id="base"
+ showgrid="false"
+ inkscape:zoom="8.9818182"
+ inkscape:cx="27.5"
+ inkscape:cy="27.5"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:current-layer="activity-connect" /><g
+ display="block"
+ id="activity-connect">
+ <path
+ style="fill:#ffffff;stroke:#010101;stroke-width:4.34408617;stroke-linejoin:bevel"
+ id="path2160"
+ d="M 22.979863,14.879617 C 22.981751,19.68323 19.261999,23.58024 14.672228,23.582216 C 10.080571,23.584191 6.3589317,19.691131 6.3570444,14.887518 C 6.3570444,14.885543 6.3570444,14.881592 6.3570444,14.879617 C 6.35327,10.076004 10.073022,6.1789938 14.662792,6.1750435 C 19.252562,6.1730683 22.976089,10.066128 22.977976,14.869741 C 22.979863,14.873692 22.979863,14.877642 22.979863,14.879617 z" /><path
+ style="fill:#ffffff;stroke:#010101;stroke-width:2.25;stroke-linejoin:bevel"
+ id="path3134"
+ d="M 26.475,9.535 C 26.475,11.967 24.504,13.94 22.072,13.941 C 19.639,13.942 17.667,11.971 17.665,9.539 C 17.665,9.538 17.665,9.536 17.665,9.535 C 17.664,7.102 19.635,5.13 22.067,5.128 C 24.5,5.127 26.472,7.098 26.474,9.53 C 26.475,9.532 26.475,9.534 26.475,9.535 z" /><path
+ style="fill:#ffffff;stroke:#010101;stroke-width:2.25;stroke-linejoin:bevel"
+ id="path3136"
+ d="M 45.400826,7.3082794 C 45.400826,9.7402794 43.429826,11.713279 40.998826,11.714279 C 38.564826,11.715279 36.592826,9.7442794 36.590826,7.3122794 C 36.590826,7.3112794 36.590826,7.3092794 36.590826,7.3082794 C 36.590826,4.8752794 38.561826,2.9032794 40.992826,2.9012794 C 43.426826,2.9002794 45.398826,4.8712794 45.400826,7.3032794 C 45.400826,7.3052794 45.400826,7.3072794 45.400826,7.3082794 z" /><path
+ style="fill:#ffffff;stroke:#010101;stroke-width:5.09838343;stroke-linejoin:bevel"
+ id="path3138"
+ d="M 50.54676,26.735695 C 50.54676,31.97851 45.85037,36.227928 40.055535,36.230083 C 34.258317,36.232238 29.559545,31.984975 29.554779,26.744314 C 29.554779,26.742159 29.554779,26.737849 29.554779,26.735695 C 29.552397,21.492879 34.248786,17.243462 40.043621,17.239152 C 45.840839,17.236997 50.539611,21.484259 50.544377,26.72492 C 50.54676,26.72923 50.54676,26.73354 50.54676,26.735695 z" /><path
+ style="fill:#ffffff;stroke:#010101;stroke-width:2.25;stroke-linejoin:bevel"
+ id="path3142"
+ d="M 14.118,21.892 C 14.119,24.325 12.148,26.297 9.716,26.298 C 7.284,26.299 5.311,24.328 5.31,21.896 C 5.31,21.895 5.31,21.893 5.31,21.892 C 5.309,19.459 7.28,17.487 9.712,17.485 C 12.144,17.484 14.117,19.455 14.118,21.887 C 14.118,21.889 14.118,21.891 14.118,21.892 z" /><path
+ style="fill:#ffffff;stroke:#010101;stroke-width:5.43040991;stroke-linejoin:bevel"
+ id="path3146"
+ d="M 40.027794,38.256097 C 40.031555,42.022712 32.618467,45.076892 23.471522,45.079989 C 14.320816,45.081538 6.903967,42.028907 6.9002059,38.262293 C 6.9002059,38.259195 6.9002059,38.257646 6.9002059,38.256097 C 6.8964448,34.487934 14.309533,31.433754 23.456478,31.432206 C 32.603423,31.429108 40.024033,34.481739 40.027794,38.249902 C 40.027794,38.251451 40.027794,38.253 40.027794,38.256097 z" />
+</g></svg> \ No newline at end of file
diff --git a/activity/activity.info b/activity/activity.info
new file mode 100644
index 0000000..47eb4fe
--- /dev/null
+++ b/activity/activity.info
@@ -0,0 +1,7 @@
+[Activity]
+name = Kandid
+service_name = net.sourceforge.kandid
+class = activity.KandidActivity
+icon = activity-kandid
+activity_version = 1
+show_launcher = yes
diff --git a/ep_buzzwordconstraint_sugar.py b/ep_buzzwordconstraint_sugar.py
new file mode 100644
index 0000000..0bec310
--- /dev/null
+++ b/ep_buzzwordconstraint_sugar.py
@@ -0,0 +1,41 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import model_locus
+
+class SugarBuzzwordConstraint(model_locus.Locus):
+ """SugarBuzzwordConstraint
+ """
+
+ def __init__(self, trunk):
+ """Buzzword constraint constructor
+ """
+ super(SugarBuzzwordConstraint, self).__init__(trunk)
+
+ def get_wordlist(self):
+ """Returns a list of predefined words.
+ """
+ return (u'Sugar Labs', \
+ u'Sugar on a Stick', \
+ u'learning platform', \
+ u'activity', \
+ u'education', \
+ u'open source', \
+ u'collaboration', \
+ u'participation', \
+ u'independence', \
+ )
diff --git a/ep_colorconstraint_bw.py b/ep_colorconstraint_bw.py
new file mode 100644
index 0000000..a55c511
--- /dev/null
+++ b/ep_colorconstraint_bw.py
@@ -0,0 +1,62 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import model_locus
+
+EPSILON = 0.00001
+
+class BwColorConstraint(model_locus.Locus):
+ """BwColorConstraint
+ """
+
+ def __init__(self, trunk):
+ """Color constraint constructor
+ """
+ super(BwColorConstraint, self).__init__(trunk)
+
+ def filter(self, rgba):
+ """Set constraint to red, green, blue and alpha values.
+ post: len(__return__) == 4
+ """
+ gray = round((rgba[0] + rgba[1] + rgba[2]) / 3.0)
+ alpha = round(rgba[3])
+ return (gray, gray, gray, alpha)
+
+ def randomize(self):
+ """Set black or white and alpha to random values.
+ post: len(__return__) == 4
+ post: __return__[0] == 0 or __return__[0] == 1
+ post: __return__[3] == 0 or __return__[3] == 1
+ """
+ gray = 1.0*random.randint(0, 1)
+ alpha = 1.0*random.randint(0, 1)
+ return (gray, gray, gray, alpha)
+
+ def mutate(self, rgba):
+ """Make random switch between black and white.
+ post: len(__return__) == 4
+ """
+ return self.randomize()
+
+ def explain(self, rgba, alfa=True):
+ """
+ pre: len(rgba) == 4
+ """
+ part1 = 'black' if abs(rgba[0]) < EPSILON else 'white'
+ part2 = ', %d%% opaque' % (100*rgba[3]) if alfa else ''
+ return part1 + part2
diff --git a/ep_colorconstraint_gray.py b/ep_colorconstraint_gray.py
new file mode 100644
index 0000000..c9ec8c6
--- /dev/null
+++ b/ep_colorconstraint_gray.py
@@ -0,0 +1,62 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import ka_random
+import model_locus
+
+class GrayColorConstraint(model_locus.Locus):
+ """GrayColorConstraint
+ """
+
+ def __init__(self, trunk):
+ """Color constraint constructor
+ """
+ super(GrayColorConstraint, self).__init__(trunk)
+
+ def filter(self, rgba):
+ """Set constraint to red, green, blue and alpha values.
+ post: len(__return__) == 4
+ """
+ gray = (rgba[0] + rgba[1] + rgba[2]) / 3.0
+ alpha = rgba[3]
+ return (gray, gray, gray, alpha)
+
+ def randomize(self):
+ """Set lightness and alpha to random values.
+ post: len(__return__) == 4
+ """
+ gray = random.random()
+ return (gray, gray, gray, random.random())
+
+ def mutate(self, rgba):
+ """Make small random changes in hue, lightness, saturation.
+ post: len(__return__) == 4
+ """
+ gray = (rgba[0] + rgba[1] + rgba[2]) / 3.0
+ gray = ka_random.limitate(gray + 0.1 * (random.random() - 0.5))
+ alpha = ka_random.limitate(rgba[3] + 0.1 * (random.random() - 0.5))
+ return (gray, gray, gray, alpha)
+
+ def explain(self, rgba, alfa=True):
+ """
+ pre: len(rgba) == 4
+ """
+ if alfa:
+ return '%d%% gray, %d%% opaque' % (100*rgba[0], 100*rgba[3])
+ else:
+ return '%d%% gray' % (100*rgba[0])
diff --git a/ep_colorconstraint_none.py b/ep_colorconstraint_none.py
new file mode 100644
index 0000000..053a67d
--- /dev/null
+++ b/ep_colorconstraint_none.py
@@ -0,0 +1,67 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import colorsys
+import ka_random
+import model_locus
+
+class NoneColorConstraint(model_locus.Locus):
+ """NoneColorConstraint
+ """
+
+ def __init__(self, trunk):
+ """Color constraint constructor
+ """
+ super(NoneColorConstraint, self).__init__(trunk)
+
+ def filter(self, rgba):
+ """Set constraint to red, green, blue and alpha values.
+ post: len(__return__) == 4
+ """
+ return (rgba[0], rgba[1], rgba[2], rgba[3])
+
+ def randomize(self):
+ """Set red, green, blue and alpha to random values.
+ post: len(__return__) == 4
+ """
+ return (random.random(), random.random(), random.random(), \
+ random.random())
+
+ def mutate(self, rgba):
+ """Make small random changes in hue, lightness, saturation.
+ post: len(__return__) == 4
+ """
+ hue, lightness, saturation = colorsys.rgb_to_hls( \
+ rgba[0], rgba[1], rgba[2])
+ hue = ka_random.cyclic_limitate(hue + 0.1 * (random.random() - 0.5))
+ lightness = ka_random.limitate(lightness + 0.1 * (random.random() - 0.5))
+ saturation = ka_random.limitate(saturation + 0.1 * (random.random() - 0.5))
+ alpha = ka_random.limitate(rgba[3] + 0.1 * (random.random() - 0.5))
+ rgb = colorsys.hls_to_rgb(hue, lightness, saturation)
+ return (rgb[0], rgb[1], rgb[2], alpha)
+
+ def explain(self, rgba, alfa=True):
+ """
+ pre: len(rgba) == 4
+ """
+ if alfa:
+ return '%d%% red, %d%% green, %d%% blue, %d%% opaque' \
+ % (100*rgba[0], 100*rgba[1], 100*rgba[2], 100*rgba[3])
+ else:
+ return '%d%% red, %d%% green, %d%% blue' \
+ % (100*rgba[0], 100*rgba[1], 100*rgba[2])
diff --git a/ep_directionconstraint_none.py b/ep_directionconstraint_none.py
new file mode 100644
index 0000000..35fb6c1
--- /dev/null
+++ b/ep_directionconstraint_none.py
@@ -0,0 +1,76 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import math
+import ka_random
+import model_constraintpool
+import model_locus
+
+DISTANCE_CONSTRAINT = 'distance'
+RADIAN_CONSTRAINT = 'radian'
+
+class NoneDirectionConstraint(model_locus.Locus):
+ """NoneDirectionConstraint
+ """
+
+ cdef = [{'bind' : DISTANCE_CONSTRAINT,
+ 'name' : 'Distance per hop',
+ 'domain': model_constraintpool.FLOAT_RANGE,
+ 'min' : 0.0, 'max': 0.5,
+ },
+ {'bind' : RADIAN_CONSTRAINT,
+ 'name' : 'Rotation in radians',
+ 'domain': model_constraintpool.FLOAT_RANGE,
+ 'min' : -1.0*math.pi, 'max': math.pi,
+ },
+ ]
+
+ def __init__(self, trunk):
+ """Direction constraint constructor
+ """
+ super(NoneDirectionConstraint, self).__init__(trunk)
+
+ def filter(self, radian, distance):
+ """No constraints for radian and distance.
+ post: (-1.0*math.pi) <= __return__[0] <= math.pi
+ post: __return__[1] >= 0.0
+ """
+ return ka_random.radian_limitate(radian), math.fabs(distance)
+
+ def randomize(self):
+ """Set radian and distance to random values.
+ post: (-1.0*math.pi) <= __return__[0] <= math.pi
+ post: __return__[1] >= 0.0
+ """
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ radian_constraint = cpool.get(self, RADIAN_CONSTRAINT)
+ distance_constraint = cpool.get(self, DISTANCE_CONSTRAINT)
+ radian = ka_random.uniform_constrained(radian_constraint)
+ distance = ka_random.uniform_constrained(distance_constraint)
+ return ka_random.radian_limitate(radian), math.fabs(distance)
+
+ def mutate(self, radian, distance):
+ """Make small random changes in radian and distance.
+ post: (-1.0*math.pi) <= __return__[0] <= math.pi
+ post: __return__[1] >= 0.0
+ """
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ radian_constraint = cpool.get(self, RADIAN_CONSTRAINT)
+ distance_constraint = cpool.get(self, DISTANCE_CONSTRAINT)
+ radian += ka_random.jitter_constrained(radian_constraint)
+ distance += ka_random.jitter_constrained(distance_constraint)
+ return ka_random.radian_limitate(radian), math.fabs(distance)
diff --git a/ep_formater_html.py b/ep_formater_html.py
new file mode 100644
index 0000000..8db6de7
--- /dev/null
+++ b/ep_formater_html.py
@@ -0,0 +1,345 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import os
+import sys
+import ka_debug
+import exon_color
+import exon_position
+import exon_direction
+
+ICON_WIDTH = ICON_HEIGHT = 48
+
+class HtmlFormater(object):
+ """HtmlFormater
+ inv: self._indent >= 0
+ """
+
+ generator = u'Minimal Kandid'
+
+ def __init__(self, base_name, unique_id, base_folder):
+ """Constructor for HTML formater."""
+ self._base_name = base_name.replace(' ', '_')
+ self.unique_id = unique_id
+ self._base_folder = base_folder.replace(' ', '_')
+ self._indent = 0
+ self._page = u''
+ self.id_count = 0
+# self.strategy = {}
+ self._header_occured = False # debugging only
+ self._footer_occured = False # debugging only
+ file_path = os.path.join(base_folder, unique_id)
+ try:
+ os.mkdir(file_path)
+ except:
+ ka_debug.err('creating directory [%s] [%s] [%s]' % \
+ (file_path, sys.exc_info()[0], sys.exc_info()[1]))
+
+
+ def _escape(self, text):
+ utext = unicode(text)
+ quoted = u''
+ for uchar in utext:
+ if uchar in [u'<']:
+ quoted += u'&lt;'
+ elif uchar in [u'&']:
+ quoted += u'&amp;'
+ else:
+ quoted += uchar
+ return quoted
+
+ def _append(self, lines):
+ indenting = u' '*(2*self._indent)
+ for line in lines:
+ self._page += indenting + line + u'\n'
+
+ def _append_escaped(self, text):
+ self._page += self._escape(text)
+
+ def _get_id(self):
+ self.id_count += 1
+ return u'id_' + unicode(self.id_count)
+
+ def get_pathname(self, extension, postfix=None):
+ return os.path.join(self._base_folder,
+ self.unique_id,
+ self.get_filename(extension, postfix))
+
+ def get_filename(self, extension, postfix=None):
+ postfix = '' if postfix is None else '_' + postfix
+ return self._base_name + postfix + '.' + extension
+
+ def header(self, title):
+ """
+ pre: not self._header_occured
+ pre: not self._footer_occured
+ """
+ self._append( ( \
+ u'<?xml version="1.0" encoding="UTF-8"?>',
+ u'<?xml-stylesheet href="treestyles.css" type="text/css"?>',
+ u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"',
+ u' "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
+ u'',
+ u'<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">',
+ u'<head>',
+ u' <meta name="generator" content="' + HtmlFormater.generator +'" />',
+ u'',
+ u' <title>' + title + u'</title>',
+ u' <link rel="stylesheet" href="' + self._base_name + u'_files/treestyles.css" type="text/css" />',
+ u' <script type="text/javascript" src="' + self._base_name + u'_files/marktree.js">',
+ u'//<![CDATA[',
+ u'',
+ u' //]]>',
+ u' </script>',
+ u'</head>',
+ u'',
+ u'<body>',
+ u' <div class="basetop">',
+ u' <a href="#" onclick="expandAll(document.getElementById(\'base\'))">Expand</a> -',
+ u' <a href="#" onclick="collapseAll(document.getElementById(\'base\'))">Collapse</a>',
+ u' </div>',
+ u'',
+ u' <div id="base" class="basetext">',
+ u'',
+ u' <ul>',
+ ) )
+ self._header_occured = True
+
+ def footer(self):
+ """
+ pre: self._indent == 0
+ pre: self._header_occured
+ pre: not self._footer_occured
+ """
+ self._append( ( \
+ u' </ul>',
+ u' </div>',
+ u'</body>',
+ u'</html>',
+ ) )
+ self._footer_occured = True
+
+ def _begin_list_item(self, identification):
+ """
+ pre: self._header_occured
+ pre: not self._footer_occured
+ """
+ self._append( ( \
+ u' <li class="basic" style="" id="id_' + identification + u'" >',
+ u' <span style="">',
+ ) )
+
+ def _end_list_item(self):
+ """
+ pre: self._header_occured
+ pre: not self._footer_occured
+ """
+ self._append( ( \
+ u' </span>',
+ u' </li>',
+ ) )
+
+ def _image_item(self, filename, width, height, description, newline=True):
+ if newline:
+ self._append( ( \
+ u' <br />',
+ ) )
+ self._append( ( \
+ u' <img src="' + filename + u'"' \
+ + u' width="' + unicode(width) + u'"' \
+ + u' height="' + unicode(height) + u'"' \
+ + u' alt="' + description + u'"'
+ + u' title="' + description + u'" border="1" />',
+ ) )
+
+ def begin_list(self, text):
+ """
+ pre: self._header_occured
+ pre: not self._footer_occured
+ """
+ self._indent += 1
+ self._append( ( \
+ u' <li class="col" style="" id="' + self._get_id() + u'">',
+ self._escape(text),
+ u' <ul class="subexp">',
+ ) )
+
+ def end_list(self):
+ """
+ pre: self._header_occured
+ pre: not self._footer_occured
+ """
+ self._append( ( \
+ u' </ul>',
+ u' </li>',
+ ) )
+ self._indent -= 1
+
+ def text_item(self, text):
+ """
+ pre: self._header_occured
+ pre: not self._footer_occured
+ """
+ self._begin_list_item(self._get_id())
+ self._append_escaped(text)
+ self._end_list_item()
+
+ def text_list(self, title, text_list):
+ """
+ pre: self._header_occured
+ pre: not self._footer_occured
+ """
+ self._begin_list_item(self._get_id())
+ self._append_escaped(title)
+ for text in text_list:
+ self._append_escaped(text + u', ')
+ self._end_list_item()
+
+ def color_item(self, color, text, alfa=True):
+ """
+ pre: self._header_occured
+ pre: not self._footer_occured
+ """
+ identification = self._get_id()
+ pathname = self.get_pathname('png', postfix=identification)
+ filename = self.get_filename('png', postfix=identification)
+ surface = exon_color.Color.make_icon(color.rgba, alfa, ICON_WIDTH, ICON_HEIGHT)
+ surface.write_to_png(pathname)
+
+ description = color.explain(alfa)
+ self._begin_list_item(identification)
+ self._append_escaped(text)
+ self._image_item(filename, ICON_WIDTH, ICON_HEIGHT, description)
+ self._end_list_item()
+
+ def alfa_item(self, alfa, text):
+ """
+ pre: self._header_occured
+ pre: not self._footer_occured
+ """
+ identification = self._get_id()
+ pathname = self.get_pathname('png', postfix=identification)
+ filename = self.get_filename('png', postfix=identification)
+ surface = exon_color.Color.make_icon((1.0, 1.0, 1.0, alfa), True, ICON_WIDTH, ICON_HEIGHT)
+ surface.write_to_png(pathname)
+
+ description = '%d%% opaque' % (100*alfa)
+ self._begin_list_item(self._get_id())
+ self._append_escaped(text)
+ self._image_item(filename, ICON_WIDTH, ICON_HEIGHT, description)
+ self._end_list_item()
+
+ def color_array(self, color_list, text, alfa=True):
+ """
+ pre: self._header_occured
+ pre: not self._footer_occured
+ """
+ self._begin_list_item(self._get_id())
+ self._append_escaped(text)
+ self._append( (u' <br />',) )
+ for color in color_list:
+ identification = self._get_id()
+ pathname = self.get_pathname('png', postfix=identification)
+ filename = self.get_filename('png', postfix=identification)
+ surface = color.make_icon(color.rgba, alfa, ICON_WIDTH, ICON_HEIGHT)
+ surface.write_to_png(pathname)
+ description = color.explain(alfa)
+ self._image_item(filename, ICON_WIDTH, ICON_HEIGHT, \
+ description, newline=False)
+ self._end_list_item()
+
+ def position_item(self, position, text):
+ self.position_array([position], text)
+
+ def position_array(self, position_list, text):
+ """
+ pre: self._header_occured
+ pre: not self._footer_occured
+ pre: len(position_list) >= 0
+ """
+ description = u'['
+ for position in position_list:
+ description += position.explain() + u'; '
+ description += u']'
+
+ self._begin_list_item(self._get_id())
+ self._append_escaped(text + u' ' + description)
+ identification = self._get_id()
+ pathname = self.get_pathname('png', postfix=identification)
+ filename = self.get_filename('png', postfix=identification)
+ surface = exon_position.Position.make_icon(position_list, ICON_WIDTH, ICON_HEIGHT)
+ surface.write_to_png(pathname)
+ self._image_item(filename, ICON_WIDTH, ICON_HEIGHT, description)
+ self._end_list_item()
+
+ def direction_item(self, direction, text):
+ self.direction_array([direction], text)
+
+ def direction_array(self, direction_list, text):
+ """
+ pre: self._header_occured
+ pre: not self._footer_occured
+ pre: len(direction_list) >= 0
+ """
+ description = u'['
+ for direction in direction_list:
+ description += direction.explain() + u'; '
+ description += u']'
+
+ self._begin_list_item(self._get_id())
+ self._append_escaped(text + u' ' + description)
+ identification = self._get_id()
+ pathname = self.get_pathname('png', postfix=identification)
+ filename = self.get_filename('png', postfix=identification)
+ surface = exon_direction.Direction.make_icon(direction_list, ICON_WIDTH, ICON_HEIGHT)
+ surface.write_to_png(pathname)
+ self._image_item(filename, ICON_WIDTH, ICON_HEIGHT, description)
+ self._end_list_item()
+
+ def surface_item(self, surface, text, description):
+ """
+ pre: self._header_occured
+ pre: not self._footer_occured
+ """
+ identification = self._get_id()
+ pathname = self.get_pathname('png', postfix=identification)
+ filename = self.get_filename('png', postfix=identification)
+ surface.write_to_png(pathname)
+ width = surface.get_width()
+ height = surface.get_height()
+
+ self._begin_list_item(self._get_id())
+ self._append_escaped(text)
+ self._image_item(filename, width, height, description)
+ self._end_list_item()
+
+ def write_html_file(self, file_path):
+ """
+ pre: self._header_occured
+ pre: self._footer_occured
+ pre: self._indent == 0
+ """
+ out_file = None
+ try:
+ out_file = open(file_path, 'w')
+ out_file.write(self._page.encode('utf-8'))
+ except:
+ ka_debug.err('failed writing [%s] [%s] [%s]' % \
+ (file_path, sys.exc_info()[0], sys.exc_info()[1]))
+ finally:
+ if out_file:
+ out_file.close()
diff --git a/ep_layer_image.py b/ep_layer_image.py
new file mode 100644
index 0000000..683fe28
--- /dev/null
+++ b/ep_layer_image.py
@@ -0,0 +1,128 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import os
+import cairo
+import model_layer
+import ka_random
+import ka_importer
+
+class ImageLayer(model_layer.Layer):
+ """ImageLayer
+ """
+
+ def __init__(self, trunk):
+ """Constructor for bitmap image layer."""
+ super(ImageLayer, self).__init__(trunk)
+ self.selected_image = ''
+ self.alpha_blending = 1.0
+
+ def __eq__(self, other):
+ """Equality based on the cells color components."""
+ equal = isinstance(other, ImageLayer) \
+ and super(ImageLayer, self).__eq__(other) \
+ and self.selected_image == other.selected_image \
+ and self.alpha_blending == other.alpha_blending
+ return equal
+
+ def randomize(self):
+ """Randomize the layers components."""
+ super(ImageLayer, self).randomize()
+ image_list = ka_importer.get_rgb_image_list()
+ if len(image_list) > 0:
+ self.selected_image = \
+ image_list[random.randint(0, len(image_list)-1)]
+ self.alpha_blending = random.random()
+
+ def mutate(self):
+ """Make small random changes to the layers components."""
+ super(ImageLayer, self).mutate()
+ if ka_random.is_mutating():
+ image_list = ka_importer.get_rgb_image_list()
+ if len(image_list) > 0:
+ self.selected_image = \
+ image_list[random.randint(0, len(image_list)-1)]
+ if ka_random.is_mutating():
+ self.alpha_blending += ka_random.jitter(0.2)
+ self.alpha_blending = ka_random.limitate(self.alpha_blending)
+
+ def shuffle(self):
+ """Shuffle similar componets."""
+ super(ImageLayer, self).shuffle()
+
+ def crossingover(self, other):
+ """
+ pre: isinstance(other, ImageLayer)
+ pre: isinstance(self, ImageLayer)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ new_one = ImageLayer(self.get_trunk())
+ cross_sequence = self.crossingover_base(new_one, other, 2)
+ new_one.selected_image = self.selected_image if cross_sequence[0] \
+ else other.selected_image
+ new_one.alpha_blending = self.alpha_blending if cross_sequence[0] \
+ else other.alpha_blending
+ return new_one
+
+ def draw(self, ctx, width, height):
+ """
+ pre: ctx is not None
+ pre: width > 0
+ pre: height > 0
+ pre: width == height
+ """
+ self.begin_draw(ctx, width, height)
+
+ if os.path.isfile(self.selected_image):
+ surface = cairo.ImageSurface.create_from_png(self.selected_image)
+ ctx.translate(-0.5, -0.5)
+ ctx.scale(1.0/surface.get_width(), 1.0/surface.get_height())
+ ctx.set_source_surface(surface)
+ ctx.paint_with_alpha(self.alpha_blending)
+
+ def explain(self, formater):
+ """
+ pre: formater is not None
+ """
+ formater.alfa_item(self.alpha_blending, 'alfa blendig: %d%%' \
+ % (100*self.alpha_blending))
+ if os.path.isfile(self.selected_image):
+ png_surface = cairo.ImageSurface.create_from_png(self.selected_image)
+ width = height = 48
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ ctx = cairo.Context(surface)
+ ctx.scale(width, height)
+ ctx.scale(1.0/png_surface.get_width(), 1.0/png_surface.get_height())
+ ctx.set_source_surface(png_surface)
+ ctx.paint()
+ formater.surface_item(surface,
+ 'image file:' + self.selected_image,
+ self.selected_image)
+
+ def copy(self):
+ """The Voronoi diagram layers copy constructor.
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = ImageLayer(self.get_trunk())
+ self.copy_base(new_one)
+ new_one.selected_image = self.selected_image
+ new_one.alpha_blending = self.alpha_blending
+ return new_one
diff --git a/ep_layer_letterpress.py b/ep_layer_letterpress.py
new file mode 100644
index 0000000..7ce19d9
--- /dev/null
+++ b/ep_layer_letterpress.py
@@ -0,0 +1,216 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import math
+import random
+import pango
+import pangocairo
+import model_layer
+import ka_random
+import model_constraintpool
+import exon_color
+import exon_position
+import exon_direction
+import exon_buzzword
+
+class LetterPress(model_layer.Layer):
+ """LetterPress
+ inv: len(self.direction_steps) > 0
+ """
+
+ cdef = [{'bind' : 'family',
+ 'name' : 'Font family',
+ 'domain': model_constraintpool.STRING_1_OF_N,
+ 'enum' : ['Sans',
+ 'Nimbus Sans L',
+ 'Monospace',]},
+ {'bind' : 'style',
+ 'name' : 'Font style',
+ 'domain': model_constraintpool.INT_1_OF_N,
+ 'enum' : [('Normal', pango.STYLE_NORMAL),
+ ('Oblique', pango.STYLE_OBLIQUE),
+ ('Italic', pango.STYLE_ITALIC),]},
+ {'bind' : 'size',
+ 'name' : 'Font size',
+ 'domain': model_constraintpool.INT_RANGE,
+ 'min' : 4, 'max': 64},
+ ]
+
+ font_style = {pango.STYLE_NORMAL: 'Normal',
+ pango.STYLE_OBLIQUE: 'Oblique',
+ pango.STYLE_ITALIC: 'Italic',
+ }
+
+ def __init__(self, trunk):
+ """LetterPress diagram layer constructor"""
+ super(LetterPress, self).__init__(trunk)
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ self.textcolor = exon_color.Color(self.path, 0, 0, 0, 1)
+ self.family = cpool.get(self, 'family')[0]
+ self.style = cpool.get(self, 'style')[0]
+ self.size = cpool.get(self, 'size')[0]
+ self.center = exon_position.Position(self.path, 0.0, 0.0)
+ self.direction_steps = [exon_direction.Direction(self.path, 0.0, 0.0)]
+ self.buzzwords = exon_buzzword.Buzzword(self.path, [''])
+
+ def __eq__(self, other):
+ """Equality based on the objects components."""
+ equal = isinstance(other, LetterPress) \
+ and super(LetterPress, self).__eq__(other) \
+ and self.textcolor == other.textcolor \
+ and self.family == other.family \
+ and self.style == other.style \
+ and self.size == other.size \
+ and self.center == other.center \
+ and len(self.direction_steps) == len(other.direction_steps) \
+ and self.buzzwords == other.buzzwords
+ if equal:
+ for index, direction in enumerate(self.direction_steps):
+ equal = equal and direction == other.direction_steps[index]
+ return equal
+
+ def randomize(self):
+ """Randomize the layers components."""
+ super(LetterPress, self).randomize()
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ family_constraint = cpool.get(self, 'family')
+ style_constraint = cpool.get(self, 'style')
+ size_constraint = cpool.get(self, 'size')
+ self.textcolor.randomize()
+ self.family = random.choice(family_constraint)
+ self.style = random.choice(style_constraint)
+ self.size = random.randint(size_constraint[0], size_constraint[1])
+ self.center.randomize()
+ self.buzzwords.randomize()
+ self.direction_steps = []
+ for i in range(random.randint(1, 6)):
+ direction = exon_direction.Direction(self.path, 0.0, 0.0)
+ direction.randomize()
+ self.direction_steps.append(direction)
+
+ def mutate(self):
+ """Make small random changes to the layers components."""
+ super(LetterPress, self).mutate()
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ family_constraint = cpool.get(self, 'family')
+ style_constraint = cpool.get(self, 'style')
+ size_constraint = cpool.get(self, 'size')
+ self.textcolor.mutate()
+ if ka_random.is_mutating():
+ self.family = random.choice(family_constraint)
+ if ka_random.is_mutating():
+ self.style = random.choice(style_constraint)
+ if ka_random.is_mutating():
+ self.size += random.randint(-12, 12)
+ self.size = ka_random.limitate_range(self.size, \
+ size_constraint[0], \
+ size_constraint[1])
+ self.center.mutate()
+ self.buzzwords.mutate()
+ for direction in self.direction_steps:
+ direction.mutate()
+
+ def shuffle(self):
+ """Shuffle similar componets."""
+ super(LetterPress, self).shuffle()
+ self.buzzwords.shuffle()
+# self.direction_steps.shuffle()
+
+ def crossingover(self, other):
+ """
+ pre: isinstance(other, LetterPress)
+ pre: isinstance(self, LetterPress)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ new_one = LetterPress(self.get_trunk())
+ crossing = self.crossingover_base(new_one, other, 5)
+ new_one.textcolor = self.textcolor.crossingover(other.textcolor)
+ new_one.center = self.center if crossing[0] else other.center
+ new_one.buzzwords = self.buzzwords if crossing[1] else other.buzzwords
+ new_one.family = self.family if crossing[2] else other.family
+ new_one.style = self.style if crossing[3] else other.style
+ new_one.size = self.size if crossing[4] else other.size
+ new_one.direction_steps = ka_random.crossingover(self.direction_steps, \
+ other.direction_steps)
+ return new_one
+
+ def draw(self, ctx, width, height):
+ """
+ pre: ctx is not None
+ pre: width > 0
+ pre: height > 0
+ pre: width == height
+ """
+# self.operator = cairo.OPERATOR_OVER
+ self.begin_draw(ctx, width, height)
+
+ ctx.scale(1.0/width, 1.0/height)
+ pango_ctx = pangocairo.CairoContext(ctx)
+ px, py = self.center.x_pos, self.center.y_pos
+ fi = di = -1
+ for word in self.buzzwords.wordlist:
+ di = (di+1) % len(self.direction_steps)
+ step = self.direction_steps[di]
+ px += step.offset * math.cos(step.radian)
+ py += step.offset * math.sin(step.radian)
+
+ layout = pango_ctx.create_layout()
+ fi = (fi+1) % len(self.family)
+ desc = pango.FontDescription(self.family[fi])
+ desc.set_size(self.size * pango.SCALE)
+ desc.set_style(self.style)
+ # desc.set_weight(self.font["weight"])
+ layout.set_text(word.encode('utf-8'))
+ layout.set_font_description(desc)
+ layout.set_alignment(pango.ALIGN_CENTER)
+ rgba = self.textcolor.rgba
+ pango_ctx.set_source_rgba(rgba[0], rgba[1], rgba[2], rgba[3])
+ pango_ctx.update_layout(layout)
+ pixel_extents = layout.get_pixel_extents()
+# extents = layout.get_extents()
+ dx = pixel_extents[1][2] / 2
+ dy = pixel_extents[1][3] / 2
+ pango_ctx.move_to((width * px) - dx, (height * py) - dy)
+ pango_ctx.show_layout(layout)
+
+ def explain(self, formater):
+# super(LetterPress, self).explain(formater)
+ formater.text_list('buzzwords: ', self.buzzwords.wordlist)
+ formater.color_item(self.textcolor, 'text color:')
+ formater.text_item(self.family \
+ + ', ' + LetterPress.font_style[self.style] \
+ + ', ' + str(self.size))
+ formater.position_item(self.center, 'center:')
+ formater.direction_array(self.direction_steps, 'direction:')
+
+ def copy(self):
+ """The LetterPress diagram layers copy constructor.
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = LetterPress(self.get_trunk())
+ self.copy_base(new_one)
+ new_one.textcolor = self.textcolor.copy()
+ new_one.family = self.family
+ new_one.style = self.style
+ new_one.size = self.size
+ new_one.center = self.center.copy()
+ new_one.buzzwords = self.buzzwords.copy()
+ new_one.direction_steps = ka_random.copy_list(self.direction_steps)
+ return new_one
diff --git a/ep_layer_markovchain.py b/ep_layer_markovchain.py
new file mode 100644
index 0000000..066a5ac
--- /dev/null
+++ b/ep_layer_markovchain.py
@@ -0,0 +1,157 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import exon_color
+import model_layer
+import ka_random
+
+class MarkovChainLayer(model_layer.Layer):
+ """Markov chain layer
+ inv: self.cell_colors is not None and len(self.cell_colors) == self.states
+ inv: self.probability is not None and len(self.probability) == self.states
+ inv: 1 <= self.square_base <= 7
+ """
+
+ def __init__(self, trunk):
+ """Markov chain layer constructor"""
+ super(MarkovChainLayer, self).__init__(trunk)
+ self.states = 4
+ self.square_base = 3
+ self.cell_colors = [None] * self.states
+ for cix in range(len(self.cell_colors)):
+ self.cell_colors[cix] = exon_color.Color(self.path, 0, 0, 0, 1)
+ self.probability = [[1.0 / self.states] * self.states for i in range(self.states)]
+# for row, row_probabilities in enumerate(other.probability):
+# for col, cell_probability in enumerate(row_probabilities):
+# self.probability[row][col] = 1.0 / self.states
+
+ def __eq__(self, other):
+ """Equality based on the cells color components."""
+ equal = isinstance(other, MarkovChainLayer) \
+ and super(MarkovChainLayer, self).__eq__(other) \
+ and self.states == other.states \
+ and self.square_base == other.square_base
+ if equal:
+ for cix, cell_color in enumerate(self.cell_colors):
+ equal = equal and cell_color == other.cell_colors[cix]
+ if equal:
+ for row, row_probabilities in enumerate(self.probability):
+ for col, cell_probability in enumerate(row_probabilities):
+ equal = equal and cell_probability == other.probability[row][col]
+ return equal
+
+ def randomize(self):
+ """Randomize the layers components."""
+ super(MarkovChainLayer, self).randomize()
+ for cix in range(len(self.cell_colors)):
+ self.cell_colors[cix].randomize()
+ for row, row_probabilities in enumerate(self.probability):
+ for col in range(len(row_probabilities)):
+ self.probability[row][col] = random.random() / self.states
+ self.square_base = random.randint(1, 7)
+
+ def mutate(self):
+ """Make small random changes to the layers components."""
+ super(MarkovChainLayer, self).mutate()
+ for cix in range(len(self.cell_colors)):
+ self.cell_colors[cix].mutate()
+ for row, row_probabilities in enumerate(self.probability):
+ for col, cell_probability in enumerate(row_probabilities):
+ val = ka_random.limitate(cell_probability \
+ + ka_random.jitter(1.0 / self.states))
+ self.probability[row][col] = val
+ if ka_random.is_mutating():
+ self.square_base += random.randint(-1, 1)
+ self.square_base = ka_random.limitate_range(self.square_base, 1, 7)
+
+ def shuffle(self):
+ """Shuffle similar componets.
+ For example exchange foreground and background color."""
+ super(MarkovChainLayer, self).shuffle()
+ if ka_random.is_shuffling():
+ random.shuffle(self.cell_colors)
+ if ka_random.is_shuffling():
+ random.shuffle(self.probability)
+ for row_probabilities in self.probability:
+ if ka_random.is_shuffling():
+ random.shuffle(row_probabilities)
+
+ def crossingover(self, other):
+ """
+ pre: isinstance(other, MarkovChainLayer)
+ pre: isinstance(self, MarkovChainLayer)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ new_one = MarkovChainLayer(self.get_trunk())
+ cross_sequence = self.crossingover_base(new_one, other, \
+ 1 + len(self.cell_colors))
+ if cross_sequence[0]:
+ new_one.square_base = self.square_base
+ else:
+ new_one.square_base = other.square_base
+ for cix in range(len(self.cell_colors)):
+ if cross_sequence[1 + cix]:
+ new_one.cell_colors[cix] = self.cell_colors[cix].copy()
+ else:
+ new_one.cell_colors[cix] = other.cell_colors[cix].copy()
+ return new_one
+
+ def draw(self, ctx, width, height):
+ """
+ pre: ctx is not None
+ pre: width > 0
+ pre: height > 0
+ pre: width == height
+ """
+ self.begin_draw(ctx, width, height)
+ cell_state = 0
+ dim = self.square_base**2
+ delta_x, delta_y = 1.0 / dim, 1.0 / dim
+ for y_index in range(dim):
+ for x_index in range(dim):
+ rgba = self.cell_colors[cell_state].rgba
+ ctx.set_source_rgba(rgba[0], rgba[1], rgba[2], rgba[3])
+ ctx.rectangle((x_index-0.5*dim)*delta_x, \
+ (y_index-0.5*dim)*delta_y, \
+ delta_x, delta_y)
+ ctx.fill()
+ cell_state = (cell_state+1) % self.states
+
+ def explain(self, formater):
+# super(MarkovChainLayer, self).explain(formater)
+ formater.color_array(self.cell_colors, 'cell colors:')
+
+ def copy(self):
+ """The Markov chain layer copy constructor
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = MarkovChainLayer(self.get_trunk())
+ self.copy_base(new_one)
+ new_one.states = self.states
+ new_one.square_base = self.square_base
+ new_one.cell_colors = [None] * self.states
+ for cix in range(len(self.cell_colors)):
+ new_one.cell_colors[cix] = self.cell_colors[cix].copy()
+ new_one.probability = [[0.0] * self.states for i in range(self.states)]
+ for row, row_probabilities in enumerate(self.probability):
+ for col, cell_probability in enumerate(row_probabilities):
+ new_one.probability[row][col] = cell_probability
+ return new_one
diff --git a/ep_layer_referencepattern.py b/ep_layer_referencepattern.py
new file mode 100644
index 0000000..0487d2c
--- /dev/null
+++ b/ep_layer_referencepattern.py
@@ -0,0 +1,123 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import cairo
+import model_layer
+
+image_list = []
+
+class ReferencePattern(model_layer.Layer):
+ """ReferencePattern
+ """
+
+ def __init__(self, trunk):
+ """Constructor for bitmap image layer."""
+ super(ReferencePattern, self).__init__(trunk)
+
+ def __eq__(self, other):
+ """Equality based on the cells color components."""
+ equal = isinstance(other, ReferencePattern) \
+ and super(ReferencePattern, self).__eq__(other)
+ return equal
+
+ def randomize(self):
+ """Randomize the layers components."""
+ super(ReferencePattern, self).randomize()
+
+ def mutate(self):
+ """Make small random changes to the layers components."""
+ super(ReferencePattern, self).mutate()
+
+ def shuffle(self):
+ """Shuffle similar componets."""
+ super(ReferencePattern, self).shuffle()
+
+ def crossingover(self, other):
+ """
+ pre: isinstance(other, ReferencePattern)
+ pre: isinstance(self, ReferencePattern)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ new_one = ReferencePattern(self.get_trunk())
+ return new_one
+
+ def draw(self, ctx, width, height):
+ """
+ pre: ctx is not None
+ pre: width > 0
+ pre: height > 0
+ pre: width == height
+ """
+ self.begin_draw(ctx, width, height)
+
+ ctx.set_source_rgba(1.0, 1.0, 1.0, 0.5)
+ ctx.rectangle(-0.5, -0.5, 1.0, 1.0)
+ ctx.fill()
+
+ linear = cairo.LinearGradient(-0.5, -0.5, 1.0, 1.0)
+ linear.add_color_stop_rgba(0.00, 1.0, 0.0, 0.0, 1.0)
+ linear.add_color_stop_rgba(1.00, 1.0, 1.0, 0.0, 0.0)
+
+ ctx.rectangle(-0.5, -0.5, 1.0, 1.0)
+ ctx.set_source(linear)
+ ctx.fill()
+
+ ctx.set_line_width(0.05)
+ ctx.set_source_rgba(0.0, 0.0, 0.0, 0.5)
+ ctx.rectangle(-0.5, -0.5, 1.0, 1.0)
+ ctx.stroke()
+
+ ctx.set_source_rgba(1.0, 1.0, 1.0, 0.25)
+ ctx.select_font_face("Sans",
+ cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
+ ctx.set_font_size(0.2)
+ text = 'L'
+ ctx.move_to(0.0, 0.0)
+ ctx.show_text(text)
+ ctx.set_font_size(0.03)
+ for ix in range(11):
+ for iy in range(11):
+ ctx.move_to(ix/10.0-0.5, iy/10.0-0.5)
+ ctx.show_text(str(ix)+'&'+str(iy))
+
+
+ ctx.set_line_width(0.01)
+ ctx.set_source_rgba(0.75, 0.75, 0.75, 0.75)
+ diagonal = 0.05
+ ctx.move_to(-diagonal, -diagonal)
+ ctx.line_to( diagonal, diagonal)
+ ctx.move_to( diagonal, -diagonal)
+ ctx.line_to(-diagonal, diagonal)
+ ctx.stroke()
+
+ def explain(self, formater):
+ """
+ pre: formater is not None
+ """
+# super(ReferencePattern, self).explain(formater)
+ pass
+
+ def copy(self):
+ """The Voronoi diagram layers copy constructor.
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = ReferencePattern(self.get_trunk())
+ self.copy_base(new_one)
+ return new_one
diff --git a/ep_merger_alphaimage.py b/ep_merger_alphaimage.py
new file mode 100644
index 0000000..7e0c052
--- /dev/null
+++ b/ep_merger_alphaimage.py
@@ -0,0 +1,116 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import cairo
+import model_allele
+import ka_random
+import ka_importer
+
+class AlphaImageMerger(model_allele.Allele):
+ """AlphaImageMerger
+ """
+ constraint_wieght = (0.0, 0.5)
+
+ def __init__(self, trunk):
+ """Constructor for a simple merger."""
+ super(AlphaImageMerger, self).__init__(trunk)
+ self.selected_image = ''
+
+ def __eq__(self, other):
+ """Equality based on same instance."""
+ equal = isinstance(other, AlphaImageMerger) \
+ and self.selected_image == other.selected_image
+ return equal
+
+ def randomize(self):
+ """No member variables. Nothing to do."""
+ image_list = ka_importer.get_alpha_image_list()
+ self.selected_image = image_list[random.randint(0, len(image_list)-1)]
+
+ def mutate(self):
+ """No member variables. Nothing to do."""
+ if ka_random.is_mutating():
+ image_list = ka_importer.get_alpha_image_list()
+ self.selected_image = image_list[random.randint(0, len(image_list)-1)]
+
+ def shuffle(self):
+ """No member variables. Nothing to do."""
+ pass
+
+ def crossingover(self, other):
+ """
+ pre: isinstance(other, AlphaImageMerger)
+ pre: isinstance(self, AlphaImageMerger)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ new_one = AlphaImageMerger(self.get_trunk())
+ cross_sequence = ka_random.crossing_sequence(1)
+ new_one.selected_image = self.selected_image if cross_sequence[0] \
+ else other.selected_image
+ return new_one
+
+ def draw_single_layer(self, single_layer, mask_layer, ctx, width, height):
+ """
+ pre: single_layer is not None
+ pre: ctx is not None
+ pre: width > 0
+ pre: height > 0
+ pre: width == height
+ """
+ # paint one layer
+ ctx.save()
+
+ msk_surface = cairo.ImageSurface.create_from_png(self.selected_image)
+ ctx.save()
+ ctx.translate(-0.5, -0.5)
+ ctx.scale(1.0/msk_surface.get_width(), 1.0/msk_surface.get_height())
+ ctx.mask_surface(msk_surface, 0.0, 0.0)
+ ctx.restore()
+
+ single_layer.draw(ctx, width, height)
+ ctx.restore()
+
+ def explain(self):
+ """
+ post len(__return__) == 3
+ """
+ png_surface = cairo.ImageSurface.create_from_png(self.selected_image)
+ width = height = 48
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ ctx = cairo.Context(surface)
+ ctx.scale(width, height)
+ ctx.scale(1.0/png_surface.get_width(), 1.0/png_surface.get_height())
+ ctx.set_source_surface(png_surface)
+ ctx.paint()
+
+ return u'Transparent image merger, image ' \
+ + unicode(self.selected_image), \
+ surface, \
+ self.selected_image
+
+ def copy(self):
+ """A copy constructor.
+ post: isinstance(__return__, AlphaImageMerger)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = AlphaImageMerger(self.get_trunk())
+ new_one.selected_image = self.selected_image
+ return new_one
diff --git a/ep_merger_border.py b/ep_merger_border.py
new file mode 100644
index 0000000..548cb31
--- /dev/null
+++ b/ep_merger_border.py
@@ -0,0 +1,132 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import cairo
+import model_allele
+import ka_random
+
+class BorderMerger(model_allele.Allele):
+ """BorderMerger
+ inv: 0.0 <= self.border_weight <= 0.5
+ inv: self.border_alfa == 0.0 or self.border_alfa == 1.0
+ """
+ constraint_wieght = (0.0, 0.5)
+
+ def __init__(self, trunk):
+ """Constructor for a simple merger."""
+ super(BorderMerger, self).__init__(trunk)
+ self.border_weight = 0.25
+ self.border_alfa = 1.0
+ self.constraint_weight = (0.0, 0.5)
+
+ def __eq__(self, other):
+ """Equality based on same instance."""
+ equal = isinstance(other, BorderMerger) \
+ and self.border_weight == other.border_weight \
+ and self.border_alfa == other.border_alfa
+ return equal
+
+ def randomize(self):
+ """No member variables. Nothing to do."""
+ self.border_weight = random.uniform(self.constraint_weight[0], self.constraint_weight[1])
+ self.border_alfa = 1.0 if random.randint(0, 1) > 0 else 0.0
+
+ def mutate(self):
+ """No member variables. Nothing to do."""
+ if ka_random.is_mutating():
+ self.border_weight += ka_random.jitter_constrained(self.constraint_weight)
+ self.border_weight = ka_random.limitate_range(self.border_weight, self.constraint_weight[0], self.constraint_weight[1])
+ if ka_random.is_mutating():
+ self.border_alfa = 1.0 if random.randint(0, 1) > 0 else 0.0
+
+ def shuffle(self):
+ """No member variables. Nothing to do."""
+ pass
+
+ def crossingover(self, other):
+ """
+ pre: isinstance(other, BorderMerger)
+ pre: isinstance(self, BorderMerger)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ new_one = BorderMerger(self.get_trunk())
+ cross_sequence = ka_random.crossing_sequence(2)
+ new_one.border_weight = self.border_weight if cross_sequence[0] else other.border_weight
+ new_one.border_alfa = self.border_alfa if cross_sequence[1] else other.border_alfa
+ return new_one
+
+ def draw_single_layer(self, single_layer, mask_layer, ctx, width, height):
+ """
+ pre: single_layer is not None
+ pre: ctx is not None
+ pre: width > 0
+ pre: height > 0
+ pre: width == height
+ """
+ # paint one layer
+ ctx.save()
+ msk_surface = ctx.get_target().create_similar(cairo.CONTENT_ALPHA,
+ width, height)
+ msk_ctx = cairo.Context(msk_surface)
+ msk_width = msk_surface.get_width()
+ msk_height = msk_surface.get_height()
+
+ # fill the whole background with an alpha value.
+ msk_ctx.set_operator(cairo.OPERATOR_SOURCE)
+ msk_ctx.set_source_rgba(1.0, 1.0, 1.0, self.border_alfa)
+ msk_ctx.paint()
+
+ # fill the interior with an alpha value.
+ msk_ctx.set_operator(cairo.OPERATOR_SOURCE)
+ msk_ctx.set_source_rgba(1.0, 1.0, 1.0, 1.0-self.border_alfa)
+ msk_ctx.rectangle(self.border_weight*msk_width,
+ self.border_weight*msk_height,
+ (1.0-2.0*self.border_weight)*msk_height,
+ (1.0-2.0*self.border_weight)*msk_width)
+ msk_ctx.fill()
+
+ ctx.save()
+ ctx.translate(-0.5, -0.5)
+ ctx.scale(1.0/msk_width, 1.0/msk_height)
+ ctx.mask_surface(msk_surface, 0.0, 0.0)
+ ctx.restore()
+
+ single_layer.draw(ctx, width, height)
+ ctx.restore()
+
+ def explain(self):
+ """
+ post len(__return__) == 3
+ """
+ return u'Border merger, border weight=' + unicode(self.border_weight) \
+ + u' alfa=' + unicode(self.border_alfa), \
+ None, \
+ None
+
+ def copy(self):
+ """A copy constructor.
+ post: isinstance(__return__, BorderMerger)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = BorderMerger(self.get_trunk())
+ new_one.border_weight = self.border_weight
+ new_one.border_alfa = self.border_alfa
+ return new_one
diff --git a/ep_merger_flip.py b/ep_merger_flip.py
new file mode 100644
index 0000000..19c5bb9
--- /dev/null
+++ b/ep_merger_flip.py
@@ -0,0 +1,108 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import ka_random
+import model_allele
+
+class FlipMerger(model_allele.Allele):
+ """FlipMerger: Reverse the layer horizontally or vertically.
+ inv: self.xFlip == -1.0 or self.xFlip == 1.0
+ inv: self.yFlip == -1.0 or self.yFlip == 1.0
+ """
+
+ def __init__(self, trunk):
+ """Constructor for a flip merger."""
+ super(FlipMerger, self).__init__(trunk)
+ self.xFlip = 1.0
+ self.yFlip = 1.0
+
+ def __eq__(self, other):
+ """Equality based on fliping horizontally or vertically."""
+ equal = isinstance(other, FlipMerger) \
+ and self.xFlip == other.xFlip \
+ and self.yFlip == other.yFlip
+ return equal
+
+ def randomize(self):
+ """Randomize the layers components."""
+ self.xFlip = 1.0 if random.randint(0, 1) > 0 else -1.0
+ self.yFlip = 1.0 if random.randint(0, 1) > 0 else -1.0
+
+ def mutate(self):
+ """Make small random changes to the layers components."""
+ if ka_random.is_mutating():
+ self.xFlip = 1.0 if random.randint(0, 1) > 0 else -1.0
+ if ka_random.is_mutating():
+ self.yFlip = 1.0 if random.randint(0, 1) > 0 else -1.0
+
+ def shuffle(self):
+ """Shuffle similar componets."""
+ pass
+
+ def crossingover(self, other):
+ """
+ pre: isinstance(other, FlipMerger)
+ pre: isinstance(self, FlipMerger)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ new_one = FlipMerger(self.get_trunk())
+ cross_sequence = ka_random.crossing_sequence(2)
+ new_one.xFlip = self.xFlip if cross_sequence[0] else other.xFlip
+ new_one.yFlip = self.yFlip if cross_sequence[1] else other.yFlip
+ return new_one
+
+ def draw_single_layer(self, single_layer, mask_layer, ctx, width, height):
+ """
+ pre: single_layer is not None
+ pre: ctx is not None
+ pre: width > 0
+ pre: height > 0
+ pre: width == height
+ """
+ # paint one layer
+ ctx.scale(self.xFlip, self.yFlip)
+ ctx.save()
+ single_layer.draw(ctx, width, height)
+ ctx.restore()
+
+ def explain(self):
+ """
+ post len(__return__) == 3
+ """
+ if self.xFlip and self.yFlip:
+ text = u'Flip merger: flip horizontally and vertically.'
+ elif self.xFlip:
+ text = u'Flip merger: flip horizontally.'
+ elif self.yFlip:
+ text = u'Flip merger: flip vertically.'
+ else:
+ text = u'Flip merger: did not flip.'
+ return text, None, None
+
+ def copy(self):
+ """A copy constructor.
+ post: isinstance(__return__, FlipMerger)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = FlipMerger(self.get_trunk())
+ new_one.xFlip = self.xFlip
+ new_one.yFlip = self.yFlip
+ return new_one
diff --git a/ep_merger_layermask.py b/ep_merger_layermask.py
new file mode 100644
index 0000000..98272e5
--- /dev/null
+++ b/ep_merger_layermask.py
@@ -0,0 +1,134 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import cairo
+import model_allele
+import ka_random
+
+class LayermaskMerger(model_allele.Allele):
+ """LayermaskMerger
+ inv: self.scale_x >= 1.0
+ inv: self.scale_y >= 1.0
+ """
+
+ def __init__(self, trunk):
+ """Constructor for a simple merger."""
+ super(LayermaskMerger, self).__init__(trunk)
+ self.scale_x = 1.0
+ self.scale_y = 1.0
+ self.constraint_scale = (1.0, 2.5)
+
+ def __eq__(self, other):
+ """Equality based on same instance."""
+ equal = isinstance(other, LayermaskMerger) \
+ and self.scale_x == other.scale_x \
+ and self.scale_y == other.scale_y
+ return equal
+
+ def randomize(self):
+ """No member variables. Nothing to do."""
+ self.scale_x = random.uniform(self.constraint_scale[0], self.constraint_scale[1])
+ self.scale_y = random.uniform(self.constraint_scale[0], self.constraint_scale[1])
+
+ def mutate(self):
+ """No member variables. Nothing to do."""
+ if ka_random.is_mutating():
+ self.scale_x += ka_random.jitter_constrained(self.constraint_scale)
+ self.scale_x = ka_random.limitate_range(self.scale_x, self.constraint_scale[0], self.constraint_scale[1])
+ if ka_random.is_mutating():
+ self.scale_y += ka_random.jitter_constrained(self.constraint_scale)
+ self.scale_y = ka_random.limitate_range(self.scale_y, self.constraint_scale[0], self.constraint_scale[1])
+
+ def shuffle(self):
+ """No member variables. Nothing to do."""
+ pass
+
+ def crossingover(self, other):
+ """
+ pre: isinstance(other, LayermaskMerger)
+ pre: isinstance(self, LayermaskMerger)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ new_one = LayermaskMerger(self.get_trunk())
+ cross_sequence = ka_random.crossing_sequence(2)
+ new_one.scale_x = self.scale_x if cross_sequence[0] else other.scale_x
+ new_one.scale_y = self.scale_y if cross_sequence[1] else other.scale_y
+ return new_one
+
+ def draw_single_layer(self, single_layer, mask_layer, ctx, width, height):
+ """
+ pre: single_layer is not None
+ pre: ctx is not None
+ pre: width > 0
+ pre: height > 0
+ pre: width == height
+ """
+ # paint one layer
+ ctx.save()
+ msk_surface = ctx.get_target().create_similar(cairo.CONTENT_ALPHA,
+ width, height)
+ # draw mask.
+ msk_ctx = cairo.Context(msk_surface)
+ sx, sy = self.scale_x**2, self.scale_y**2
+# sx, sy = 2.5**2, 2.5**2
+ msk_ctx.scale(sx*width, sy*height)
+# msk_ctx.translate(0.5-0.5/sx, 0.5-0.5/sy)
+ msk_ctx.translate(0.5/sx, 0.5/sy)
+
+ # draw mask background
+ msk_ctx.set_operator(cairo.OPERATOR_SOURCE)
+ msk_ctx.set_source_rgba(0.0, 0.0, 0.0, 0.5)
+ msk_ctx.paint()
+ # draw mask layer
+ msk_ctx.set_operator(cairo.OPERATOR_SOURCE)
+ mask_layer.draw(msk_ctx, width, height)
+# dummy = ep_layer_referencepattern.ReferencePattern(self.path+'/TODO')
+# dummy.operator = cairo.OPERATOR_SOURCE
+# dummy.draw(msk_ctx, width, height)
+# msk_surface.write_to_png('/dev/shm/TODO.png')
+
+ ctx.save()
+ ctx.translate(-0.5, -0.5)
+ ctx.scale(1.0/msk_surface.get_width(), 1.0/msk_surface.get_height())
+ ctx.mask_surface(msk_surface, 0, 0)
+ ctx.restore()
+
+ single_layer.draw(ctx, width, height)
+ ctx.restore()
+
+ def explain(self):
+ """
+ post len(__return__) == 3
+ """
+ return 'Layermask merger, scale_x**2=' + unicode(self.scale_x**2) \
+ + u' scale_y**2=' + unicode(self.scale_y**2), \
+ None, \
+ None
+
+ def copy(self):
+ """A copy constructor.
+ post: isinstance(__return__, LayermaskMerger)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = LayermaskMerger(self.get_trunk())
+ new_one.scale_x = self.scale_x
+ new_one.scale_y = self.scale_y
+ return new_one
diff --git a/ep_merger_mask.py b/ep_merger_mask.py
new file mode 100644
index 0000000..910ab4d
--- /dev/null
+++ b/ep_merger_mask.py
@@ -0,0 +1,131 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import math
+import random
+import cairo
+import ka_random
+import model_allele
+import exon_position
+import exon_direction
+
+class MaskMerger(model_allele.Allele):
+ """MaskMerger
+ """
+
+ def __init__(self, trunk):
+ """Constructor for a simple merger."""
+ super(MaskMerger, self).__init__(trunk)
+ self.alfa1 = 1.0
+ self.alfa2 = 0.0
+ self.center = exon_position.Position(self.path, 0.0, 0.0)
+ self.direction = exon_direction.Direction(self.path, 0.0, 0.0)
+ self.constraint_alfa = (0.0, 1.0)
+
+ def __eq__(self, other):
+ """Equality based on same instance."""
+ equal = isinstance(other, MaskMerger) \
+ and self.alfa1 == other.alfa1 \
+ and self.alfa2 == other.alfa2 \
+ and self.center == other.center \
+ and self.direction == other.direction
+ return equal
+
+ def randomize(self):
+ """
+ """
+ self.alfa1 = random.uniform(self.constraint_alfa[0], self.constraint_alfa[1])
+ self.alfa2 = random.uniform(self.constraint_alfa[0], self.constraint_alfa[1])
+ self.center.randomize()
+ self.direction.randomize()
+
+ def mutate(self):
+ """
+ """
+ if ka_random.is_mutating():
+ self.alfa1 += ka_random.jitter_constrained(self.constraint_alfa)
+ self.alfa1 = ka_random.limitate_range(self.alfa1, self.constraint_alfa[0], self.constraint_alfa[1])
+ if ka_random.is_mutating():
+ self.alfa2 += ka_random.jitter_constrained(self.constraint_alfa)
+ self.alfa2 = ka_random.limitate_range(self.alfa2, self.constraint_alfa[0], self.constraint_alfa[1])
+ self.center.mutate()
+ self.direction.mutate()
+
+ def shuffle(self):
+ """
+ """
+ pass
+
+ def crossingover(self, other):
+ """
+ pre: isinstance(other, MaskMerger)
+ pre: isinstance(self, MaskMerger)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ new_one = MaskMerger(self.get_trunk())
+ crossing = ka_random.crossing_sequence(2)
+ new_one.alfa1 = self.alfa1 if crossing[0] else other.alfa1
+ new_one.alfa2 = self.alfa2 if crossing[0] else other.alfa2
+ new_one.center = self.center if crossing[0] else other.center
+ new_one.direction = self.direction if crossing[1] else other.direction
+ return new_one
+
+ def draw_single_layer(self, single_layer, mask_layer, ctx, width, height):
+ """
+ pre: single_layer is not None
+ pre: ctx is not None
+ pre: width > 0
+ pre: height > 0
+ pre: width == height
+ """
+ # paint one layer masked by an linear gradient
+ ctx.save()
+
+ dx = self.direction.offset * math.cos(self.direction.radian)
+ dy = self.direction.offset * math.sin(self.direction.radian)
+ linear = cairo.LinearGradient(self.center.x_pos + dx,
+ self.center.y_pos + dy,
+ self.center.x_pos - dx,
+ self.center.y_pos - dy)
+ linear.add_color_stop_rgba(1.0, 1.0, 1.0, 1.0, self.alfa1)
+ linear.add_color_stop_rgba(0.0, 0.0, 0.0, 0.0, self.alfa2)
+ ctx.mask(linear)
+
+ single_layer.draw(ctx, width, height)
+ ctx.restore()
+
+ def explain(self):
+ """
+ post len(__return__) == 3
+ """
+ return u'Mask merger, center=' + self.center.explain() \
+ + u' direction=' + self.direction.explain(), \
+ None, \
+ None
+
+ def copy(self):
+ """A copy constructor.
+ post: isinstance(__return__, MaskMerger)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = MaskMerger(self.get_trunk())
+ new_one.center = self.center.copy()
+ new_one.direction = self.direction.copy()
+ return new_one
diff --git a/ep_merger_rectangulartile.py b/ep_merger_rectangulartile.py
new file mode 100644
index 0000000..1db1736
--- /dev/null
+++ b/ep_merger_rectangulartile.py
@@ -0,0 +1,112 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import ka_random
+import model_allele
+
+class RectangularTileMerger(model_allele.Allele):
+ """RectangularTileMerger: Reverse the layer horizontally or vertically.
+ inv: self.xTiles > 0
+ inv: self.yTiles > 0
+ """
+
+ def __init__(self, trunk):
+ """Constructor for a flip merger."""
+ super(RectangularTileMerger, self).__init__(trunk)
+ self.xTiles = 1
+ self.yTiles = 1
+
+ def __eq__(self, other):
+ """Equality based on fliping horizontally or vertically."""
+ equal = isinstance(other, RectangularTileMerger) \
+ and self.xTiles == other.xTiles \
+ and self.yTiles == other.yTiles
+ return equal
+
+ def randomize(self):
+ """Randomize the layers components."""
+ self.xTiles = random.randint(2, 4)
+ self.yTiles = random.randint(2, 4)
+
+ def mutate(self):
+ """Make small random changes to the layers components."""
+ if ka_random.is_mutating():
+ self.xTiles += random.randint(-1, 1)
+ self.xTiles = 1 if self.xTiles < 1 else self.xTiles
+ if ka_random.is_mutating():
+ self.yTiles += random.randint(-1, 1)
+ self.yTiles = 1 if self.yTiles < 1 else self.yTiles
+
+ def shuffle(self):
+ """Shuffle similar componets."""
+ pass
+
+ def crossingover(self, other):
+ """
+ pre: isinstance(other, RectangularTileMerger)
+ pre: isinstance(self, RectangularTileMerger)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ new_one = RectangularTileMerger(self.get_trunk())
+ cross_sequence = ka_random.crossing_sequence(2)
+ new_one.xTiles = self.xTiles if cross_sequence[0] else other.xTiles
+ new_one.yTiles = self.yTiles if cross_sequence[1] else other.yTiles
+ return new_one
+
+ def draw_single_layer(self, single_layer, mask_layer, ctx, width, height):
+ """
+ pre: single_layer is not None
+ pre: ctx is not None
+ pre: width > 0
+ pre: height > 0
+ pre: width == height
+ """
+ # repeat painting layer
+ dx, dy = 1.0 / self.xTiles, 1.0 / self.yTiles
+ for tx in range(self.xTiles):
+ for ty in range(self.yTiles):
+ ctx.save()
+ ctx.translate((tx-0.5*self.xTiles)*dx+0.5*dx, \
+ (ty-0.5*self.yTiles)*dy+0.5*dy)
+ ctx.scale(dx, dy)
+ ctx.save()
+ single_layer.draw(ctx, width, height)
+ ctx.restore()
+ ctx.restore()
+
+ def explain(self):
+ """
+ post len(__return__) == 3
+ """
+ return u'Rectangular tile merger: %d*x, %d*y' \
+ % (self.xTiles, self.yTiles), \
+ None, \
+ None
+
+ def copy(self):
+ """A copy constructor.
+ post: isinstance(__return__, RectangularTileMerger)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = RectangularTileMerger(self.get_trunk())
+ new_one.xTiles = self.xTiles
+ new_one.yTiles = self.yTiles
+ return new_one
diff --git a/ep_merger_straight.py b/ep_merger_straight.py
new file mode 100644
index 0000000..77ee0e1
--- /dev/null
+++ b/ep_merger_straight.py
@@ -0,0 +1,81 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import model_allele
+
+class StraightMerger(model_allele.Allele):
+ """StraightMerger
+ """
+
+ def __init__(self, trunk):
+ """Constructor for a simple merger."""
+ super(StraightMerger, self).__init__(trunk)
+
+ def __eq__(self, other):
+ """Equality based on same instance."""
+ equal = isinstance(other, StraightMerger)
+ return equal
+
+ def randomize(self):
+ """No member variables. Nothing to do."""
+ pass
+
+ def mutate(self):
+ """No member variables. Nothing to do."""
+ pass
+
+ def shuffle(self):
+ """No member variables. Nothing to do."""
+ pass
+
+ def crossingover(self, other):
+ """
+ pre: isinstance(other, StraightMerger)
+ pre: isinstance(self, StraightMerger)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ new_one = StraightMerger(self.get_trunk())
+ return new_one
+
+ def draw_single_layer(self, single_layer, mask_layer, ctx, width, height):
+ """
+ pre: single_layer is not None
+ pre: ctx is not None
+ pre: width > 0
+ pre: height > 0
+ pre: width == height
+ """
+ # paint one layer
+ ctx.save()
+ single_layer.draw(ctx, width, height)
+ ctx.restore()
+
+ def explain(self):
+ return 'Straight merger', \
+ None, \
+ None
+
+ def copy(self):
+ """A copy constructor.
+ post: isinstance(__return__, StraightMerger)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = StraightMerger(self.get_trunk())
+ return new_one
diff --git a/ep_positionconstraint_centered.py b/ep_positionconstraint_centered.py
new file mode 100644
index 0000000..12fd99c
--- /dev/null
+++ b/ep_positionconstraint_centered.py
@@ -0,0 +1,53 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import ka_random
+import model_locus
+
+class CenteredPositionConstraint(model_locus.Locus):
+ """CenteredPositionConstraint
+ """
+
+ def __init__(self, trunk):
+ """Position constraint constructor
+ """
+ super(CenteredPositionConstraint, self).__init__(trunk)
+
+ def filter(self, x_pos, y_pos):
+ """Set constraint x- and y-position.
+ post: len(__return__) == 2
+ """
+ xp = (x_pos + 0.5) / 2.0
+ yp = (y_pos + 0.5) / 2.0
+ return xp, yp
+
+ def randomize(self):
+ """Set x- and y-position to random values.
+ post: len(__return__) == 2
+ """
+ return random.gauss(0.5, 0.1), random.gauss(0.5, 0.1)
+
+ def mutate(self, x_pos, y_pos):
+ """Make small random changes in x- and y-position.
+ post: len(__return__) == 2
+ """
+ xp = x_pos + ka_random.jitter(0.2)
+ xp = (2.0*xp + random.gauss(0.5, 0.1)) / 3.0
+ yp = y_pos + ka_random.jitter(0.2)
+ yp = (2.0*yp + random.gauss(0.5, 0.1)) / 3.0
+ return xp, yp
diff --git a/ep_positionconstraint_none.py b/ep_positionconstraint_none.py
new file mode 100644
index 0000000..31dd0ff
--- /dev/null
+++ b/ep_positionconstraint_none.py
@@ -0,0 +1,49 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import ka_random
+import model_locus
+
+class NonePositionConstraint(model_locus.Locus):
+ """NonePositionConstraint
+ """
+
+ def __init__(self, trunk):
+ """Position constraint constructor
+ """
+ super(NonePositionConstraint, self).__init__(trunk)
+
+ def filter(self, x_pos, y_pos):
+ """Set constraint x- and y-position.
+ post: len(__return__) == 2
+ """
+ return x_pos, y_pos
+
+ def randomize(self):
+ """Set x- and y-position to random values.
+ post: len(__return__) == 2
+ """
+ return random.random(), random.random()
+
+ def mutate(self, x_pos, y_pos):
+ """Make small random changes in x- and y-position.
+ post: len(__return__) == 2
+ """
+ xp = x_pos + ka_random.jitter(0.2)
+ yp = y_pos + ka_random.jitter(0.2)
+ return xp, yp
diff --git a/exon_buzzword.py b/exon_buzzword.py
new file mode 100644
index 0000000..523d1d6
--- /dev/null
+++ b/exon_buzzword.py
@@ -0,0 +1,129 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import ka_random
+import ka_extensionpoint
+import model_constraintpool
+import model_allele
+
+BUZZWORD_CONSTRAINTS = 'buzzwordconstraint'
+
+class Buzzword(model_allele.Allele):
+ """Buzzword
+ inv: len(self.wordlist) > 0
+ """
+
+ cdef = [{'bind' : BUZZWORD_CONSTRAINTS,
+ 'name' : 'Buzzwords',
+ 'domain': model_constraintpool.STRING_1_OF_N,
+ 'enum' : ka_extensionpoint.list_extensions(BUZZWORD_CONSTRAINTS)
+ },
+ ]
+
+ def __init__(self, trunk, words):
+ """Buzzword constructor
+ pre: len(words) > 0
+ """
+ super(Buzzword, self).__init__(trunk)
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ constraint_name = cpool.get(self, BUZZWORD_CONSTRAINTS)[0]
+ self.constraint = ka_extensionpoint.create(constraint_name, self.path)
+ self.wordlist = words
+
+ def __eq__(self, other):
+ """Equality based on list of strings."""
+ equal = isinstance(other, Buzzword) \
+ and len(self.wordlist) == len(other.wordlist)
+ if equal:
+ for index, word in enumerate(self.wordlist):
+ equal = equal and word == other.wordlist[index]
+ return equal
+
+# def __ne__(self, other):
+# return not self.__eq__(other)
+#
+# def __hash__(self):
+# raise TypeError("Genome objects are unhashable")
+
+ def randomize(self):
+ """Create a random word list.
+ Result is a subset of the word list from
+ extension point 'ep_buzzwordconstraint'.
+ """
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ constraints = cpool.get(self, BUZZWORD_CONSTRAINTS)
+ self.constraint = ka_extensionpoint.create(random.choice(constraints), self.path)
+
+ full_wordlist = self.constraint.get_wordlist()
+ approximate_count = random.randint(1, len(full_wordlist)-1)
+ self.wordlist = []
+ self.wordlist.append(random.choice(full_wordlist))
+ for i in range(approximate_count):
+ candidate = random.choice(full_wordlist)
+ if candidate not in self.wordlist:
+ self.wordlist.append(candidate)
+
+ def mutate(self):
+ """Mutate word list.
+ """
+ full_wordlist = self.constraint.get_wordlist()
+
+ # change a word
+ if ka_random.is_mutating():
+ candidate = random.choice(full_wordlist)
+ if candidate not in self.wordlist:
+ self.wordlist[random.randint(0, len(self.wordlist)-1)] = candidate
+
+ # change number of words
+ if ka_random.is_mutating():
+ new_count = len(self.wordlist) + random.randint(-1, 1)
+ if new_count > len(self.wordlist):
+ # append one
+ self.wordlist.insert(random.randint(0, len(self.wordlist)-1), \
+ random.choice(full_wordlist))
+ elif new_count < len(self.wordlist) and len(self.wordlist) >= 2:
+ #remove one
+ del self.wordlist[random.randint(0, len(self.wordlist)-1)]
+
+ def shuffle(self):
+ """Exchange position of words in word list"""
+ random.shuffle(self.wordlist)
+
+ def crossingover(self, other):
+ """Merges both word lists and returns a copy of Buzzword.
+ pre: isinstance(other, Buzzword)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ new_one = Buzzword(self.get_trunk(), [''])
+ new_one.wordlist = ka_random.crossingover(self.wordlist, other.wordlist)
+ return new_one.copy()
+
+ def copy(self):
+ """A word list copy constructor.
+ post: isinstance(__return__, Buzzword)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = Buzzword(self.get_trunk(), [''])
+ new_one.wordlist = []
+ for word in self.wordlist:
+ new_one.wordlist.append(word)
+ new_one.constraint = self.constraint
+ return new_one
diff --git a/exon_color.py b/exon_color.py
new file mode 100644
index 0000000..cf3150d
--- /dev/null
+++ b/exon_color.py
@@ -0,0 +1,135 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import cairo
+import ka_extensionpoint
+import ka_random
+import model_constraintpool
+import model_allele
+
+EPSILON = 0.00001
+COLOR_CONSTRAINT = 'colorconstraint'
+
+class Color(model_allele.Allele):
+ """Color
+ inv: len(self.rgba) == 4
+ inv: 0.0 <= self.rgba[0] <= 1.0
+ inv: 0.0 <= self.rgba[1] <= 1.0
+ inv: 0.0 <= self.rgba[2] <= 1.0
+ inv: 0.0 <= self.rgba[3] <= 1.0
+ """
+
+ cdef = [{'bind' : COLOR_CONSTRAINT,
+ 'name' : 'Color constraint',
+ 'domain': model_constraintpool.STRING_1_OF_N,
+ 'enum' : ka_extensionpoint.list_extensions(COLOR_CONSTRAINT)
+ },
+ ]
+
+ def __init__(self, trunk, red, green, blue, alpha):
+ """Color constructor
+ pre: 0.0 <= red <= 1.0
+ pre: 0.0 <= green <= 1.0
+ pre: 0.0 <= blue <= 1.0
+ pre: 0.0 <= alpha <= 1.0
+ """
+ super(Color, self).__init__(trunk)
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ constraint_name = cpool.get(self, COLOR_CONSTRAINT)[0]
+ self.constraint = ka_extensionpoint.create(constraint_name, self.path)
+ self.rgba = self.constraint.filter((red, green, blue, alpha))
+
+ def __eq__(self, other):
+ """Equality based on color components."""
+ return isinstance(other, Color) \
+ and abs(self.rgba[0] - other.rgba[0]) < EPSILON \
+ and abs(self.rgba[1] - other.rgba[1]) < EPSILON \
+ and abs(self.rgba[2] - other.rgba[2]) < EPSILON \
+ and abs(self.rgba[3] - other.rgba[3]) < EPSILON
+
+ def randomize(self):
+ """Set red, green, blue and alpha to random values."""
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ constraints = cpool.get(self, COLOR_CONSTRAINT)
+ self.constraint = ka_extensionpoint.create(random.choice(constraints), self.path)
+ self.rgba = self.constraint.randomize()
+
+ def mutate(self):
+ """Make small random changes in hue, lightness, saturation."""
+ if ka_random.is_mutating():
+ if ka_random.is_mutating():
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ constraints = cpool.get(self, COLOR_CONSTRAINT)
+ self.constraint = ka_extensionpoint.create(random.choice(constraints), self.path)
+ self.rgba = self.constraint.mutate(self.rgba)
+
+ def shuffle(self):
+ """Not implemented."""
+ pass
+
+ def crossingover(self, other):
+ """Returns either a copy of self or a copy of other.
+ pre: isinstance(other, Color)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ randseq = ka_random.crossing_sequence(1)
+ return self.copy() if randseq[0] else other.copy()
+
+ def copy(self):
+ """A color copy constructor
+ post: isinstance(__return__, Color)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = Color(self.get_trunk(), 0.0, 0.0, 0.0, 0.0)
+ new_one.rgba = (self.rgba[0], self.rgba[1], self.rgba[2], self.rgba[3])
+ new_one.constraint = self.constraint
+ return new_one
+
+ def explain(self, alfa=True):
+ return self.constraint.explain(self.rgba, alfa)
+
+ @staticmethod
+ def make_icon(rgba, alfa, width, height):
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ ctx = cairo.Context(surface)
+ if alfa:
+ # paint checker board background
+ steps = 4
+ delta = (1.0 * width) / (1.0 * steps)
+ ctx.set_operator(cairo.OPERATOR_SOURCE)
+ for row in range(steps):
+ for col in range(steps):
+ ctx.rectangle(col * delta, row * delta, delta, delta)
+ if (col + row) % 2 == 0:
+ ctx.set_source_rgb(0.4, 0.4, 0.4)
+ else:
+ ctx.set_source_rgb(0.6, 0.6, 0.6)
+ ctx.fill()
+ # paint color and alfa
+ ctx.set_operator(cairo.OPERATOR_OVER)
+ ctx.set_source_rgba(rgba[0], rgba[1], rgba[2], rgba[3])
+ else:
+ # paint color
+ ctx.set_operator(cairo.OPERATOR_OVER)
+ ctx.set_source_rgb(rgba[0], rgba[1], rgba[2])
+ ctx.paint()
+ return surface
+
diff --git a/exon_direction.py b/exon_direction.py
new file mode 100644
index 0000000..efd435f
--- /dev/null
+++ b/exon_direction.py
@@ -0,0 +1,123 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import math
+import cairo
+import ka_extensionpoint
+import ka_random
+import model_constraintpool
+import model_allele
+
+EPSILON = 0.00001
+DIRECTION_CONSTRAINT = 'directionconstraint'
+
+class Direction(model_allele.Allele):
+ """Direction
+ """
+
+ cdef = [{'bind' : DIRECTION_CONSTRAINT,
+ 'name' : 'Direction constraint',
+ 'domain': model_constraintpool.STRING_1_OF_N,
+ 'enum' : ka_extensionpoint.list_extensions(DIRECTION_CONSTRAINT)
+ },
+ ]
+
+ def __init__(self, trunk, radian, offset):
+ """Direction constructor
+ """
+ super(Direction, self).__init__(trunk)
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ constraint_name = cpool.get(self, DIRECTION_CONSTRAINT)[0]
+ self.constraint = ka_extensionpoint.create(constraint_name, self.path)
+ self.radian, self.offset = self.constraint.filter(radian, offset)
+
+ def __eq__(self, other):
+ """Equality based on radian and offset."""
+ return isinstance(other, Direction) \
+ and abs(self.radian - other.radian) < EPSILON \
+ and abs(self.offset - other.offset) < EPSILON
+
+# def __ne__(self, other):
+# return not self.__eq__(other)
+#
+# def __hash__(self):
+# raise TypeError("Genome objects are unhashable")
+
+ def randomize(self):
+ """Set radian and offset to random values."""
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ constraints = cpool.get(self, DIRECTION_CONSTRAINT)
+ self.constraint = ka_extensionpoint.create(random.choice(constraints), self.path)
+ self.radian, self.offset = self.constraint.randomize()
+
+ def mutate(self):
+ """Make small random changes in radian and offset."""
+ if ka_random.is_mutating():
+ if ka_random.is_mutating():
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ constraints = cpool.get(self, DIRECTION_CONSTRAINT)
+ self.constraint = ka_extensionpoint.create(random.choice(constraints), self.path)
+ self.radian, self.offset = self.constraint.mutate(self.radian, self.offset)
+
+ def shuffle(self):
+ """Not implemented."""
+ pass
+
+ def crossingover(self, other):
+ """Returns either a copy of self or a copy of other.
+ pre: isinstance(other, Direction)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ randseq = ka_random.crossing_sequence(1)
+ return self.copy() if randseq[0] else other.copy()
+
+ def copy(self):
+ """A direction copy constructor
+ post: isinstance(__return__, Direction)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = Direction(self.get_trunk(), 0.0, 0.0)
+ new_one.radian, new_one.offset = self.radian, self.offset
+ new_one.constraint = self.constraint
+ return new_one
+
+ def explain(self):
+ return u'%4.3f, %4.3f' % (self.radian, self.offset)
+
+ @staticmethod
+ def make_icon(direction_list, width, height):
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ ctx = cairo.Context(surface)
+ ctx.scale(width, height)
+ # paint background
+ ctx.set_operator(cairo.OPERATOR_OVER)
+ ctx.set_source_rgb(1.0, 1.0, 1.0)
+ ctx.paint()
+ ctx.set_line_width(0.02)
+ for direction in direction_list:
+ # paint a arrow for each direction
+ dx = direction.offset * math.cos(direction.radian)
+ dy = direction.offset * math.sin(direction.radian)
+ ctx.set_source_rgb(0.0, 0.0, 0.0)
+ ctx.move_to(0.5, 0.5)
+ ctx.line_to(0.5+dx, 0.5+dy)
+ ctx.stroke()
+ return surface
diff --git a/exon_position.py b/exon_position.py
new file mode 100644
index 0000000..efe560a
--- /dev/null
+++ b/exon_position.py
@@ -0,0 +1,118 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import cairo
+import ka_extensionpoint
+import ka_random
+import model_constraintpool
+import model_allele
+
+EPSILON = 0.00001
+POSITION_CONSTRAINT = 'positionconstraint'
+
+class Position(model_allele.Allele):
+ """Position
+ """
+
+ cdef = [{'bind' : POSITION_CONSTRAINT,
+ 'name' : 'Position',
+ 'domain': model_constraintpool.STRING_1_OF_N,
+ 'enum' : ka_extensionpoint.list_extensions(POSITION_CONSTRAINT)
+ },
+ ]
+
+ def __init__(self, trunk, x_pos, y_pos):
+ """Position constructor
+ """
+ super(Position, self).__init__(trunk)
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ constraint_name = cpool.get(self, POSITION_CONSTRAINT)[0]
+ self.constraint = ka_extensionpoint.create(constraint_name, self.path)
+ self.x_pos, self.y_pos = self.constraint.filter(x_pos, y_pos)
+
+ def __eq__(self, other):
+ """Equality based on color components."""
+ return isinstance(other, Position) \
+ and abs(self.x_pos - other.x_pos) < EPSILON \
+ and abs(self.y_pos - other.y_pos) < EPSILON
+
+ def randomize(self):
+ """Set x_pos, y_pos to random values."""
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ constraints = cpool.get(self, POSITION_CONSTRAINT)
+ self.constraint = ka_extensionpoint.create(random.choice(constraints), self.path)
+ self.x_pos, self.y_pos = self.constraint.randomize()
+
+ def mutate(self):
+ """Make small random changes in position."""
+ if ka_random.is_mutating():
+ if ka_random.is_mutating():
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ constraints = cpool.get(self, POSITION_CONSTRAINT)
+ self.constraint = ka_extensionpoint.create(random.choice(constraints), self.path)
+ self.x_pos, self.y_pos = self.constraint.mutate(self.x_pos, self.y_pos)
+
+ def shuffle(self):
+ """Exchange x- and y-coordinate."""
+ temp0, temp1 = self.x_pos, self.y_pos
+ self.x_pos, self.y_pos = temp1, temp0
+
+ def crossingover(self, other):
+ """Returns either a copy of self or a copy of other.
+ pre: isinstance(other, Position)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ post: __return__ is not other
+ """
+ randseq = ka_random.crossing_sequence(1)
+ return self.copy() if randseq[0] else other.copy()
+
+ def copy(self):
+ """A position copy constructor
+ post: isinstance(__return__, Position)
+ # check for distinct references, needs to copy content, not references
+ post: __return__ is not self
+ """
+ new_one = Position(self.get_trunk(), 0.0, 0.0)
+ new_one.x_pos, new_one.y_pos = self.x_pos, self.y_pos
+ new_one.constraint = self.constraint
+ return new_one
+
+ def explain(self):
+ return u'%4.3f, %4.3f' % (self.x_pos, self.y_pos)
+
+ @staticmethod
+ def make_icon(position_list, width, height):
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ ctx = cairo.Context(surface)
+ ctx.scale(width, height)
+ # paint background
+ ctx.set_operator(cairo.OPERATOR_OVER)
+ ctx.set_source_rgb(1.0, 1.0, 1.0)
+ ctx.paint()
+ radius = 0.1
+ ctx.set_line_width(0.02)
+ for position in position_list:
+ # paint a cross for each position
+ ctx.set_source_rgb(0.0, 0.0, 0.0)
+ ctx.move_to(position.x_pos-radius, position.y_pos-radius)
+ ctx.line_to(position.x_pos+radius, position.y_pos+radius)
+ ctx.move_to(position.x_pos+radius, position.y_pos-radius)
+ ctx.line_to(position.x_pos-radius, position.y_pos+radius)
+ ctx.stroke()
+ return surface
diff --git a/ka_canvas.py b/ka_canvas.py
new file mode 100644
index 0000000..ab31380
--- /dev/null
+++ b/ka_canvas.py
@@ -0,0 +1,42 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import gtk
+import gtk.glade
+
+class KandidCanvas(object):
+ """
+ inv: self.widgetTree is not None
+ inv: self.widget is not None
+ """
+
+ def __init__(self):
+ # Load Glade XML
+ self.widgetTree = gtk.glade.XML("kandid.glade")
+
+ # Get Window
+ self._window = self.widgetTree.get_widget('window1')
+ # Get Windows child
+ self._window_child = self._window.get_child()
+
+ # Remove the widget's parent
+ if self._window_child.parent:
+ self._window_child.parent.remove(self._window_child)
+
+ # self.widget will be attached to the Activity
+ # This can be any GTK widget except a window
+ self.widget = self._window_child
diff --git a/ka_controller.py b/ka_controller.py
new file mode 100644
index 0000000..05ef161
--- /dev/null
+++ b/ka_controller.py
@@ -0,0 +1,357 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import hashlib
+import gtk
+import gtk.glade
+import cairo
+import ka_debug
+import model_population
+import ka_incoming
+import kandidtube
+import ka_random
+import ka_task
+
+class KandidController(object):
+ """
+ inv: self.model is not None
+ inv: self._canvas is not None
+ """
+
+ def __init__(self, init_canvas):
+ """
+ pre: init_canvas is not None
+ """
+ self._canvas = init_canvas
+ self._tube = None
+
+ # create to data model
+ self.model = model_population.KandidModel(12)
+ self.model.randomize()
+ self.incoming = ka_incoming.KandidIncoming(3)
+
+ self.surface_cache = {}
+
+ #Create a dictionary to connect events
+ events = {
+ 'on_breed_generation' : self.on_breed_generation,
+ 'on_random_generation' : self.on_random_generation,
+ 'on_flurry_value_changed' : self.on_flurry_value_changed,
+ 'on_decline_incoming' : self.on_decline_incoming,
+ 'on_accept_incoming' : self.on_accept_incoming,
+ 'delete_event' : gtk.main_quit }
+ for cell_index in range(self.model.size):
+ strix = str(cell_index)
+ key = 'on_drawingarea_#_expose'.replace('#', strix)
+ events[key] = self.on_drawingarea_expose
+ key = 'on_drawingarea_#_size_allocate'.replace('#', strix)
+ events[key] = self.on_drawingarea_size_allocate
+ key = 'on_fitness_#_value_changed'.replace('#', strix)
+ events[key] = self.on_fitness_value_changed
+ key = 'on_protozoon_popup_#'.replace('#', strix)
+ events[key] = self.on_protozoon_popup
+ key = 'on_publishprotozoon_activate_#'.replace('#', strix)
+ events[key] = self.on_publishprotozoon_activate
+ events['on_favorite_activate_' + strix] = self.on_favorite_activate
+ events['on_awfull_activate_' + strix] = self.on_awfull_activate
+ for cell_index in range(3):
+ strix = str(cell_index)
+ key = 'on_incomingarea_#_expose'.replace('#', strix)
+ events[key] = self.on_incomingarea_expose
+ key = 'on_incoming_#_popup'.replace('#', strix)
+ events[key] = self.on_incoming_popup
+ self._canvas.widgetTree.signal_autoconnect(events)
+ self._update_gui()
+
+ def _update_model(self, in_model):
+ if in_model:
+ self.model = in_model
+ self.model._state = model_population.STATE_EVOLVED
+ self._update_gui()
+
+ def _update_gui(self):
+ for cell_index in range(self.model.size):
+ strix = str(cell_index)
+ key = 'fitness_#'.replace('#', strix)
+ self._canvas.widgetTree.get_widget(key). \
+ set_value(self.model.fitness[cell_index])
+ self._canvas.widgetTree.get_widget('flurrySpinButton'). \
+ set_value(self.model.flurry_rate)
+
+ def _draw_from_cache(self, widget, cell_index):
+ if self.surface_cache.has_key(cell_index):
+ ka_debug.info('_draw_from_cache: ' + widget.name + ' ' + str(cell_index))
+ ctx = self._create_context(widget)
+ ctx.set_operator(cairo.OPERATOR_SOURCE)
+ ctx.set_source_surface(self.surface_cache[cell_index])
+ ctx.paint()
+
+ def on_drawingarea_expose(self, widget, event):
+ """ Repaint image of a single protozoon inside population area.
+ pre: widget is not None
+ """
+ # draw precalculated protozoon stored in the surface cache.
+ ka_debug.info('on_drawingarea_expose: ' + widget.name + ' '
+ + str(widget.allocation.width)
+ + 'x' + str(widget.allocation.height))
+ self._draw_from_cache(widget, _name_to_index(widget.name))
+
+ def on_drawingarea_size_allocate(self, widget, event):
+ """ New size for drawing area available.
+ pre: widget is not None
+ """
+ ka_debug.info('on_drawingarea_size_allocate: ' + widget.name + ' '
+ + str(widget.allocation.width)
+ + 'x' + str(widget.allocation.height))
+ self._start_calculation([_name_to_index(widget.name)])
+
+ def _create_context(self, widget):
+ """ Create cairo context.
+ pre: widget is not None
+ """
+ ctx = widget.window.cairo_create()
+ ctx.rectangle(0, 0, \
+ widget.allocation.width, widget.allocation.height)
+ ctx.clip()
+ return ctx
+
+ def on_fitness_value_changed(self, *args):
+ """
+ pre: len(args) >= 1
+ """
+ ka_debug.info('on_fitness_value_changed %f [%s]' %
+ (args[0].get_value(), args[0].get_name()))
+ self.model.fitness[_name_to_index(args[0].get_name())] \
+ = args[0].get_value()
+
+ def on_breed_generation(self, *args):
+ if ka_task.is_completed():
+ ka_debug.info('on_breed_generation entry')
+ ka_task.GeneratorTask(self.task_breed_generation,
+ self.on_model_completed).start()
+ ka_debug.info('on_breed_generation exit')
+ else:
+ ka_debug.info('on_breed_generation ignored')
+
+ def on_random_generation(self, *args):
+ if ka_task.is_completed():
+ ka_debug.info('on_random_generation entry')
+ ka_task.GeneratorTask(self.task_random_generation,
+ self.on_model_completed).start()
+ ka_debug.info('on_random_generation exit')
+ else:
+ ka_debug.info('on_random_generation ignored')
+
+ def on_model_completed(self, *args):
+ """
+ pre: len(args) == 1
+ """
+ ka_debug.info('on_model_completed entry')
+ for cell_index in args[0]:
+ self._canvas.widgetTree.get_widget('vbox_' + str(cell_index)). \
+ set_sensitive(False)
+ self._start_calculation(args[0])
+ ka_debug.info('on_model_completed exit')
+
+ def _start_calculation(self, concerned):
+ """
+ pre: len(concerned) > 0
+ pre: forall(concerned, lambda x: 0 <= x <= self.model.size)
+ """
+ for cell_index in concerned:
+ widget = self._canvas.widgetTree.get_widget('drawingarea_'
+ + str(cell_index))
+ task = ka_task.GeneratorTask(self.task_draw,
+ self.on_image_completed)
+ task.start(self.model.protozoans[cell_index], cell_index,
+ widget.allocation.width, widget.allocation.height)
+
+ def task_breed_generation(self, *args, **kwargs):
+ """
+ pre: len(args) == 0
+ """
+ ka_debug.info('task_breed_generation entry')
+ new_indices = self.model.breed()
+ ka_debug.info('task_breed_generation exit')
+ return new_indices
+
+ def task_random_generation(self, *args, **kwargs):
+ """
+ pre: len(args) == 0
+ """
+ ka_debug.info('task_random_generation entry')
+ new_indices = self.model.random()
+ ka_debug.info('task_random_generation exit')
+ return new_indices
+
+ def task_draw(self, *args, **kwargs):
+ """
+ pre: len(args) == 4
+ """
+ protozoon, cell_index, width, height = args[0], args[1], args[2], args[3]
+ ka_debug.info('task_draw entry: ' + str(cell_index))
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ ctx = cairo.Context(surface)
+ protozoon.draw(ctx, width, height)
+ self.surface_cache[cell_index] = surface
+ ka_debug.info('task_draw exit: ' + str(cell_index))
+ return cell_index
+
+ def on_image_completed(self, *args):
+ ka_debug.info('on_image_completed: ' + str(args[0]))
+ cell_index = args[0]
+ widget = self._canvas.widgetTree.get_widget('drawingarea_'
+ + str(cell_index))
+ self._draw_from_cache(widget, cell_index)
+ self._canvas.widgetTree.get_widget('vbox_' + str(cell_index)). \
+ set_sensitive(True)
+ self._canvas.widgetTree.get_widget('fitness_' + str(cell_index)). \
+ set_value(self.model.fitness[cell_index])
+
+ def on_flurry_value_changed(self, *args):
+ """
+ pre: len(args) >= 1
+ pre: 0 <= args[0].get_value() <= 9
+ """
+ ka_debug.info('on_flurry_value_changed [%s]' % args[0].get_value())
+ ka_random.set_flurry(args[0].get_value())
+
+ def on_protozoon_popup(self, widget, event):
+ ka_debug.info('on_protozoon_popup: ' + widget.name)
+ self._show_popup(widget, event, \
+ 'protozoon_menu_%u' % _name_to_index(widget.name))
+
+ def on_publishprotozoon_activate(self, *args):
+ """Publish single protozoon to all other buddies.
+ pre: len(args) >= 1
+ """
+ ka_debug.info('on_publishprotozoon_activate [%s]' % args[0].get_name())
+ if self._tube:
+ proto = self.model.protozoans[_name_to_index(args[0].get_name())]
+ self._tube.publish_protozoon(model_population.to_buffer(proto))
+
+ def on_favorite_activate(self, *args):
+ """Set best ranking for this protozoon.
+ pre: len(args) >= 1
+ """
+ ka_debug.info('on_favorite_activate [%s]' % args[0].get_name())
+ self.model.raise_fitness(_name_to_index(args[0].get_name()))
+ self._update_gui()
+
+ def on_awfull_activate(self, *args):
+ """Set last ranking for this protozoon.
+ pre: len(args) >= 1
+ """
+ ka_debug.info('on_awfull_activate [%s]' % args[0].get_name())
+ self.model.reduce_fitness(_name_to_index(args[0].get_name()))
+ self._update_gui()
+
+ def on_received(self, code_type, code_element, code_md5):
+ """Update population or protozoon preview when received from others."""
+ ka_debug.info('on_received: Received %u bytes, type: [%s] md5: [%s]' % \
+ (len(code_element), code_type, code_md5))
+ if hashlib.md5(code_element).hexdigest() == code_md5:
+ inbox_widget = self._canvas.widgetTree.get_widget('incomingBox')
+ if code_type == kandidtube.SEND_POPULATION:
+ if self.is_overwrite_allowed:
+ self._update_model(model_population.from_buffer(code_element))
+ self.model._state = model_population.STATE_EVOLVED
+ inbox_widget.queue_draw()
+ else:
+ ka_debug.info("I've already an evolved population, doing nothing")
+ elif code_type == kandidtube.SEND_PROTOZOON:
+ self.incoming.append_protozoon(model_population.from_buffer(code_element))
+ inbox_widget.queue_draw()
+ else:
+ ka_debug.info('Somebody called me using an illegal type [%s]' \
+ % code_type)
+ else:
+ ka_debug.info('Somebody called me with an corrupt data model')
+
+ def on_new_tube(self, tube, is_initiator, get_buddy):
+ """Creates communication object and sends population
+ pre: tube > 0
+ pre: get_buddy is not None
+ """
+ self._tube = kandidtube.KandidTube(self,
+ tube,
+ is_initiator,
+ get_buddy)
+
+# Event handling for incoming protozoans
+ def on_incoming_popup(self, widget, event):
+ ka_debug.info('on_incoming_popup: ' + widget.name)
+ index = _name_to_index(widget.name)
+ self._show_popup(widget, event, 'incoming_menu_'+ str(index))
+
+ def _show_popup(self, widget, event, menu):
+ ka_debug.info('%s [%s]' % (menu, widget.name))
+ popup_menu = self._canvas.widgetTree.get_widget(menu)
+ popup_menu.popup(None, None, None, event.button, event.time)
+
+ def on_accept_incoming(self, menu_item):
+ ka_debug.info('on_accept_incoming [%s]' % menu_item.parent.name)
+ new_at = self.incoming.accept_protozoon(self.model, \
+ _name_to_index(menu_item.parent.name))
+ self._start_calculation([new_at])
+# self._update_gui()
+ inbox_widget = self._canvas.widgetTree.get_widget('incomingBox')
+ inbox_widget.queue_draw()
+
+ def on_decline_incoming(self, menu_item):
+ ka_debug.info('on_decline_incoming [%s]' % menu_item.parent.name)
+ self.incoming.decline_protozoon(_name_to_index(menu_item.parent.name))
+ inbox_widget = self._canvas.widgetTree.get_widget('incomingBox')
+ inbox_widget.queue_draw()
+
+ def on_incomingarea_expose(self, widget, event):
+ """ Repaint image of a single protozoon inside incoming area.
+ pre: widget is not None
+ """
+ ka_debug.info('on_incomingarea_expose: ' + widget.name)
+ self.incoming.draw(_name_to_index(widget.name), \
+ self._create_context(widget), \
+ widget.allocation.width, widget.allocation.height)
+
+# data model and persistence
+ def is_overwrite_allowed(self):
+ """Preserve an already evolved population from over writing."""
+ return self.model.is_overwrite_allowed()
+
+ def serialize_model(self):
+ """Serialize population to a string buffer."""
+ return model_population.to_buffer(self.model)
+
+ def read_file(self, file_path):
+ """Delegate reading from journal to data model
+ pre: (file_path is not None) and (len(file_path) >= 1)
+ """
+ self._update_model(model_population.read_file(file_path))
+
+ def write_file(self, file_path):
+ """Delegate writing data model to journal
+ pre: (file_path is not None) and (len(file_path) >= 1)
+ """
+ model_population.write_file(file_path, self.model)
+
+def _name_to_index(name):
+ """Extract index from last part of a name.
+ Parts are separated by _.
+ pre: name[-1].isdigit()
+ post: __return__ >= 0
+ """
+ return int(name.rsplit('_')[-1])
diff --git a/ka_debug.py b/ka_debug.py
new file mode 100644
index 0000000..a29acb5
--- /dev/null
+++ b/ka_debug.py
@@ -0,0 +1,75 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import logging
+import os
+import time
+
+# default path for testing on local machine
+DEBUG_PATH = '/home/strom/minimal/activities/Kandid.activity'
+DBC_BLACK_LIST = ['activity', 'ka_debug', 'kandidtube', 'setup',
+ 'test_suite', 'test_enumerator']
+_logger = None
+_start_time = time.time()
+_last_clock = 0.0
+
+def _avtivate_logger():
+ global _logger
+ if not _logger:
+ _logger = logging.getLogger('Kandid')
+ _logger.setLevel(logging.DEBUG)
+
+def info(msg):
+ global _last_clock
+ clock_now = time.clock()
+ print 'debug', int((time.time()-_start_time)*1000), int((clock_now-_last_clock)*1000), ':', msg
+ _last_clock = clock_now
+ _avtivate_logger()
+ _logger.debug(msg)
+
+def err(msg):
+ global _last_clock
+ clock_now = time.clock()
+ print 'error', int((time.time()-_start_time)*1000), int((clock_now-_last_clock)*1000), ':', msg
+ _last_clock = clock_now
+ _avtivate_logger()
+ _logger.error(msg)
+
+# Add support for design by contract.
+if True:
+#if False:
+ try:
+ import contract, doctest
+ def enable_contact(module_name):
+ if module_name not in DBC_BLACK_LIST:
+ a_modul = __import__(module_name)
+ contract.checkmod(module_name)
+ doctest.testmod(a_modul)
+ # print 'enable_contact', a_modul
+
+ bundle_path = DEBUG_PATH
+ if 'SUGAR_BUNDLE_PATH' in os.environ:
+ from sugar.activity import activity
+ bundle_path = activity.get_bundle_path()
+ for element in os.listdir(bundle_path):
+ if element.endswith('.py') \
+ and os.path.isfile(os.path.join(bundle_path, element)):
+ name_parts = element.split('.')
+ if len(name_parts) == 2:
+ enable_contact(name_parts[0])
+ except ImportError:
+ print "unsupported design by contract"
diff --git a/ka_extensionpoint.py b/ka_extensionpoint.py
new file mode 100644
index 0000000..1f6067f
--- /dev/null
+++ b/ka_extensionpoint.py
@@ -0,0 +1,81 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import os
+#import types
+import ka_debug
+
+_PREFIX = 'ep_'
+_PYEXT = '.py'
+_SEP = '_'
+
+_extension_types = []
+_extensions = []
+
+def list_extensions(extension_type):
+ """
+ pre: extension_type in _extension_types
+ """
+ e_list = []
+ for extension_class in _extensions:
+ if extension_class.startswith(extension_type + _SEP):
+ e_list.append(extension_class)
+ e_list.sort()
+ return e_list
+
+def list_extension_types():
+ return _extension_types
+
+def create(extension_key, *params):
+ """
+ pre: extension_key in _extensions
+ post: __return__ is not None
+ """
+ a_module = __import__('ep_' + extension_key)
+ for key, value in a_module.__dict__.iteritems():
+# print '-- ', str(type(value)), key
+ if str(type(value)) == "<type 'type'>":
+ a_class = getattr(a_module, key)
+ return a_class(*params)
+# if len(params) == 0:
+# return a_class()
+# elif len(params) == 1:
+# return a_class(params[0])
+
+def _add(extension_type, extension_class):
+ if extension_type not in _extension_types:
+ _extension_types.append(extension_type)
+ e_key = extension_type + _SEP + extension_class
+ if e_key not in _extensions:
+ _extensions.append(e_key)
+
+def scann():
+ bundle_path = ka_debug.DEBUG_PATH
+ if 'SUGAR_BUNDLE_PATH' in os.environ:
+ from sugar.activity import activity
+ bundle_path = activity.get_bundle_path()
+ ka_debug.info('searching for extensions in ' + bundle_path)
+ for element in os.listdir(bundle_path):
+ if element.startswith(_PREFIX) and element.endswith(_PYEXT) \
+ and os.path.isfile(os.path.join(bundle_path, element)):
+ name_parts = element.split(_SEP)
+ if len(name_parts) == 3:
+ _add(name_parts[1], name_parts[2].replace(_PYEXT, ''))
+ _extension_types.sort()
+ _extensions.sort()
+
+scann() \ No newline at end of file
diff --git a/ka_factory.py b/ka_factory.py
new file mode 100644
index 0000000..21f50d4
--- /dev/null
+++ b/ka_factory.py
@@ -0,0 +1,73 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import ka_extensionpoint
+
+class LayerFactory(object):
+ """
+ inv: self.factory_keys is not None
+ """
+ def __init__(self, category):
+ """ LayerFactory is a singleton.
+ Please call ka_factory.get_factory(category) to retrieve the instance.
+ """
+ self.category = category
+ self.factory_keys = ka_extensionpoint.list_extensions(self.category)
+ self.factory_keys = map(lambda x: x.replace(self.category + '_', ''), \
+ self.factory_keys)
+
+ def count(self):
+ """
+ post: __return__ > 0
+ """
+ return len(self.factory_keys)
+
+ def keys(self):
+ """
+ """
+ return self.factory_keys
+
+ def create(self, factory_key, trunk):
+ """
+ pre: factory_key in self.factory_keys
+ post: __return__ is not None
+ """
+ return ka_extensionpoint.create(self.category + '_' + factory_key, trunk)
+
+ def create_random(self, key_filter, trunk):
+ """
+ pre: key_filter is not None
+ pre: len(key_filter) > 0
+ post: __return__ is not None
+ """
+ permitted = [x for x in self.factory_keys if x in key_filter]
+ return self.create(random.choice(permitted), trunk)
+
+_factory_index = {}
+
+def get_factory(category):
+ """
+ pre: category is not None
+ post: __return__ is not None
+ """
+# global _factory_index
+# if _factory_index is None:
+# _factory_index = {}
+ if not _factory_index.has_key(category):
+ _factory_index[category] = LayerFactory(category)
+ return _factory_index[category]
diff --git a/ka_importer.py b/ka_importer.py
new file mode 100644
index 0000000..ece269f
--- /dev/null
+++ b/ka_importer.py
@@ -0,0 +1,46 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import os
+
+_rgb_image_list = []
+_alpha_image_list = []
+
+def _populate_image_list():
+ #TODO use correct folder for user files
+ image_path = '/home/strom/.sugar/1/net.sourceforge.kandid/instance'
+ if 'SUGAR_BUNDLE_PATH' in os.environ:
+ from sugar.activity import activity
+ image_path = os.path.join(activity.get_activity_root(), 'instance')
+ for element in os.listdir(image_path):
+ if element.endswith('.png') or element.endswith('.PNG'):
+ abs_name = os.path.join(image_path, element)
+ if os.path.isfile(abs_name):
+ if element.find('.alpha.') == -1:
+ _rgb_image_list.append(abs_name)
+ else:
+ _alpha_image_list.append(abs_name)
+
+def get_rgb_image_list():
+ if len(_rgb_image_list) == 0:
+ _populate_image_list()
+ return _rgb_image_list
+
+def get_alpha_image_list():
+ if len(_alpha_image_list) == 0:
+ _populate_image_list()
+ return _alpha_image_list
diff --git a/ka_incoming.py b/ka_incoming.py
new file mode 100644
index 0000000..a97dbc4
--- /dev/null
+++ b/ka_incoming.py
@@ -0,0 +1,68 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import ka_debug
+
+class KandidIncoming(object):
+ """
+ inv: 0 <= len(self.incoming) <= self.capacity
+ inv: self.capacity > 0
+ """
+
+ def __init__(self, capacity):
+ self.capacity = capacity
+ self.incoming = []
+
+ def append_protozoon(self, incoming_protozoon):
+ """ Append protozoon from incoming list and manage capacity.
+ pre: incoming_protozoon is not None
+ """
+ ka_debug.info('Append protozoon')
+ while len(self.incoming) >= self.capacity:
+ self.incoming[0:1] = []
+ self.incoming.append(incoming_protozoon)
+
+ def accept_protozoon(self, model, index):
+ """ Move protozoon from incoming list to image population.
+ pre: 0 <= index < self.capacity
+ """
+ new_at = -1
+ if index < len(self.incoming):
+ ka_debug.info('accept incoming protozoon %u' % index)
+ new_at = model.replace(self.incoming[index])
+ self.incoming[index:1] = []
+ return new_at
+
+ def decline_protozoon(self, index):
+ """ Remove protozoon from incoming list
+ pre: 0 <= index < self.capacity
+ """
+ if index < len(self.incoming):
+ ka_debug.info('decline incoming protozoon %u' % index)
+ self.incoming[index:1] = []
+
+ def draw(self, index, ctx, width, height):
+ """ Repaint all protozoon images inside incoming area.
+ pre: 0 <= index < self.capacity
+ pre: ctx is not None
+ pre: width > 0
+ pre: height > 0
+ """
+ if index < len(self.incoming):
+ # draw protozoon
+ self.incoming[index].draw(ctx, width, height)
+
diff --git a/ka_random.py b/ka_random.py
new file mode 100644
index 0000000..fde952a
--- /dev/null
+++ b/ka_random.py
@@ -0,0 +1,163 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import math
+
+crossing_probability = 0.75
+shuffling_probability = 0.10
+mutating_probability = 0.25
+
+_flurry = 0.5
+
+def set_flurry(flurry_rate):
+ """
+ pre: 0 <= flurry_rate <= 9
+ post: 0.0 <= _flurry <= 1.0
+ """
+ global _flurry
+ _flurry = flurry_rate / 10.0
+
+def get_flurry():
+ """
+ post: 0 <= __return__ <= 9
+ """
+ return _flurry * 10.0
+
+def jitter(sigma):
+ """
+ """
+ return 0.03 * _flurry * random.gauss(0.0, sigma)
+
+def jitter_constrained(constraint):
+ """
+ pre: len(constraint) == 2
+ pre: constraint[0] <= constraint[1]
+ """
+# mean = 0.5 * (constraint[0] + constraint[1])
+ sigma = 0.03 * _flurry * (constraint[1] - constraint[0])
+ return random.gauss(0.0, sigma)
+
+def uniform_constrained(constraint):
+ """
+ pre: len(constraint) == 2
+ pre: constraint[0] <= constraint[1]
+ """
+ return random.uniform(constraint[0], constraint[1])
+
+def is_shuffling():
+ return random.random() < shuffling_probability * _flurry
+
+def is_mutating():
+ return random.random() < mutating_probability * _flurry
+
+def is_crossing():
+ return random.random() < crossing_probability * _flurry
+
+def crossing_sequence(size):
+ """Produces a sequence filled with True or False elements.
+ With a low crossing_probability there will be lesser change overs from
+ True series to False series.
+ pre: size >= 1
+ post: len(__return__) == size
+ post: forall(__return__, lambda x: x is True or x is False)
+ """
+ sample = [random.choice([False, True])]
+ for i in range(size-1):
+ if is_crossing():
+ sample.append(not sample[-1])
+ else:
+ sample.append(sample[-1])
+ return sample
+
+def crossingover(first, second):
+ """
+ pre: len(first) >= 1
+ pre: len(second) >= 1
+ post: min([len(first), len(second)]) <= len(__return__) <= max([len(first), len(second)])
+ post: forall(__return__, lambda x: x in first or x in second)
+ """
+ seq = []
+ len_first, len_second = len(first), len(second)
+ len_max = max([len_first, len_second])
+ randseq = crossing_sequence(len_max)
+ for index in range(len_max):
+ if randseq[index]:
+ if index < len_first:
+ seq.append(first[index])
+ else:
+ if index < len_second:
+ seq.append(second[index])
+ return seq
+
+def limitate(value):
+ """
+ post: 0.0 <= __return__ <= 1.0
+ """
+ if value < 0.0:
+ value = 0.0
+ elif value > 1.0:
+ value = 1.0
+ return value
+
+def cyclic_limitate(value):
+ """
+ post: 0.0 <= __return__ <= 1.0
+ """
+ return _cyclic_limitate(value, 0.0, 1.0)
+
+def radian_limitate(value):
+ """
+ post: (-1.0*math.pi) <= __return__ <= math.pi
+ """
+ return _cyclic_limitate(value, -1.0*math.pi, math.pi)
+
+def _cyclic_limitate(value, lower_bound, upper_bound):
+ """
+ post: lower_bound <= __return__ <= upper_bound
+ """
+ while value < lower_bound:
+ value += upper_bound-lower_bound
+ while value > upper_bound:
+ value -= upper_bound-lower_bound
+ return value
+
+def limitate_range(value, min_value, max_value):
+ """
+ pre: min_value <= max_value
+ post: min_value <= __return__ <= max_value
+ """
+ if value < min_value:
+ value = min_value
+ elif value > max_value:
+ value = max_value
+ return value
+
+def copy_list(other_list):
+ """Make a deep copy of a list and its elements.
+ pre: other_list is not None
+ post: __return__ is not other_list
+ post: len(__return__) == len(other_list)
+ post: forall([other_list[i] is not __return__[i] for i in range(len(other_list))])
+ post: forall([other_list[i] == __return__[i] for i in range(len(other_list))])
+ """
+ new_list = []
+ for element in other_list:
+ new_list.append(element.copy())
+# for i in range(len(other_list)):
+# print other_list[i] == new_list[i], other_list[i], new_list[i]
+ return new_list
diff --git a/ka_task.py b/ka_task.py
new file mode 100644
index 0000000..b18212b
--- /dev/null
+++ b/ka_task.py
@@ -0,0 +1,78 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import sys
+import threading
+import gobject
+import ka_debug
+
+#gtk.gdk.threads_init()
+
+_internal_lock = threading.Lock()
+_internal_task_count = 0
+
+def _enter():
+ """
+ pre: _internal_task_count >= 0
+ post: _internal_task_count >= 1
+ """
+ _internal_lock.acquire()
+ global _internal_task_count
+ _internal_task_count += 1
+ _internal_lock.release()
+
+def _leave():
+ """
+ pre: _internal_task_count >= 1
+ post: _internal_task_count >= 0
+ """
+ _internal_lock.acquire()
+ global _internal_task_count
+ _internal_task_count -= 1
+ _internal_lock.release()
+
+def is_completed():
+ """
+ pre: _internal_task_count >= 0
+ """
+ global _internal_task_count
+ return _internal_task_count < 1
+
+class GeneratorTask(object):
+
+ def __init__(self, task_function, on_task_completed):
+ """
+ pre: task_function is not None and callable(task_function)
+ pre: on_task_completed is not None and callable(on_task_completed)
+ """
+ self._on_task_completed = on_task_completed
+ self._task_function = task_function
+
+ def _start(self, *args, **kwargs):
+ try:
+ _enter()
+ result = self._task_function(*args)
+ print 'result', result
+ gobject.idle_add(self._on_task_completed, result)
+ except:
+ ka_debug.err('failed calculating [%s] [%s] [%s]' % \
+ (self._task_function, sys.exc_info()[0], sys.exc_info()[1]))
+ finally:
+ _leave()
+
+ def start(self, *args, **kwargs):
+ threading.Thread(target=self._start, args=args, kwargs=kwargs).start()
diff --git a/kandid.glade b/kandid.glade
new file mode 100644
index 0000000..127a0f5
--- /dev/null
+++ b/kandid.glade
@@ -0,0 +1,1531 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.5 on Sun Aug 16 13:53:21 2009 -->
+<glade-interface>
+ <widget class="GtkWindow" id="window1">
+ <child>
+ <widget class="GtkFrame" id="frame5">
+ <property name="visible">True</property>
+ <property name="border_width">8</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment17">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkNotebook" id="notebook1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <widget class="GtkHBox" id="populationBox">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkVBox" id="controlBox">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkVButtonBox" id="toolbarButtonbox">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="spacing">5</property>
+ <property name="layout_style">GTK_BUTTONBOX_START</property>
+ <child>
+ <widget class="GtkButton" id="breedGenerationButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Breed</property>
+ <property name="response_id">0</property>
+ <signal name="clicked" handler="on_breed_generation"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="randomGenerationButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Random</property>
+ <property name="response_id">0</property>
+ <signal name="clicked" handler="on_random_generation"/>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="flurryLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">1</property>
+ <property name="label" translatable="yes">Flurry rate:</property>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkSpinButton" id="flurrySpinButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">0 0 9 1 3 0</property>
+ <signal name="value_changed" handler="on_flurry_value_changed"/>
+ </widget>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">5</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="incomingBox">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox0">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="incomingbutton_0">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_incoming_0_popup"/>
+ <child>
+ <widget class="GtkArrow" id="arrow0">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label0">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe0">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="incomingarea_0">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_incomingarea_0_expose"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="incomingbutton_1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_incoming_1_popup"/>
+ <child>
+ <widget class="GtkArrow" id="arrow1">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="incomingarea_1">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_incomingarea_1_expose"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="incomingbutton_2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_incoming_2_popup"/>
+ <child>
+ <widget class="GtkArrow" id="arrow2">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe2">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="incomingarea_2">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_incomingarea_2_expose"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="pack_type">GTK_PACK_END</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">5</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTable" id="populationTable">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">4</property>
+ <property name="column_spacing">5</property>
+ <property name="row_spacing">5</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <widget class="GtkVBox" id="vbox_11">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox_11">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="open_popup_11">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_protozoon_popup_11"/>
+ <child>
+ <widget class="GtkArrow" id="arrow_11">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHScale" id="fitness_11">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="update_policy">GTK_UPDATE_DISCONTINUOUS</property>
+ <property name="adjustment">1 0 10 1 1 1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="on_fitness_11_value_changed"/>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe_11">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="drawingarea_11">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_drawingarea_11_expose"/>
+ <signal name="size_allocate" handler="on_drawingarea_11_size_allocate"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox_10">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox_10">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="open_popup_10">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_protozoon_popup_10"/>
+ <child>
+ <widget class="GtkArrow" id="arrow_10">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHScale" id="fitness_10">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="update_policy">GTK_UPDATE_DISCONTINUOUS</property>
+ <property name="adjustment">1 0 10 1 1 1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="on_fitness_10_value_changed"/>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe_10">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="drawingarea_10">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_drawingarea_10_expose"/>
+ <signal name="size_allocate" handler="on_drawingarea_10_size_allocate"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox_9">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox_9">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="open_popup_9">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_protozoon_popup_9"/>
+ <child>
+ <widget class="GtkArrow" id="arrow_9">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHScale" id="fitness_9">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="update_policy">GTK_UPDATE_DISCONTINUOUS</property>
+ <property name="adjustment">1 0 10 1 1 1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="on_fitness_9_value_changed"/>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe_9">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="drawingarea_9">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_drawingarea_9_expose"/>
+ <signal name="size_allocate" handler="on_drawingarea_9_size_allocate"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox_8">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox_8">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="open_popup_8">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_protozoon_popup_8"/>
+ <child>
+ <widget class="GtkArrow" id="arrow_8">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHScale" id="fitness_8">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="update_policy">GTK_UPDATE_DISCONTINUOUS</property>
+ <property name="adjustment">1 0 10 1 1 1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="on_fitness_8_value_changed"/>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe_8">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="drawingarea_8">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_drawingarea_8_expose"/>
+ <signal name="size_allocate" handler="on_drawingarea_8_size_allocate"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox_7">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox_7">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="open_popup_7">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_protozoon_popup_7"/>
+ <child>
+ <widget class="GtkArrow" id="arrow_7">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHScale" id="fitness_7">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="update_policy">GTK_UPDATE_DISCONTINUOUS</property>
+ <property name="adjustment">1 0 10 1 1 1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="on_fitness_7_value_changed"/>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe_7">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="drawingarea_7">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_drawingarea_7_expose"/>
+ <signal name="size_allocate" handler="on_drawingarea_7_size_allocate"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox_6">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox_6">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="open_popup_6">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_protozoon_popup_6"/>
+ <child>
+ <widget class="GtkArrow" id="arrow_6">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHScale" id="fitness_6">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="update_policy">GTK_UPDATE_DISCONTINUOUS</property>
+ <property name="adjustment">1 0 10 1 1 1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="on_fitness_6_value_changed"/>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe_6">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="drawingarea_6">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_drawingarea_6_expose"/>
+ <signal name="size_allocate" handler="on_drawingarea_6_size_allocate"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox_5">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox_5">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="open_popup_5">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_protozoon_popup_5"/>
+ <child>
+ <widget class="GtkArrow" id="arrow_5">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHScale" id="fitness_5">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="update_policy">GTK_UPDATE_DISCONTINUOUS</property>
+ <property name="adjustment">1 0 10 1 1 1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="on_fitness_5_value_changed"/>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe_5">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="drawingarea_5">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_drawingarea_5_expose"/>
+ <signal name="size_allocate" handler="on_drawingarea_5_size_allocate"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox_4">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox_4">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="open_popup_4">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_protozoon_popup_4"/>
+ <child>
+ <widget class="GtkArrow" id="arrow_4">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHScale" id="fitness_4">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="update_policy">GTK_UPDATE_DISCONTINUOUS</property>
+ <property name="adjustment">1 0 10 1 1 1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="on_fitness_4_value_changed"/>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe_4">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="drawingarea_4">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_drawingarea_4_expose"/>
+ <signal name="size_allocate" handler="on_drawingarea_4_size_allocate"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox_3">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox_3">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="open_popup_3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_protozoon_popup_3"/>
+ <child>
+ <widget class="GtkArrow" id="arrow_3">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHScale" id="fitness_3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="update_policy">GTK_UPDATE_DISCONTINUOUS</property>
+ <property name="adjustment">1 0 10 1 1 1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="on_fitness_3_value_changed"/>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe_3">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="drawingarea_3">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_drawingarea_3_expose"/>
+ <signal name="size_allocate" handler="on_drawingarea_3_size_allocate"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox_2">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox_2">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="open_popup_2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_protozoon_popup_2"/>
+ <child>
+ <widget class="GtkArrow" id="arrow_2">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHScale" id="fitness_2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="update_policy">GTK_UPDATE_DISCONTINUOUS</property>
+ <property name="adjustment">1 0 10 1 1 1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="on_fitness_2_value_changed"/>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe_2">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="drawingarea_2">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_drawingarea_2_expose"/>
+ <signal name="size_allocate" handler="on_drawingarea_2_size_allocate"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox_1">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox_1">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="open_popup_1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_protozoon_popup_1"/>
+ <child>
+ <widget class="GtkArrow" id="arrow_1">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHScale" id="fitness_1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="update_policy">GTK_UPDATE_DISCONTINUOUS</property>
+ <property name="adjustment">1 0 10 1 1 1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="on_fitness_1_value_changed"/>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe_1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="drawingarea_1">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_drawingarea_1_expose"/>
+ <signal name="size_allocate" handler="on_drawingarea_1_size_allocate"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox_0">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox_0">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkButton" id="open_popup_0">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <signal name="button_press_event" handler="on_protozoon_popup_0"/>
+ <child>
+ <widget class="GtkArrow" id="arrow_0">
+ <property name="visible">True</property>
+ <property name="arrow_type">GTK_ARROW_DOWN</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHScale" id="fitness_0">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="update_policy">GTK_UPDATE_DISCONTINUOUS</property>
+ <property name="adjustment">1 0 10 1 1 1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="on_fitness_0_value_changed"/>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="aspectframe_0">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkDrawingArea" id="drawingarea_0">
+ <property name="visible">True</property>
+ <signal name="expose_event" handler="on_drawingarea_0_expose"/>
+ <signal name="size_allocate" handler="on_drawingarea_0_size_allocate"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="populationLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Popolation</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEventBox" id="eventboxintro">
+ <property name="visible">True</property>
+ <property name="border_width">4</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <widget class="GtkTextView" id="textview_intro">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="cursor_visible">False</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="intro">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Introduction</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="protozoon_menu_0">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="favorite_menuitem_0">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">My favorite</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_favorite_activate_0"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="awfull_menuitem_0">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Awful bore</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_awfull_activate_0"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="publishprotozoon_menuitem_0">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Publish to a friend</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_publishprotozoon_activate_0"/>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="protozoon_menu_1">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="favorite_menuitem_1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">My favorite</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_favorite_activate_1"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="awfull_menuitem_1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Awful bore</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_awfull_activate_1"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="publishprotozoon_menuitem_1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Publish to a friend</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_publishprotozoon_activate_1"/>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="protozoon_menu_2">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="favorite_menuitem_2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">My favorite</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_favorite_activate_2"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="awfull_menuitem_2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Awful bore</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_awfull_activate_2"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="publishprotozoon_menuitem_2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Publish to a friend</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_publishprotozoon_activate_2"/>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="protozoon_menu_3">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="favorite_menuitem_3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">My favorite</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_favorite_activate_3"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="awfull_menuitem_3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Awful bore</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_awfull_activate_3"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="publishprotozoon_menuitem_3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Publish to a friend</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_publishprotozoon_activate_3"/>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="protozoon_menu_4">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="favorite_menuitem_4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">My favorite</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_favorite_activate_4"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="awfull_menuitem_4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Awful bore</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_awfull_activate_4"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="publishprotozoon_menuitem_4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Publish to a friend</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_publishprotozoon_activate_4"/>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="protozoon_menu_5">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="favorite_menuitem_5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">My favorite</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_favorite_activate_5"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="awfull_menuitem_5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Awful bore</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_awfull_activate_5"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="publishprotozoon_menuitem_5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Publish to a friend</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_publishprotozoon_activate_5"/>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="protozoon_menu_6">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="favorite_menuitem_6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">My favorite</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_favorite_activate_6"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="awfull_menuitem_6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Awful bore</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_awfull_activate_6"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="publishprotozoon_menuitem_6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Publish to a friend</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_publishprotozoon_activate_6"/>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="protozoon_menu_7">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="favorite_menuitem_7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">My favorite</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_favorite_activate_7"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="awfull_menuitem_7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Awful bore</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_awfull_activate_7"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="publishprotozoon_menuitem_7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Publish to a friend</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_publishprotozoon_activate_7"/>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="protozoon_menu_8">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="favorite_menuitem_8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">My favorite</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_favorite_activate_8"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="awfull_menuitem_8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Awful bore</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_awfull_activate_8"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="publishprotozoon_menuitem_8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Publish to a friend</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_publishprotozoon_activate_8"/>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="protozoon_menu_9">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="favorite_menuitem_9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">My favorite</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_favorite_activate_9"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="awfull_menuitem_9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Awful bore</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_awfull_activate_9"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="publishprotozoon_menuitem_9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Publish to a friend</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_publishprotozoon_activate_9"/>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="protozoon_menu_10">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="favorite_menuitem_10">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">My favorite</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_favorite_activate_10"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="awfull_menuitem_10">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Awful bore</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_awfull_activate_10"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="publishprotozoon_menuitem_10">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Publish to a friend</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_publishprotozoon_activate_10"/>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="protozoon_menu_11">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="favorite_menuitem_11">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">My favorite</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_favorite_activate_11"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="awfull_menuitem_11">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Awful bore</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_awfull_activate_11"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="publishprotozoon_menuitem_11">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Publish to a friend</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_publishprotozoon_activate_11"/>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="incoming_menu_0">
+ <property name="visible">True</property>
+ <property name="attach_widget">incomingbutton_0</property>
+ <child>
+ <widget class="GtkMenuItem" id="accept_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Accept protozoon</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_accept_incoming"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="decline_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Decline protozoon</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_decline_incoming"/>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="incoming_menu_1">
+ <property name="visible">True</property>
+ <property name="attach_widget">incomingbutton_1</property>
+ <child>
+ <widget class="GtkMenuItem" id="accept_menuitem1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Accept protozoon</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_accept_incoming"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="decline_menuitem1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Decline protozoon</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_decline_incoming"/>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="incoming_menu_2">
+ <property name="visible">True</property>
+ <property name="attach_widget">incomingbutton_2</property>
+ <child>
+ <widget class="GtkMenuItem" id="accept_menuitem2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Accept protozoon</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_accept_incoming"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="decline_menuitem2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Decline protozoon</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_decline_incoming"/>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
diff --git a/kandid.py b/kandid.py
new file mode 100644
index 0000000..46151bd
--- /dev/null
+++ b/kandid.py
@@ -0,0 +1,80 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+# This source code is based on the helloworld.py example from
+# PyGTK 2.0 Tutorial. see: http://www.pygtk.org/pygtk2tutorial/
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+gtk.gdk.threads_init()
+import ka_debug
+import ka_canvas
+import ka_controller
+
+TESTMODEL = '/home/strom/tmp/kmodel'
+
+class KandidApplication(object):
+
+ def delete_event(self, widget, event, data=None):
+ # If you return FALSE in the "delete_event" signal handler,
+ # GTK will emit the "destroy" signal. Returning TRUE means
+ # you don't want the window to be destroyed.
+ # This is useful for popping up 'are you sure you want to quit?'
+ # type dialogs.
+ ka_debug.info('Kandid test application: delete event occurred')
+ # Change FALSE to TRUE and the main window will not be destroyed
+ # with a "delete_event".
+ return False
+
+ def destroy(self, widget, data=None):
+ ka_debug.info('Kandid test application: destroy signal occurred')
+ self._controller.write_file(TESTMODEL)
+ gtk.main_quit()
+
+ def __init__(self):
+ # create a new window
+ self._window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ self._window.connect('destroy', self.destroy)
+
+ # Create GUI
+ self._canvas = ka_canvas.KandidCanvas()
+ self._window.add(self._canvas.widget)
+ # Create a controller to connect view and model
+ self._controller = ka_controller.KandidController(self._canvas)
+ self._controller.read_file(TESTMODEL)
+
+ # Display everything
+ self._canvas.widget.show()
+ self._window.set_default_size(1200, 800)
+ self._window.show()
+
+ def main(self):
+ """
+ All PyGTK applications must have a gtk.main(). Control ends here
+ and waits for an event to occur (like a key press or mouse event).
+ """
+ gtk.main()
+
+
+# If the program is run directly or passed as an argument to the python
+# interpreter then create a Kandid instance and show it
+if __name__ == "__main__":
+ ka_debug.info('starting Minimal Kandid')
+ application = KandidApplication()
+ application.main()
+
+
diff --git a/kandidtube.py b/kandidtube.py
new file mode 100644
index 0000000..67c0dba
--- /dev/null
+++ b/kandidtube.py
@@ -0,0 +1,132 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import hashlib
+import base64
+
+from dbus.service import method, signal
+from dbus.gobject_service import ExportedGObject
+import ka_debug
+
+SERVICE = 'net.sourceforge.kandid'
+IFACE = SERVICE
+PATH = '/net/sourceforge/kandid/minimal'
+
+SEND_POPULATION = 'P'
+SEND_PROTOZOON = 'I'
+
+class KandidTube(ExportedGObject):
+ """The bit that talks over the tubes"""
+
+ def __init__(self, controller, tube, is_initiator, get_buddy):
+ super(KandidTube, self).__init__(tube, PATH)
+ self._controller = controller
+ self._tube = tube
+ self._is_initiator = is_initiator
+ self._get_buddy = get_buddy # Converts handle to Buddy object
+ self._entered = False # Have we set up the tube?
+ self._tube.watch_participants(self.on_participant_change)
+
+ def on_participant_change(self, added, removed):
+ if added:
+ ka_debug.info('Tube: Added participants: %r' % added)
+ if removed:
+ ka_debug.info('Tube: Removed participants: %r' % removed)
+ for handle, bus_name in added:
+ buddy = self._get_buddy(handle)
+ if buddy is not None:
+ ka_debug.info('Tube: Handle %u (Buddy [%s]) was added' % \
+ (handle, buddy.props.nick))
+ for handle in removed:
+ buddy = self._get_buddy(handle)
+ if buddy is not None:
+ ka_debug.info('Buddy [%s] was removed' % buddy.props.nick)
+ if not self._entered:
+ if self._is_initiator:
+ ka_debug.info("I'm initiating the tube, will watch for hellos.")
+ self.add_hello_handler()
+ self.add_publish_handler()
+ else:
+ ka_debug.info('Hello, everyone! What did I miss?')
+ self.Hello()
+ self.add_publish_handler()
+ self._entered = True
+
+ def add_hello_handler(self):
+ ka_debug.info('I am initiating. Adding hello handler.')
+ self._tube.add_signal_receiver(self.on_hello, 'Hello',
+ IFACE, path=PATH, sender_keyword='sender')
+
+ def add_publish_handler(self):
+ ka_debug.info('I am entered. Adding input handler.')
+ self._tube.add_signal_receiver(self.on_publish_protozoon, 'PublishProtozoon',
+ IFACE, path=PATH, sender_keyword='sender')
+
+ @signal(dbus_interface=IFACE, signature='')
+ def Hello(self):
+ """Say Hello to whoever else is in the tube."""
+ ka_debug.info('I said Hello.')
+
+ def on_hello(self, sender=None):
+ """Somebody helloed me. Send them my population."""
+ if sender == self._tube.get_unique_name():
+ # sender is my bus name, so ignore my own signal
+ return
+ ka_debug.info('on_hello: Newcomer [%s] has joined. World them.' % sender)
+ self.send_population(self._controller.serialize_model(), sender)
+
+ def publish_protozoon(self, code_element):
+ ka_debug.info('publish_protozoon: %s %s' % (type(code_element), code_element))
+ code_element_base64 = base64.b64encode(code_element)
+ code_md5 = hashlib.md5(code_element).hexdigest()
+ ka_debug.info('publish_protozoon: Sent %u bytes, type: [%s] md5: [%s]' % \
+ (len(code_element), SEND_PROTOZOON, code_md5))
+ self.PublishProtozoon(SEND_PROTOZOON, code_element_base64, code_md5)
+
+ @signal(dbus_interface=IFACE, signature='sss')
+ def PublishProtozoon(self, code_type, code_element, code_md5):
+ """Send protozoon."""
+ ka_debug.info('PublishProtozoon: I published %u bytes, type: [%s] md5: [%s]' \
+ % (len(code_element), code_type, code_md5))
+
+ def on_publish_protozoon(self, code_type, code_element, code_md5, sender=None):
+ """Somebody published. Process received parameters."""
+ if sender == self._tube.get_unique_name():
+ # sender is my bus name, so ignore my own signal
+ return
+ ka_debug.info('on_publish: I got %u bytes, type: [%s] md5: [%s]' \
+ % (len(code_element), code_type, code_md5))
+ dec = base64.b64decode(code_element)
+ self._controller.on_received(code_type, dec, code_md5)
+
+ def send_population(self, code_element, sender=None):
+ code_element_base64 = base64.b64encode(code_element)
+ code_md5 = hashlib.md5(code_element).hexdigest()
+ ka_debug.info('send_population: Sent %u bytes, type: [%s] md5: [%s]' % \
+ (len(code_element), SEND_POPULATION, code_md5))
+ self._tube.get_object(sender, PATH).SendPopulation(SEND_POPULATION,
+ code_element_base64,
+ code_md5)
+
+ #@signal(dbus_interface=IFACE, signature='sss')
+ @method(dbus_interface=IFACE, in_signature='sss', out_signature='')
+ def SendPopulation(self, code_type, code_element, code_md5):
+ """Send to all participants."""
+ ka_debug.info('SendPopulation: Sent %u bytes, type: [%s] md5: [%s]' \
+ % (len(code_element), code_type, code_md5))
+ dec = base64.b64decode(code_element)
+ self._controller.on_received(code_type, dec, code_md5)
diff --git a/locale/en/LC_MESSAGES/net.sourceforge.kandid.mo b/locale/en/LC_MESSAGES/net.sourceforge.kandid.mo
new file mode 100644
index 0000000..5bfb918
--- /dev/null
+++ b/locale/en/LC_MESSAGES/net.sourceforge.kandid.mo
Binary files differ
diff --git a/locale/en/activity.linfo b/locale/en/activity.linfo
new file mode 100644
index 0000000..4b628b7
--- /dev/null
+++ b/locale/en/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Kandid
diff --git a/model_allele.py b/model_allele.py
new file mode 100644
index 0000000..ffe3cb2
--- /dev/null
+++ b/model_allele.py
@@ -0,0 +1,32 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import model_locus
+
+class Allele(model_locus.Locus):
+
+ def __init__(self, root):
+ super(Allele, self).__init__(root)
+
+ def __eq__(self, other):
+ raise TypeError("Use only __eq__ from derived subclasses.")
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ raise TypeError("Genome objects are unhashable")
diff --git a/model_constraintpool.py b/model_constraintpool.py
new file mode 100644
index 0000000..12eca65
--- /dev/null
+++ b/model_constraintpool.py
@@ -0,0 +1,143 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import model_population
+
+#_CONSTRAINT = 'constraint'
+
+INT_1_OF_N = 11
+INT_M_OF_N = 12
+INT_RANGE = 13
+FLOAT_RANGE = 22
+STRING_1_OF_N = 31
+STRING_M_OF_N = 32
+
+class ConstraintPool(object):
+ """ConstraintPool is a singleton.
+ Use ConstraintPool.get_pool() to get an instance.
+ """
+
+ _constraintpool = None
+ _known_keys = None
+
+ def __init__(self):
+ self._overwrite = {}
+ self._my_defaults()
+
+ @staticmethod
+ def get_pool():
+ if ConstraintPool._constraintpool == None:
+ ConstraintPool._constraintpool = ConstraintPool()
+ return ConstraintPool._constraintpool
+
+ def _generate_key(self, class_key, attribute_key):
+ """
+ pre: class_key is not None
+ pre: attribute_key is not None
+ """
+ return class_key + '/' + attribute_key
+
+ def get(self, caller, attribute_key):
+ """
+ #pre: 'cdef' in dir(caller) or 'base_cdef' in dir(caller)
+ pre: True if 'cdef' not in dir(caller) else caller.cdef is not None and len(caller.cdef) > 0
+ pre: True if 'base_cdef' not in dir(caller) else caller.base_cdef is not None and len(caller.base_cdef) > 0
+ pre: caller.path is not None
+ pre: attribute_key is not None
+ post: __return__ is not None
+ """
+ primary_key = self._generate_key(caller.path, attribute_key)
+ result = None
+ if self._overwrite.has_key(primary_key):
+ result = self._overwrite[primary_key]
+# print '>> over', primary_key, result
+ else:
+ if 'cdef' in dir(caller):
+ result = self._search_constraint(caller.cdef, attribute_key)
+ if result is None and 'base_cdef' in dir(caller):
+ result = self._search_constraint(caller.base_cdef, attribute_key)
+# print '>> cdef', primary_key, result
+ if result is not None:
+ self._add_key(primary_key)
+ return result
+
+ def _search_constraint(self, caller, attribute_key):
+ result = None
+ for definition in caller:
+ if definition['bind'] == attribute_key:
+ if definition['domain'] == INT_1_OF_N:
+ result = [x[1] for x in definition['enum']]
+ if definition['domain'] == INT_M_OF_N:
+ result = [x[1] for x in definition['enum']]
+ if definition['domain'] == INT_RANGE:
+ result = (definition['min'], definition['max'])
+ if definition['domain'] == FLOAT_RANGE:
+ result = (definition['min'], definition['max'])
+ if definition['domain'] == STRING_1_OF_N:
+ result = definition['enum']
+ if definition['domain'] == STRING_M_OF_N:
+ result = definition['enum']
+ return result
+
+ def is_overwritten(self, caller, attribute_key):
+ return False
+
+ def set(self, class_key, attribute_key, constraint):
+ """
+ pre: class_key is not None
+ pre: class_key.startswith('/')
+ pre: attribute_key is not None
+ pre: constraint is not None
+ """
+ primary_key = self._generate_key(class_key, attribute_key)
+ self._overwrite[primary_key] = constraint
+
+ def clear_all(self):
+ """
+ """
+ self._overwrite = {}
+
+ def listknown_keys(self):
+ """
+ """
+ return [] if ConstraintPool._known_keys is None \
+ else ConstraintPool._known_keys
+
+ def _add_key(self, key):
+ """
+ """
+ file_path = '/dev/shm/minmal_known_keys.mkey'
+ if ConstraintPool._known_keys is None:
+ ConstraintPool._known_keys = model_population.read_file(file_path)
+ if ConstraintPool._known_keys is None:
+ ConstraintPool._known_keys = []
+ if key not in ConstraintPool._known_keys:
+ ConstraintPool._known_keys.append(key)
+ model_population.write_file(file_path, ConstraintPool._known_keys)
+
+ def _my_defaults(self):
+ self.set('/Protozoon', 'layertypeconstraint', ['image', 'letterpress', 'markovchain', ])
+ self.set('/Protozoon', 'mergertypeconstraint', ['layermask', ])
+ #TODO read from persistence, provide an constraint editor
+# self.set('/Protozoon', 'layertypeconstraint', ['referencepattern', 'image', 'letterpress', 'markovchain', ])
+# self.set('/Protozoon', 'layertypeconstraint', ['referencepattern'])
+# self.set('/Protozoon', 'layertypeconstraint', ['image', ])
+
+# self.set('/Protozoon', 'mergertypeconstraint', ['flip', ])
+# self.set('/Protozoon', 'mergertypeconstraint', ['flip', 'rectangulartile', 'mask', 'straight', 'border', ])
+# self.set('/Protozoon', 'mergertypeconstraint', ['alphaimage', ])
+ pass
diff --git a/model_layer.py b/model_layer.py
new file mode 100644
index 0000000..5d2eb25
--- /dev/null
+++ b/model_layer.py
@@ -0,0 +1,167 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import time
+import random
+import cairo
+import ka_random
+import model_allele
+import model_constraintpool
+
+# Cairo's compositing operators
+# OPERATOR_ADD = 12
+# OPERATOR_ATOP = 5
+# OPERATOR_CLEAR = 0
+# OPERATOR_DEST = 6
+# OPERATOR_DEST_ATOP = 10
+# OPERATOR_DEST_IN = 8
+# OPERATOR_DEST_OUT = 9
+# OPERATOR_DEST_OVER = 7
+# OPERATOR_IN = 3
+# OPERATOR_OUT = 4
+# OPERATOR_OVER = 2
+# OPERATOR_SATURATE = 13
+# OPERATOR_SOURCE = 1
+# OPERATOR_XOR = 11
+#OPERATORS = [operator for operator in range(cairo.OPERATOR_SATURATE+1)]
+#
+
+OPERATOR_CONSTRAINT = 'operatorconstraint'
+
+class Layer(model_allele.Allele):
+ """Layer
+ inv: cairo.OPERATOR_CLEAR <= self.operator <= cairo.OPERATOR_SATURATE
+ """
+
+ base_cdef = [{'bind' : OPERATOR_CONSTRAINT,
+ 'name' : 'Permitted operators for combining two layers',
+ 'domain': model_constraintpool.INT_M_OF_N,
+ 'enum' : [('add', cairo.OPERATOR_ADD),
+ ('atop', cairo.OPERATOR_ATOP),
+# ('clear', cairo.OPERATOR_CLEAR),
+# ('destination', cairo.OPERATOR_DEST),
+ ('destination atop', cairo.OPERATOR_DEST_ATOP),
+ ('destination in', cairo.OPERATOR_DEST_IN),
+ ('destination out', cairo.OPERATOR_DEST_OUT),
+# ('destination over', cairo.OPERATOR_DEST_OVER),
+# ('in', cairo.OPERATOR_IN),
+ ('out', cairo.OPERATOR_OUT),
+ ('over', cairo.OPERATOR_OVER),
+# ('saturate', cairo.OPERATOR_SATURATE),
+ ('source', cairo.OPERATOR_SOURCE),
+ ('xor', cairo.OPERATOR_XOR),],},
+ ]
+ OPERATORS = [o[1] for o in base_cdef[0]['enum']]
+# OPERATOR_NAMES = [o[0] for o in base_cdef[0]['enum']]
+ OPERATOR_NAMES = {
+ cairo.OPERATOR_ADD: 'ADD',
+ cairo.OPERATOR_ATOP: 'ATOP',
+ cairo.OPERATOR_CLEAR: 'CLEAR',
+ cairo.OPERATOR_DEST: 'DEST',
+ cairo.OPERATOR_DEST_ATOP: 'DEST_ATOP',
+ cairo.OPERATOR_DEST_IN: 'DEST_IN',
+ cairo.OPERATOR_DEST_OUT: 'DEST_OUT',
+ cairo.OPERATOR_DEST_OVER: 'DEST_OVER',
+ cairo.OPERATOR_IN: 'IN',
+ cairo.OPERATOR_OUT: 'OUT',
+ cairo.OPERATOR_OVER: 'OVER',
+ cairo.OPERATOR_SATURATE: 'SATURATE',
+ cairo.OPERATOR_SOURCE: 'SOURCE',
+ cairo.OPERATOR_XOR: 'XOR',
+ }
+
+ def __init__(self, trunk):
+ """Layer constructor"""
+ super(Layer, self).__init__(trunk)
+ self.seed = 1512
+ self.operator = cairo.OPERATOR_OVER
+
+ def __eq__(self, other):
+ """Equality based on the cells color components."""
+ equal = isinstance(other, Layer) \
+ and self.seed == other.seed \
+ and self.operator == other.operator
+ return equal
+
+ def randomize(self):
+ """Randomize the layers components."""
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ self.seed = random.randint(1, 65535)
+ operator_constraint = cpool.get(self, OPERATOR_CONSTRAINT)
+ self.operator = random.choice(operator_constraint)
+
+ def mutate(self):
+ """Make small random changes to the layers components."""
+ if ka_random.is_mutating():
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ operator_constraint = cpool.get(self, OPERATOR_CONSTRAINT)
+ self.operator = random.choice(operator_constraint)
+
+ def shuffle(self):
+ """Shuffle similar componets.
+ For example exchange foreground and background color."""
+
+ def crossingover(self, other):
+ """
+ """
+ raise TypeError("Layer is an abstract class. Call crossingover() on sub classes only.")
+
+ def crossingover_base(self, new_one, other, cross_lenght):
+ """
+ pre: isinstance(new_one, Layer)
+ pre: isinstance(other, Layer)
+ pre: cross_lenght >= 0
+ """
+ cross_sequence = ka_random.crossing_sequence(cross_lenght + 2)
+ if cross_sequence[cross_lenght]:
+ new_one.seed = self.seed
+ else:
+ new_one.seed = other.seed
+ if cross_sequence[cross_lenght+1]:
+ new_one.operator = self.operator
+ else:
+ new_one.operator = other.operator
+ return cross_sequence
+
+ def draw(self, ctx, width, height):
+ raise TypeError("Layer is an abstract class. Call draw() on sub classes only.")
+
+ def begin_draw(self, ctx, width, height):
+ """
+ pre: ctx is not None
+ pre: width > 0
+ pre: height > 0
+ pre: width == height
+ """
+ time.sleep(0.001) # Python can not set thread priorities.
+ # Workaround: regularly released the global inter-
+ # preter lock GIL to give the GUI Thread a chance.
+ ctx.set_operator(self.operator)
+
+ def explain(self, formater):
+ formater.text_item('Compositing drawing operator: ' \
+ + Layer.OPERATOR_NAMES[self.operator])
+
+ def copy(self):
+ raise TypeError("Layer is an abstract class. Call copy() on sub classes only.")
+
+ def copy_base(self, new_one):
+ """The layers copy constructor
+ pre: isinstance(new_one, Layer)
+ """
+ new_one.seed = self.seed
+ new_one.operator = self.operator
diff --git a/model_locus.py b/model_locus.py
new file mode 100644
index 0000000..7db01dd
--- /dev/null
+++ b/model_locus.py
@@ -0,0 +1,69 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+
+#@staticmethod
+def _fix_pattern():
+ p = '_'
+ for i in range(6):
+ # 12345678901234567890123456
+ p += random.choice('abcdefghijklmnopqrstuvwxyz')
+ p += '_'
+ return p
+
+class Locus(object):
+
+# @staticmethod
+# def _fix_pattern():
+# p = ''
+# p += random.choice('abcd')
+# return p
+#
+ _modulo_count = 0
+ _pattern = _fix_pattern()
+
+ def __init__(self, trunk):
+ """
+ pre: isinstance(trunk, str)
+ pre: not (trunk == '')
+ """
+ Locus._modulo_count = Locus._modulo_count + 1 \
+ if Locus._modulo_count < 999 else 0
+ self._unique_id = Locus._pattern + str(Locus._modulo_count)
+ if trunk == '/':
+ self.path = '/' + self.__class__.__name__
+ else:
+ self.path = trunk + '/' + self.__class__.__name__
+# print '>>', self.path, self._unique_id
+
+ def get_trunk(self):
+ parts = self.path.split('/')
+ if len(parts) > 2:
+ trunk = ''
+ for part in parts[1:-1]:
+ if len(part) > 0:
+ trunk += '/' + part
+ return trunk
+ else:
+ return '/'
+
+ def get_unique_id(self):
+ """ unique identifikation for this protozoons copy constructor.
+ """
+ return self._unique_id
+
diff --git a/model_population.py b/model_population.py
new file mode 100644
index 0000000..93ae43e
--- /dev/null
+++ b/model_population.py
@@ -0,0 +1,243 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import pickle
+import sys
+import os.path
+import random
+import ka_debug
+import ka_random
+import model_protozoon
+
+STATE_INIT = 'I'
+STATE_RANDOMIZED = 'R'
+STATE_EVOLVED = 'E'
+
+class KandidModel(object):
+ """
+ inv: self.size >= 2
+ inv: 1 <= self.fade_away <= self.size
+ #inv: 0.0 <= self._flurry_rate <= 9.0
+ inv: len(self.fitness) == self.size
+ inv: forall(self.fitness, lambda f: 0.0 <= f <= 9.0)
+ inv: len(self.protozoans) == self.size
+ inv: forall(self.protozoans, lambda p: p is not None)
+ # all protozoans must be distinct objects, maybe with equal content
+ inv: all_uniqe_reference(self.protozoans)
+ """
+
+ def __init__(self, init_size):
+ self._state = STATE_INIT
+ self.size = init_size
+ self.fade_away = init_size / 2
+ #self._flurry_rate = 5.0
+ self.protozoans = [model_protozoon.Protozoon() for i in range(self.size)]
+ self.fitness = [4.0 for i in range(self.size)]
+ ka_debug.info('initializing model with population size %u' % init_size)
+
+ def get_flurry_rate(self):
+ return ka_random.get_flurry()
+# return self._flurry_rate
+
+ def set_flurry_rate(self, value):
+ """
+ pre: 0 <= value <= 9
+ """
+ ka_random.set_flurry(value)
+# self._flurry_rate = value
+
+ def is_overwrite_allowed(self):
+ """Preserve an already evolved population from over writing."""
+ return not self._state == STATE_EVOLVED
+
+ def classify(self):
+ """
+ # __return__[0] -> good
+ # __return__[1] -> moderate
+ # __return__[2] -> poor
+ post: len(__return__[0])+len(__return__[1])+len(__return__[2]) == self.size
+ post: len(__return__[0]) >= 1
+ post: len(__return__[2]) >= 1
+
+ # mutual exclusive
+ post: forall(__return__[0], lambda x: not contains_reference(x, __return__[1]))
+ post: forall(__return__[0], lambda x: not contains_reference(x, __return__[2]))
+ post: forall(__return__[2], lambda x: not contains_reference(x, __return__[0]))
+ post: forall(__return__[2], lambda x: not contains_reference(x, __return__[1]))
+ """
+ good, moderate, poor = [], [], []
+ sorted_fitness = sorted(self.fitness)
+ poor_level = sorted_fitness[self.fade_away-1]
+ good_level = sorted_fitness[-1]
+ for protoz, fit in zip(self.protozoans, self.fitness):
+ if fit >= good_level and len(good) < 1:
+ good.append(protoz)
+ elif fit <= poor_level:
+ if len(poor) >= self.fade_away:
+ index = random.randint(0, len(poor)-1)
+ moderate.append(poor[index])
+ del poor[index]
+ poor.append(protoz)
+ else:
+ moderate.append(protoz)
+ return good, moderate, poor
+
+ def reduce_fitness(self, index):
+ """
+ pre: 0 <= index < len(self.fitness)
+ post: self.fitness.count(0.0) == 1
+ """
+ for raise_at, fit in enumerate(self.fitness):
+ if fit < 1.0:
+ self.fitness[raise_at] = 1.0
+ self.fitness[index] = 0.0
+
+ def raise_fitness(self, index):
+ """
+ pre: 0 <= index < len(self.fitness)
+ post: self.fitness.count(9.0) == 1
+ """
+ for lower_at, fit in enumerate(self.fitness):
+ if 5.0 < fit:
+ self.fitness[lower_at] = round(self.fitness[lower_at] - 1.0)
+ self.fitness[index] = 9.0
+
+ def randomize(self):
+ self._state = STATE_RANDOMIZED
+ for protoz in self.protozoans:
+ protoz.randomize()
+
+ def random(self):
+ """
+ post: len(__return__) > 0
+ post: forall(__return__, lambda x: 0 <= x < self.size)
+ """
+ new_indices = []
+ self._state = STATE_EVOLVED
+ good, moderate, poor = self.classify()
+ for new_at, protoz in enumerate(self.protozoans):
+ if protoz in poor:
+ self.protozoans[new_at].randomize()
+ self.fitness[new_at] = 4.0
+ new_indices.append(new_at)
+ print 'new_indices', new_indices
+ return new_indices
+
+ def breed(self):
+ """
+ post: len(__return__) > 0
+ post: forall(__return__, lambda x: 0 <= x < self.size)
+ """
+ new_indices = []
+ self._state = STATE_EVOLVED
+ good, moderate, poor = self.classify()
+ index = 0
+ for new_at, protoz in enumerate(self.protozoans):
+ if protoz in poor:
+ new_one = good[0].crossingover(moderate[index % len(moderate)])
+ new_one.shuffle()
+ new_one.mutate()
+ self.protozoans[new_at] = new_one
+ self.fitness[new_at] = 4.0
+ new_indices.append(new_at)
+ index += 1
+ print 'new_indices', new_indices
+ return new_indices
+
+ def replace(self, new_one):
+ """Replace protozoon with lowest fitness.
+ pre: isinstance(new_one, model_protozoon.Protozoon)
+ """
+ poor_level = 999999.9
+ for protoz, fit in zip(self.protozoans, self.fitness):
+ if fit < poor_level:
+ poor_level = fit
+ poor = protoz
+ for new_at, protoz in enumerate(self.protozoans):
+ if protoz is poor:
+ self.protozoans[new_at] = new_one
+ self.fitness[new_at] = 5.0
+ return new_at
+ return -1
+
+ flurry_rate = property(get_flurry_rate, set_flurry_rate)
+
+
+def from_buffer(str_buffer):
+ ka_debug.info('read from_buffer')
+ obj = None
+ try:
+ obj = pickle.loads(str_buffer)
+ except:
+ ka_debug.err('failed reading buffer [%s] [%s]' % \
+ (sys.exc_info()[0], sys.exc_info()[1]))
+ ka_debug.info('[%s]' % str_buffer)
+ return obj
+
+def to_buffer(obj):
+ ka_debug.info('write %s to_buffer' % type(obj))
+ try:
+ return pickle.dumps(obj)
+ except:
+ ka_debug.err('failed writing buffer [%s] [%s]' % \
+ (sys.exc_info()[0], sys.exc_info()[1]))
+
+def read_file(file_path):
+ model = None
+ if os.path.isfile(file_path):
+ in_file = None
+ try:
+ in_file = open(file_path, 'r')
+ ka_debug.info('in_file [%s]' % in_file.name)
+ model = pickle.load(in_file)
+ except:
+ ka_debug.err('failed reading [%s] [%s] [%s]' % \
+ (in_file.name, sys.exc_info()[0], sys.exc_info()[1]))
+ finally:
+ if in_file:
+ in_file.close()
+ return model
+
+def write_file(file_path, model):
+ out_file = None
+ try:
+ out_file = open(file_path, 'w')
+ ka_debug.info('write out_file [%s]' % out_file.name)
+ pickle.dump(model, out_file)
+ except:
+ ka_debug.err('failed writing [%s] [%s] [%s]' % \
+ (out_file.name, sys.exc_info()[0], sys.exc_info()[1]))
+ finally:
+ if out_file:
+ out_file.close()
+
+def all_uniqe_reference(sequ):
+ # Brute force is all that's left.
+ unique = []
+ for elem in sequ:
+ if contains_reference(elem, unique):
+ return False
+ else:
+ unique.append(elem)
+ return len(unique) == len(sequ)
+
+def contains_reference(find_elem, sequ):
+ for elem in sequ:
+ if id(find_elem) == id(elem):
+ return True
+ return False
+
diff --git a/model_protozoon.py b/model_protozoon.py
new file mode 100644
index 0000000..2e389f0
--- /dev/null
+++ b/model_protozoon.py
@@ -0,0 +1,315 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import cairo
+import ka_factory
+import ka_random
+import model_constraintpool
+import exon_color
+import model_allele
+
+TRUNK = '/'
+
+LAYERTYPE_CONSTRAINT = 'layertypeconstraint'
+NUMBER_OF_LAYERS_CONSTRAINT = 'layernumberofconstraint'
+MERGER_CONSTRAINT = 'mergertypeconstraint'
+
+class Protozoon(model_allele.Allele):
+ """
+ inv: self.layers is not None
+ inv: self.mergers is not None
+ inv: self.background is not None
+ inv: isinstance(self.background, exon_color.Color)
+ """
+
+ cdef = [{'bind' : LAYERTYPE_CONSTRAINT,
+ 'name' : 'Permitted layer types',
+ 'domain': model_constraintpool.STRING_M_OF_N,
+ 'enum' : ka_factory.get_factory('layer').keys()
+ },
+ {'bind' : NUMBER_OF_LAYERS_CONSTRAINT,
+ 'name' : 'Number of layers',
+ 'domain': model_constraintpool.INT_RANGE,
+ 'min' : 2, 'max': 5,
+ },
+ {'bind' : MERGER_CONSTRAINT,
+ 'name' : 'Permitted merging strategies for layers',
+ 'domain': model_constraintpool.STRING_M_OF_N,
+ 'enum' : ka_factory.get_factory('merger').keys()
+ },
+ ]
+
+ def __init__(self):
+ super(Protozoon, self).__init__(TRUNK)
+ self.layers = []
+ self.mergers = []
+ self.background = exon_color.Color(self.path, 0, 0, 0, 1)
+
+ def __eq__(self, other):
+ """Equality based on layers quantity and content."""
+ equal = isinstance(other, Protozoon) \
+ and len(self.layers) == len(other.layers) \
+ and len(self.mergers) == len(other.mergers) \
+ and self.background == other.background
+ if equal:
+ for index in range(len(self.layers)):
+ equal = equal and self.layers[index] == other.layers[index]
+ if equal:
+ for index in range(len(self.mergers)):
+ equal = equal and self.mergers[index] == other.mergers[index]
+ return equal
+
+ def randomize(self):
+ """Randomize the protozoons components.
+ post: len(self.layers) >= 1
+ post: len(self.mergers) >= 1
+ """
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+
+ # create layers
+ layer_factory = ka_factory.get_factory('layer')
+ layertype_constraint = cpool.get(self, LAYERTYPE_CONSTRAINT)
+ number_of_constraint = cpool.get(self, NUMBER_OF_LAYERS_CONSTRAINT)
+
+ number_of_layers = random.randint(number_of_constraint[0], \
+ number_of_constraint[1])
+ self.layers = []
+ for i in range(number_of_layers):
+ single_layer = layer_factory.create_random(layertype_constraint, self.path)
+ single_layer.randomize()
+ self.layers.append(single_layer)
+
+ # create strategy for merging layers
+ merger_factory = ka_factory.get_factory('merger')
+ mergertype_constraint = cpool.get(self, MERGER_CONSTRAINT)
+ number_of_mergers = 1 if number_of_layers < 3 else int(number_of_layers / 2)
+ self.mergers = []
+ for i in range(number_of_mergers):
+ single_merger = merger_factory.create_random(mergertype_constraint, self.path)
+ single_merger.randomize()
+ self.mergers.append(single_merger)
+
+ # create background color
+ self.background.randomize()
+
+
+ def mutate(self):
+ """Make small random changes to the protozoon.
+ """
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ layertype_constraint = cpool.get(self, LAYERTYPE_CONSTRAINT)
+ number_of_constraint = cpool.get(self, NUMBER_OF_LAYERS_CONSTRAINT)
+
+ self._mutate_list(self.layers,
+ layertype_constraint, number_of_constraint,
+ ka_factory.get_factory('layer'), self.path)
+ mergertype_constraint = cpool.get(self, MERGER_CONSTRAINT)
+ self._mutate_list(self.mergers,
+ mergertype_constraint, number_of_constraint,
+ ka_factory.get_factory('merger'), self.path)
+
+ # mutate background color
+ self.background.mutate()
+
+ def shuffle(self):
+ """Shuffle layer list and delegate shuffling to the protozoons componets."""
+ # delegate shuffling to the layers child components
+ for single_layer in self.layers:
+ single_layer.shuffle()
+ # shuffle layers
+ if ka_random.is_shuffling():
+ random.shuffle(self.layers)
+ # shuffle mergers
+ if ka_random.is_shuffling():
+ random.shuffle(self.mergers)
+
+ def crossingover(self, other):
+ """Returns a deep copy mixed from my protozoon and the other protozoon.
+ pre: isinstance(other, Protozoon)
+ """
+ # deep copy
+ new_one = Protozoon()
+ # crossing over the layers
+ for single_layer in self._crossingover_list(self.layers, other.layers):
+ new_one.layers.append(single_layer.copy())
+
+ # crossing over the layer mergers
+ new_one.mergers = [m.copy() for m in self._crossingover_list(self.mergers,
+ other.mergers)]
+ # crossing over the background color
+ new_one.background = self.background.crossingover(other.background)
+ return new_one
+
+ def _crossingover_list(self, this_list, other_list):
+ # crossing over common part of this list and other list
+ len_this, len_other = len(this_list), len(other_list)
+ min_elements = min([len_this, len_other])
+ max_elements = max([len_this, len_other])
+
+ new_list = []
+ cross_sequence = ka_random.crossing_sequence(min_elements)
+ for index in range(min_elements):
+ this_element, other_element = this_list[index], other_list[index]
+ if this_element.__class__ == other_element.__class__:
+ # delegate crossing over to the elements components
+ new_list.append(this_element.crossingover(other_element))
+ else:
+ # crossing over whole elements
+ if cross_sequence[index]:
+ new_list.append(this_element)
+ else:
+ new_list.append(other_element)
+
+ # appending elements from protozoon of greater size
+ if len_this > len_other:
+ for index in range(min_elements, max_elements):
+ new_list.append(this_list[index])
+ if len_this < len_other:
+ for index in range(min_elements, max_elements):
+ new_list.append(other_list[index])
+ return new_list
+
+ def _mutate_list(self, this_list, type_constraint, number_of_constraint,
+ factory, path):
+ # maybe remove one element
+ len_this = len(this_list)
+ if len_this > number_of_constraint[0] and ka_random.is_mutating():
+ del this_list[random.randint(0, len_this-1)]
+ len_this -= 1
+ # maybe duplicate one of the elements
+ if len_this < number_of_constraint[1] and ka_random.is_mutating():
+ random_element = this_list[random.randint(0, len_this-1)]
+ dupli_element = random_element.copy()
+ this_list.insert(random.randint(0, len_this-1), dupli_element)
+ len_this += 1
+ # maybe insert a new element
+ if len_this < number_of_constraint[1] and ka_random.is_mutating():
+ single_element = factory.create_random(type_constraint, path)
+ single_element.randomize()
+ this_list.insert(random.randint(0, len_this-1), single_element)
+ len_this += 1
+
+ # delegate mutation to the elements child components
+ for single_element in this_list:
+ single_element.mutate()
+
+ def draw(self, ctx, width, height):
+ """
+ pre: ctx is not None
+ pre: width > 0
+ pre: height > 0
+ pre: width == height
+ """
+ ctx.save()
+ ctx.scale(width, height)
+ ctx.translate(0.5, 0.5)
+
+ # paint background
+ ctx.set_operator(cairo.OPERATOR_SOURCE)
+ ctx.set_source_rgb(self.background.rgba[0],
+ self.background.rgba[1],
+ self.background.rgba[2])
+ ctx.paint()
+
+ # merge all layers
+ for index, single_layer in enumerate(self.layers):
+ self.mergers[index % len(self.mergers)].draw_single_layer(
+ single_layer,
+ self.layers[-1-index],
+ ctx, width, height)
+ ctx.restore()
+
+ def explain(self, formater):
+ width = height = 256
+ # begin with header
+ titel = 'protozoon ' + str(id(self))
+ formater.header(titel)
+
+ # display combined layers
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ ctx = cairo.Context(surface)
+ self.draw(ctx, width, height)
+ formater.surface_item(surface, 'combined layers', titel)
+
+ # explain all layers from top to bottom
+ formater.begin_list('protozoon has %d layers' % (len(self.layers)))
+ reverse_list = self.layers[:]
+ reverse_list.reverse()
+ for index, single_layer in enumerate(reverse_list):
+ title = 'top level layer' if index == 0 else 'layer ' + str(index)
+ formater.begin_list(title)
+ # explain operator to merge layer
+ super(single_layer.__class__, single_layer).explain(formater)
+
+ # explain layer merging strategy
+ text, surface, descr = self.mergers[index % len(self.mergers)].explain()
+ if surface is not None:
+ formater.surface_item(surface, 'merging strategy:' + text, descr)
+ else:
+ formater.text_item(text)
+
+ # preview of a single layer
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ ctx = cairo.Context(surface)
+ ctx.scale(width, height)
+ ctx.translate(0.5, 0.5)
+
+ # paint checker board background
+ steps = 16
+# delta = (1.0 * width) / (1.0 * steps)
+ delta = (1.0) / (1.0 * steps)
+ ctx.set_operator(cairo.OPERATOR_SOURCE)
+ for row in range(steps):
+ for col in range(steps):
+ ctx.rectangle(col * delta - 0.5, row * delta - 0.5,
+ delta, delta)
+ if (col + row) % 2 == 0:
+ ctx.set_source_rgb(0.4, 0.4, 0.4)
+ else:
+ ctx.set_source_rgb(0.6, 0.6, 0.6)
+ ctx.fill()
+
+ ctx.save()
+ single_layer.draw(ctx, width, height)
+ formater.surface_item(surface, single_layer.__class__.__name__, \
+ single_layer.__class__.__name__)
+ ctx.restore()
+
+ # explain the layers details
+ formater.begin_list('Details for ' + single_layer.__class__.__name__)
+ single_layer.explain(formater)
+ formater.end_list()
+ formater.end_list()
+ formater.end_list()
+
+ #explain background color
+ formater.color_item(self.background, 'background color:', alfa=False)
+
+ # stop with footer
+ formater.footer()
+
+ def copy(self):
+ """ The protozoons copy constructor.
+ """
+ new_one = Protozoon()
+ new_one.layers = []
+ for single_layer in self.layers:
+ new_one.layers.append(single_layer.copy())
+ new_one.mergers = [m.copy() for m in self.mergers]
+ new_one.background = self.background.copy()
+ return new_one
diff --git a/po/Kandid.pot b/po/Kandid.pot
new file mode 100644
index 0000000..4cfcba0
--- /dev/null
+++ b/po/Kandid.pot
@@ -0,0 +1,49 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-10-31 13:04+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: activity/activity.info:2
+msgid "Connect"
+msgstr ""
+
+#: activity.py:63
+msgid "To play, share or invite someone."
+msgstr ""
+
+#: activity.py:133
+msgid "Waiting for another player to join."
+msgstr ""
+
+#: activity.py:164
+msgid "Joined a game. Waiting for my turn..."
+msgstr ""
+
+#: game.py:183
+msgid "The other player wins!"
+msgstr ""
+
+#: game.py:200
+msgid "Your turn"
+msgstr ""
+
+#: game.py:257
+msgid "You win!"
+msgstr ""
+
+#: game.py:259
+msgid "Other player's turn"
+msgstr ""
diff --git a/po/en.po b/po/en.po
new file mode 100644
index 0000000..2c8ed1f
--- /dev/null
+++ b/po/en.po
@@ -0,0 +1,49 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-10-31 13:04+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Translate Toolkit 1.0.1\n"
+
+#: activity/activity.info:2
+msgid "Connect"
+msgstr ""
+
+#: activity.py:63
+msgid "To play, share or invite someone."
+msgstr ""
+
+#: activity.py:133
+msgid "Waiting for another player to join."
+msgstr ""
+
+#: activity.py:164
+msgid "Joined a game. Waiting for my turn..."
+msgstr ""
+
+#: game.py:183
+msgid "The other player wins!"
+msgstr ""
+
+#: game.py:200
+msgid "Your turn"
+msgstr ""
+
+#: game.py:257
+msgid "You win!"
+msgstr ""
+
+#: game.py:259
+msgid "Other player's turn"
+msgstr ""
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..231385d
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,20 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+from sugar.activity import bundlebuilder
+
+bundlebuilder.start('Kandid')
diff --git a/test_enumerator.py b/test_enumerator.py
new file mode 100644
index 0000000..e4af604
--- /dev/null
+++ b/test_enumerator.py
@@ -0,0 +1,97 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import sys
+import cairo
+import ka_debug
+import ka_factory
+import ka_random
+import ka_extensionpoint
+import model_protozoon
+import model_constraintpool
+import model_layer
+
+class VariantEnumerator(object):
+
+ def explain_permutation(self):
+ ka_random.set_flurry(9)
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+
+ for operator_key in model_layer.Layer.OPERATORS:
+ for merger_key in ka_factory.get_factory('merger').keys():
+ cpool.clear_all()
+ cpool.set('/Protozoon', 'layernumberofconstraint', [4, 4])
+ cpool.set('/Protozoon', 'layertypeconstraint',
+ ['image', 'markovchain'])
+ cpool.set('/Protozoon/ImageLayer', 'operatorconstraint',
+ [operator_key])
+ cpool.set('/Protozoon/MarkovChainLayer', 'operatorconstraint',
+ [operator_key])
+ cpool.set('/Protozoon', 'mergertypeconstraint', merger_key)
+ self._explain_protozon(
+ model_layer.Layer.OPERATOR_NAMES[operator_key], merger_key)
+
+ for layer_key in ka_factory.get_factory('layer').keys():
+ for merger_key in ka_factory.get_factory('merger').keys():
+ cpool.clear_all()
+ cpool.set('/Protozoon', 'layernumberofconstraint', [3, 3])
+ cpool.set('/Protozoon', 'layertypeconstraint', layer_key)
+ cpool.set('/Protozoon', 'mergertypeconstraint', merger_key)
+ cpool.set('/Protozoon/ImageLayer', 'operatorconstraint',
+ [cairo.OPERATOR_OVER])
+ cpool.set('/Protozoon/MarkovChainLayer', 'operatorconstraint',
+ [cairo.OPERATOR_OVER])
+ cpool.set('/Protozoon/ReferencePattern', 'operatorconstraint',
+ [cairo.OPERATOR_OVER])
+ cpool.set('/Protozoon/LetterPress', 'operatorconstraint',
+ [cairo.OPERATOR_OVER])
+ self._explain_protozon(layer_key, merger_key)
+
+ def _explain_protozon(self, key1, key2):
+ a_protozoon = model_protozoon.Protozoon()
+ a_protozoon.randomize()
+
+ base_name = 'index'
+ base_folder = '/dev/shm/'
+ explain_id = '_' + key1 + '_' + key2
+ ep_list = ka_extensionpoint.list_extensions('formater')
+ formater = ka_extensionpoint.create(ep_list[0],
+ base_name,
+ explain_id,
+ base_folder)
+ a_protozoon.explain(formater)
+ file_path = formater.get_pathname('html')
+ formater.write_html_file(file_path)
+
+ def _write_file(self, file_path, page):
+ out_file = None
+ try:
+ out_file = open(file_path, 'w')
+ out_file.write(page)
+ except:
+ print 'failed writing [%s] [%s] [%s]' % \
+ (out_file.name, sys.exc_info()[0], sys.exc_info()[1])
+ finally:
+ if out_file:
+ out_file.close()
+
+def explain():
+ ve = VariantEnumerator()
+ ve.explain_permutation()
+
+if __name__ == '__main__':
+ explain() \ No newline at end of file
diff --git a/test_suite.py b/test_suite.py
new file mode 100644
index 0000000..d78b3f0
--- /dev/null
+++ b/test_suite.py
@@ -0,0 +1,385 @@
+# coding: UTF8
+# Copyright 2009 Thomas Jourdan
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import os
+import sys
+import unittest
+import cairo
+import ka_debug
+import ka_factory
+import ka_random
+import ka_extensionpoint
+import model_protozoon
+import model_constraintpool
+import model_population
+import model_locus
+import exon_color
+import exon_buzzword
+import exon_direction
+import exon_position
+
+EPSILON = 0.00001
+
+class TestKandidModel(unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ def _neutral(self):
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ permitted = [x for x in ka_extensionpoint.list_extensions(exon_position.POSITION_CONSTRAINT) if x not in ['positionconstraint_centered']]
+ cpool.set('/Position', exon_position.POSITION_CONSTRAINT, permitted)
+ cpool.set('/LetterPress/Position', exon_position.POSITION_CONSTRAINT, permitted)
+ cpool.set('/Protozoon/LetterPress/Position', exon_position.POSITION_CONSTRAINT, permitted)
+ permitted = ['colorconstraint_none']
+ cpool.set('/Color', exon_color.COLOR_CONSTRAINT, permitted)
+ cpool.set('/Protozoon/Color/', exon_color.COLOR_CONSTRAINT, permitted)
+ cpool.set('/MarkovChainLayer/Color', exon_color.COLOR_CONSTRAINT, permitted)
+ cpool.set('/LetterPress/Color', exon_color.COLOR_CONSTRAINT, permitted)
+ cpool.set('/Protozoon/LetterPress/Color', exon_color.COLOR_CONSTRAINT, permitted)
+ cpool.set('/Protozoon/MarkovChainLayer/Color', exon_color.COLOR_CONSTRAINT, permitted)
+
+ #/Protozoon/mergertypeconstraint ['flip', 'mask', 'rectangulartile', 'straight']
+ permitted = [x for x in ka_factory.get_factory('merger').keys() if x not in ['straight', ]]
+ cpool.set('/Protozoon', model_protozoon.MERGER_CONSTRAINT, permitted)
+
+ def test_locus(self):
+ self.assertEqual('/Locus', model_locus.Locus('/').path)
+ self.assertEqual('/A/Locus', model_locus.Locus('/A').path)
+ self.assertEqual('/A', model_locus.Locus('/A').get_trunk())
+ self.assertEqual('/A/B', model_locus.Locus('/A/B').get_trunk())
+
+ def test_html_formater(self):
+ base_name = 'test_html_formater'
+ base_folder = '/dev/shm/'
+ formater = ka_extensionpoint.create('formater_html', base_name, 'test', base_folder)
+ formater.header('Test title')
+ formater.begin_list('l1')
+ a_text = '&<>'
+ formater.text_item(a_text)
+
+ formater.begin_list('l2')
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ key = 'color'
+ for cc in ka_extensionpoint.list_extensions(key+'constraint'):
+ cpool.set('/'+key.capitalize(), key+'constraint', [cc])
+ a_color = exon_color.Color('/', 0.5, 0.6, 0.7, 0.8)
+ formater.color_item(a_color, 'Color ' + str(cc))
+ formater.end_list()
+
+ formater.end_list()
+ formater.footer()
+ file_path = formater.get_pathname('html')
+ formater.write_html_file(file_path)
+ exit_code = os.spawnvp(os.P_WAIT, 'tidy', ['tidy', '-i', file_path])
+ self.assertEqual(0, exit_code)
+
+ def test_protozon_formater(self):
+ ka_random.set_flurry(9)
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ cpool.clear_all()
+
+ ep_list = ka_extensionpoint.list_extensions('formater')
+ self.assertTrue(len(ep_list) > 0)
+ for ep_key in ep_list:
+ a_protozoon = model_protozoon.Protozoon()
+ a_protozoon.randomize()
+
+ base_name = 'index'
+ base_folder = '/dev/shm/'
+ formater = ka_extensionpoint.create(ep_key, base_name, a_protozoon.get_unique_id(), base_folder)
+ a_protozoon.explain(formater)
+
+ file_path = formater.get_pathname('html')
+ formater.write_html_file(file_path)
+ exit_code = os.spawnvp(os.P_WAIT, 'tidy', ['tidy', '-i', file_path])
+ self.assertEqual(0, exit_code)
+
+ def _write_file(self, file_path, page):
+ out_file = None
+ try:
+ out_file = open(file_path, 'w')
+ out_file.write(page)
+ except:
+ self.fail('failed writing [%s] [%s] [%s]' % \
+ (out_file.name, sys.exc_info()[0], sys.exc_info()[1]))
+ finally:
+ if out_file:
+ out_file.close()
+
+ def test_constrain_pool(self):
+ position = exon_position.Position('/', 0.1, 0.2)
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ cpool.get(position, exon_position.POSITION_CONSTRAINT)
+ print cpool
+
+ def test_limit(self):
+ self.assertTrue(ka_random.limitate(-0.01) == 0.0)
+ self.assertTrue(ka_random.limitate(0.01) == 0.01)
+ self.assertTrue(ka_random.limitate(1.01) == 1.0)
+ self.assertTrue(ka_random.limitate_range(-0.02, -0.01, 0.01) == -0.01)
+ self.assertTrue(ka_random.limitate_range(0.01, -0.01, 0.01) == 0.01)
+ self.assertTrue(ka_random.limitate_range(0.011, -0.01, 0.01) == 0.01)
+ self.assertTrue(abs(ka_random.cyclic_limitate(-0.01) - 0.99) < EPSILON)
+ self.assertTrue(abs(ka_random.cyclic_limitate(0.01) - 0.01) < EPSILON)
+ self.assertTrue(abs(ka_random.cyclic_limitate(1.01) - 0.01) < EPSILON)
+ for value in range(-20, 20):
+ self.assertTrue(0.0 <= ka_random.limitate(0.1 * value) <= 1.0)
+ self.assertTrue(0.0 <= ka_random.cyclic_limitate(0.1 * value) <= 1.0)
+
+ def test_extensionpoint(self):
+# ka_extensionpoint.scann()
+ ep_types = ka_extensionpoint.list_extension_types()
+ self.assertTrue(len(ep_types) > 0)
+
+ ep_list = ka_extensionpoint.list_extensions('colorconstraint')
+ self.assertTrue(len(ep_list) > 0)
+ for ep_key in ep_list:
+ colorconstraint = ka_extensionpoint.create(ep_key, '/')
+ self.assertEqual(4, len(colorconstraint.filter((.5, .5, .5, 1.))))
+
+ def test_deepcopy(self):
+ self._neutral()
+ list1 = [exon_buzzword.Buzzword('/', [u'sugar', u'kandid', u'']), \
+ exon_color.Color('/', 0.1, 0.2, 0.3, 1.0), \
+ exon_direction.Direction('/', 0.1, 0.2), \
+ exon_position.Position('/', 0.1, 0.2), \
+ ]
+ list2 = ka_random.copy_list(list1)
+ self.assertEqual(len(list1), len(list2))
+ for index, i in enumerate(list1):
+ self.assertEqual(list1[index], list2[index])
+ self.assertTrue(list1[index] is not list2[index])
+
+ def test_random(self):
+ ka_random.set_flurry(0)
+ seq = ka_random.crossing_sequence(10)
+ for index in range(1,len(seq)):
+ self.assertEqual(seq[0], seq[index])
+ ka_random.set_flurry(9)
+ seq = ka_random.crossingover([11], [21, 22, 23, 24, 25])
+ print seq
+ seq = ka_random.crossingover([11, 12, 13], [21, 22, 23, 24, 25])
+ print seq
+ seq = ka_random.crossingover([11, 12, 13], [21])
+ print seq
+
+ def test_positionconstraint(self):
+ self._run_ep_test('position')
+
+ def test_directionconstraint(self):
+ self._run_ep_test('direction')
+
+ def _run_ep_test(self, key):
+ for flavor in ka_extensionpoint.list_extensions(key+'constraint'):
+ constraint = ka_extensionpoint.create(flavor, '/')
+ x_pos, y_pos = 0.1, 0.2
+ x_pos, y_pos = constraint.filter(x_pos, y_pos)
+ x_pos, y_pos = constraint.randomize()
+ x_pos, y_pos = constraint.mutate(x_pos, y_pos)
+ self.assertTrue(isinstance(x_pos, float))
+ self.assertTrue(isinstance(y_pos, float))
+
+ def test_classification(self):
+ population_size = 12
+ model = model_population.KandidModel(population_size)
+ self.assertEqual(population_size, len(model.fitness))
+ for fa in [1, 2, population_size-1]:
+ model.fade_away = fa
+ for i in range(population_size):
+ model.fitness[i] = (population_size-i-1)*(1.0 / population_size)
+ self._check_classification(population_size, model)
+ for ft in [0.0, 5.0, 9.0]:
+ for i in range(population_size):
+ model.fitness[i] = ft
+ self._check_classification(population_size, model)
+
+ def _check_classification(self, population_size, model):
+ good, moderate, poor = model.classify()
+ self.assertEqual(model.fade_away, len(poor))
+ self.assertEqual(population_size, len(good) + len(moderate) + len(poor))
+ self.assertTrue(good[0] in model.protozoans)
+ self.assertFalse(model_population.contains_reference(good[0], moderate))
+ self.assertFalse(model_population.contains_reference(good[0], poor))
+ for mo in moderate:
+ self.assertFalse(model_population.contains_reference(mo, poor))
+ self.assertFalse(model_population.contains_reference(mo, good))
+ for po in poor:
+ self.assertFalse(model_population.contains_reference(po, moderate))
+ self.assertFalse(model_population.contains_reference(po, good))
+
+ def test_exon_position(self):
+ self._run_exon_test('position', exon_position.Position, 0.5, 0.5)
+
+ def test_exon_direction(self):
+ self._run_exon_test('direction', exon_direction.Direction, 0.3, 0.4)
+
+ def test_exon_buzzword(self):
+ self._run_exon_test('buzzword', exon_buzzword.Buzzword, [u'a', u'b'])
+
+ def _run_exon_test(self, key, constructor, *params):
+ ka_random.set_flurry(9)
+ self._neutral()
+
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ for cc in ka_extensionpoint.list_extensions(key+'constraint'):
+ cpool.set('/'+key.capitalize(), key+'constraint', [cc])
+ element1 = constructor('/', *params)
+ element2 = element1.copy()
+ for i in range(32):
+ element3 = element1.crossingover(element2)
+ self.assertEqual(element1, element2)
+ self.assertEqual(element1, element3)
+ equal = True
+ for i in range(32):
+ element2.randomize()
+ element2.mutate()
+ element2.shuffle()
+ equal = equal and element1 == element2
+ self.assertFalse(equal)
+ self.assertEqual(element1, element3)
+
+ def test_exon_color(self):
+ ka_random.set_flurry(9)
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+ cpool.clear_all()
+
+ key = 'color'
+ cpool = model_constraintpool.ConstraintPool.get_pool()
+
+ for cc in ka_extensionpoint.list_extensions(key+'constraint'):
+ cpool.set('/'+key.capitalize(), key+'constraint', [cc])
+ a_color = exon_color.Color('/', 0.5, 0.5, 0.5, 1.0)
+ self.assertFalse(a_color is a_color.crossingover( \
+ exon_color.Color('/', 0.5, 0.5, 0.5, 1.0)))
+ self.assertEqual(4, len(a_color.copy().rgba))
+ a_color.randomize()
+ self.assertEqual(4, len(a_color.rgba))
+ for i in range(32):
+ a_color.mutate()
+ self.assertEqual(4, len(a_color.rgba))
+
+ self._neutral()
+ color1 = exon_color.Color('/', 1.0, 1.0, 1.0, 1.0)
+ color2 = color1.copy()
+ self.assertTrue(color2 is not color1)
+ self.assertTrue(color2 == color1)
+ color1.randomize()
+ self.assertFalse(color2 == color1)
+ for i in range(32):
+ color3 = color1.crossingover(color2)
+ self.assertTrue(color3 is not color1)
+ self.assertTrue(color3 is not color2)
+ self.assertTrue(color3 == color1 or color3 == color2)
+ color4 = color3.copy()
+ eq = True
+ for i in range(32):
+ color4.mutate()
+ color4.shuffle()
+ eq = eq and color4 == color3
+ self.assertFalse(eq)
+
+ def test_layer_factory(self):
+ self._neutral()
+ layer_factory = ka_factory.get_factory('layer')
+ keys = layer_factory.keys()
+ self.assertTrue(layer_factory.count() >= 1)
+ self.assertEqual(len(keys), layer_factory.count())
+ for layer_key in keys:
+ self.assertTrue(layer_factory.create(layer_key, '/') is not None)
+ self.assertTrue(layer_factory.create_random([keys[0]], '/') is not None)
+
+ def test_layer(self):
+ ka_random.set_flurry(9)
+ self._neutral()
+
+ layer_factory = ka_factory.get_factory('layer')
+ for layer_key in layer_factory.keys():
+ layer1 = layer_factory.create(layer_key, '/')
+ layer2 = layer1.copy()
+ self.assertTrue(layer2 is not layer1)
+ self.assertTrue(layer2 == layer1)
+ layer1.randomize()
+ layer1.shuffle()
+ if layer2 == layer1:
+ pass
+ self.assertFalse(layer2 == layer1)
+ layer3 = layer1.crossingover(layer2)
+ self.assertTrue(layer3 is not layer1)
+ self.assertTrue(layer3 is not layer2)
+ layer4 = layer3.copy()
+ layer4.mutate()
+ for i in range(32):
+ layer4.shuffle()
+# self.assertFalse(layer4 == layer3)
+
+ def test_protozoon(self):
+ ka_random.set_flurry(9)
+ self._neutral()
+
+ protozoon1 = model_protozoon.Protozoon()
+ protozoon1.randomize()
+ protozoon2 = protozoon1.copy()
+ self.assertTrue(protozoon2 is not protozoon1)
+ self.assertTrue(protozoon2 == protozoon1)
+ protozoon1.randomize()
+ protozoon1.shuffle()
+ self.assertFalse(protozoon2 == protozoon1)
+ protozoon3 = protozoon1.crossingover(protozoon2)
+ self.assertTrue(protozoon3 is not protozoon1)
+ self.assertTrue(protozoon3 is not protozoon2)
+ protozoon4 = protozoon3.copy()
+ protozoon4.mutate()
+ for i in range(32):
+ protozoon4.shuffle()
+# self.assertFalse(protozoon4 == protozoon3)
+
+ def test_draw(self):
+ self._neutral()
+
+ width, height = 200, 200
+ surface = cairo.SVGSurface('testoutput_p.svg', width, height)
+ ctx = cairo.Context(surface)
+ protozoon1 = model_protozoon.Protozoon()
+ protozoon1.randomize()
+ protozoon1.draw(ctx, width, height)
+
+ layer_factory = ka_factory.get_factory('layer')
+ for strategy in layer_factory.keys():
+ layer = layer_factory.create(strategy, '/')
+ layer.randomize()
+ outname = 'testoutput_' + strategy
+# surface = cairo.SVGSurface(outname + '.svg', width, height)
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ ctx = cairo.Context(surface)
+ ctx.scale(width, height)
+ # paint background
+ ctx.set_operator(cairo.OPERATOR_SOURCE)
+ ctx.set_source_rgb(0.0, 0.0, 0.0)
+ ctx.paint()
+ layer.draw(ctx, width, height)
+ surface.write_to_png(outname + '.png')
+
+ka_debug.info('starting TestSuite')
+ka_debug.err('testing error message channel.')
+alltests = unittest.TestSuite((\
+ unittest.makeSuite(TestKandidModel), \
+ ))
+unittest.TextTestRunner(verbosity=2).run(alltests)