diff --git a/doc/GPL_V3 b/doc/GPL_V3 new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/doc/GPL_V3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/src/.emacs.desktop b/src/.emacs.desktop new file mode 100644 index 00000000..9bb7eeae --- /dev/null +++ b/src/.emacs.desktop @@ -0,0 +1,673 @@ +;; -*- mode: emacs-lisp; coding: emacs-mule; -*- +;; -------------------------------------------------------------------------- +;; Desktop File for Emacs +;; -------------------------------------------------------------------------- +;; Created Thu Nov 1 17:09:37 2012 +;; Desktop file format version 206 +;; Emacs version 23.3.1 + +;; Global section: +(setq extended-command-history nil) +(setq file-name-history nil) +(setq grep-history '("grep -nH -e ")) +(setq compile-history nil) +(setq minibuffer-history nil) +(setq query-replace-history '("ARGoS3" "Argos3" "argos3" "argos" "core" "core/common" "argos/core/" "argos/" "argos" "argos2" "argos" "argos3" "ARGOS_" "ARGOS3_" "" "inline " "CXX_" "C_" "argos" "argos3" " /****************************************/" "/****************************************/" "" "./" "TTypeMap" "TCreatorMap" "GetTypeMap" "GetCreatorMap" "plugin" "user")) +(setq read-expression-history nil) +(setq regexp-history nil) +(setq regexp-search-ring nil) +(setq search-ring '("dynamic li" "led" "data" "clone" "visito")) +(setq shell-command-history nil) +(setq tags-file-name nil) +(setq register-alist nil) + +;; Buffer section -- buffers listed in same order as in buffer list: +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/simulator.cpp" + "simulator.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 3403 + '(1408 nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/space/space.h" + "space.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(750 nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/argos3/core/simulator/entity/embodied_entity.h" + "embodied_entity.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 552 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/datatypes/any.h" + "any.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1740 + '(1735 nil) + nil + nil + '((buffer-file-coding-system . iso-latin-1-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos2/common/utility/datatypes/any.h" + "any.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 3399 + '(1033 nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/CMakeLists.txt" + "CMakeLists.txt" + 'cmake-mode + nil + 1143 + '(1140 nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/argos3/core/simulator/entity/entity.h" + "entity.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 662 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/CMakeLists.txt" + "CMakeLists.txt" + 'cmake-mode + nil + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/argos_command_line_arg_parser.cpp" + "argos_command_line_arg_parser.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/physics_engine/physics_engine.cpp" + "physics_engine.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/physics_engine/physics_engine.h" + "physics_engine.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/physics_engine/physics_engine_entity.h" + "physics_engine_entity.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/simulator.h" + "simulator.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/argos_command_line_arg_parser.h" + "argos_command_line_arg_parser.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/main.cpp" + "main.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/space/space_no_threads.h" + "space_no_threads.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/space/space_multi_thread_scatter_gather.h" + "space_multi_thread_scatter_gather.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/space/space_no_threads.cpp" + "space_no_threads.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/space/space_hash_native.h" + "space_hash_native.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/space/space_multi_thread_scatter_gather.cpp" + "space_multi_thread_scatter_gather.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/space/space_multi_thread_h_dispatch.h" + "space_multi_thread_h_dispatch.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/space/space_multi_thread_h_dispatch.cpp" + "space_multi_thread_h_dispatch.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/space/space_hash.h" + "space_hash.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/space/space.cpp" + "space.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/extra/operation.h" + "operation.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/extra/entity.h" + "entity.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/simulator/extra/old_main.cpp" + "old_main.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/control_interface/ci_sensor.h" + "ci_sensor.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/control_interface/ci_actuator.h" + "ci_actuator.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/control_interface/ci_robot.h" + "ci_robot.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/control_interface/ci_robot.cpp" + "ci_robot.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/control_interface/ci_controller.h" + "ci_controller.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/plugins/dynamic_loading.cpp" + "dynamic_loading.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/plugins/factory_impl.h" + "factory_impl.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/plugins/dynamic_loading.h" + "dynamic_loading.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/plugins/factory.h" + "factory.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/string_utilities.cpp" + "string_utilities.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/command_line_arg_parser.h" + "command_line_arg_parser.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/memento.h" + "memento.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/base_configurable_resource.h" + "base_configurable_resource.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/command_line_arg_parser.cpp" + "command_line_arg_parser.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/tinyxml/ticpprc.h" + "ticpprc.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/tinyxml/ticpp.cpp" + "ticpp.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/tinyxml/tinyxmlerror.cpp" + "tinyxmlerror.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/tinyxml/tinyxml.h" + "tinyxml.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/tinyxml/tinyxmlparser.cpp" + "tinyxmlparser.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/tinyxml/tinystr.h" + "tinystr.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/tinyxml/tinystr.cpp" + "tinystr.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . iso-latin-1-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/tinyxml/ticpp.h" + "ticpp.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/tinyxml/tinyxml.cpp" + "tinyxml.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/argos_configuration.h" + "argos_configuration.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/configuration/argos_exception.h" + "argos_exception.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/string_utilities.h" + "string_utilities.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/logging/argos_log.h" + "argos_log.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/logging/argos_colored_text.h" + "argos_colored_text.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/logging/argos_log.cpp" + "argos_log.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/datatypes/byte_array.h" + "byte_array.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/datatypes/byte_array.cpp" + "byte_array.cpp" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + +(desktop-create-buffer 206 + "/home/pincy/Fabrica/argos3/src/core/utility/datatypes/datatypes.h" + "datatypes.h" + 'c++-mode + '(abbrev-mode doxymacs-mode) + 1 + '(nil nil) + nil + nil + '((buffer-file-coding-system . undecided-unix))) + diff --git a/src/.emacs.desktop.lock b/src/.emacs.desktop.lock new file mode 100644 index 00000000..4bd09e97 --- /dev/null +++ b/src/.emacs.desktop.lock @@ -0,0 +1 @@ +22449 \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..79e8d638 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,57 @@ +# +# Set minimum required version +# +cmake_minimum_required(VERSION 2.8) + +# +# Set the path additional cmake files must be searched for +# +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + +# +# Make sure we are under unix +# +if(NOT UNIX) + message(FATAL_ERROR "ARGoS compiles only under UNIX, sorry!") +endif(NOT UNIX) + +# +# Check for GCC version >= 4.2 +# +execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) +if (GCC_VERSION VERSION_GREATER 4.2 OR GCC_VERSION VERSION_EQUAL 4.2) + message(STATUS "GCC/G++ version >= 4.2") +else(GCC_VERSION VERSION_GREATER 4.2 OR GCC_VERSION VERSION_EQUAL 4.2) + message(FATAL_ERROR "You need to have at least GCC/G++ version 4.2!") +endif(GCC_VERSION VERSION_GREATER 4.2 OR GCC_VERSION VERSION_EQUAL 4.2) + +# +# Set compiler flags +# +include(${CMAKE_SOURCE_DIR}/cmake/ARGoSBuildFlags.cmake) + +# +# Set build options +# +include(${CMAKE_SOURCE_DIR}/cmake/ARGoSBuildOptions.cmake) + +# +# Check for GSL +# It is required only when compiling the simulator +# +find_package(GSL) +if(GSL_FOUND) + add_definitions(${CMAKE_GSL_CXX_FLAGS}) + include_directories(${GSL_INCLUDE_DIR}) + link_directories(${GSL_LINK_DIRECTORIES}) +else(GSL_FOUND) + if(ARGOS_BUILD_FOR STREQUAL "SIMULATOR") + message(FATAL_ERROR "When compiling the ARGoS simulator, GSL is required") + endif(ARGOS_BUILD_FOR STREQUAL "SIMULATOR") +endif(GSL_FOUND) + +# +# Compile stuff +# +add_subdirectory(core) +#add_subdirectory(plugins) diff --git a/src/argos3 b/src/argos3 new file mode 120000 index 00000000..945c9b46 --- /dev/null +++ b/src/argos3 @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/src/cmake/ARGoSBuildFlags.cmake b/src/cmake/ARGoSBuildFlags.cmake new file mode 100644 index 00000000..10cb155c --- /dev/null +++ b/src/cmake/ARGoSBuildFlags.cmake @@ -0,0 +1,27 @@ +# +# General compilation flags +# +set(CMAKE_C_FLAGS "-Wall") +set(CMAKE_C_FLAGS_RELEASE "-Os -DNDEBUG") +set(CMAKE_C_FLAGS_RELWITHDEBINFO "-Os -ggdb3 -DNDEBUG") +set(CMAKE_C_FLAGS_DEBUG "-ggdb3") + +set(CMAKE_CXX_FLAGS "-Wall") +set(CMAKE_CXX_FLAGS_RELEASE "-Os -DNDEBUG") +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-Os -ggdb3 -DNDEBUG") +set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3") + +if(APPLE) + # MAC OSX + # Allow for dynamic lookup of undefined symbols + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -undefined dynamic_lookup") +else(APPLE) + # Linux + # Avoid discarding unused symbols to allow plugins to work + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-as-needed") +endif(APPLE) + +# +# This is required for TinyXML++ to work +# +add_definitions(-DTIXML_USE_TICPP) diff --git a/src/cmake/ARGoSBuildOptions.cmake b/src/cmake/ARGoSBuildOptions.cmake new file mode 100644 index 00000000..a28ba5b2 --- /dev/null +++ b/src/cmake/ARGoSBuildOptions.cmake @@ -0,0 +1,40 @@ +# +# What is ARGoS being built for? +# Accepted values: "SIMULATOR" or a robot name (uppercase) +# +if(NOT DEFINED ARGOS_BUILD_FOR) + # Variable was not set, set to default value + set(ARGOS_BUILD_FOR "SIMULATOR" CACHE STRING "What is ARGoS being built for? \"Simulator\" or a robot name (uppercase)") +endif(NOT DEFINED ARGOS_BUILD_FOR) +# Set the macro according to value set in ARGOS_BUILD_FOR +add_definitions(-DARGOS_${ARGOS_BUILD_FOR}_BUILD) + +# +# Compile thread-safe or thread-unsafe log +# By default, use the thread-safe version +# When compiling for the simulator, the thread-safe version is mandatory +# +if(NOT DEFINED ARGOS_THREADSAFE_LOG) + set(ARGOS_THREADSAFE_LOG TRUE CACHE BOOL "ON -> compile thread-safe version of log, OFF -> compile thread-unsafe version of log") +endif(NOT DEFINED ARGOS_THREADSAFE_LOG) +if((NOT ARGOS_THREADSAFE_LOG) AND (ARGOS_BUILD_FOR STREQUAL "SIMULATOR")) + message(FATAL_ERROR "When compiling for the simulator, ARGOS_THREADSAFE_LOG must be ON") +endif((NOT ARGOS_THREADSAFE_LOG) AND (ARGOS_BUILD_FOR STREQUAL "SIMULATOR")) +if(ARGOS_THREADSAFE_LOG) + add_definitions(-DARGOS_THREADSAFE_LOG) +endif(ARGOS_THREADSAFE_LOG) + +# +# Compile support for dynamic library loading or not +# By default, support for dynamic library loading is on +# When compiling for the simulator, support for dynamic library loading must be on +# +if(NOT DEFINED ARGOS_DYNAMIC_LIBRARY_LOADING) + set(ARGOS_DYNAMIC_LIBRARY_LOADING TRUE CACHE BOOL "ON -> compile support for dynamic library loading, OFF -> no support for dynamic library loading") +endif(NOT DEFINED ARGOS_DYNAMIC_LIBRARY_LOADING) +if((NOT ARGOS_DYNAMIC_LIBRARY_LOADING) AND (ARGOS_BUILD_FOR STREQUAL "SIMULATOR")) + message(FATAL_ERROR "When compiling for the simulator, ARGOS_DYNAMIC_LIBRARY_LOADING must be ON") +endif((NOT ARGOS_DYNAMIC_LIBRARY_LOADING) AND (ARGOS_BUILD_FOR STREQUAL "SIMULATOR")) +if(ARGOS_DYNAMIC_LIBRARY_LOADING) + add_definitions(-DARGOS_DYNAMIC_LIBRARY_LOADING) +endif(ARGOS_DYNAMIC_LIBRARY_LOADING) diff --git a/src/cmake/CheckQTOpenGL4ARGoS.cmake b/src/cmake/CheckQTOpenGL4ARGoS.cmake new file mode 100644 index 00000000..9fa6ef00 --- /dev/null +++ b/src/cmake/CheckQTOpenGL4ARGoS.cmake @@ -0,0 +1,71 @@ +# Checks whether QT4, OpenGL and GLUT are present and correctly configured +# +# Usage: +# +# INCLUDE(CheckQTOpenGL4ARGoS) +# IF(ARGOS_COMPILE_QTOPENGL) +# +# your qt-opengl-dependent code here +# +# ENDIF(ARGOS_COMPILE_QTOPENGL) +# +# This module defines this variable: +# +# ARGOS_COMPILE_QTOPENGL - True if QT4, OpenGL and GLUT are present and correctly configured +# +# AUTHOR: Carlo Pinciroli + +# Check for QT4 +set(ARGOS_COMPILE_QTOPENGL false) +set(QT_USE_QTOPENGL true) +find_package(Qt4) +if(QT4_FOUND) + # QT4 found, is it the minimum required version? + if(QT_VERSION_MAJOR GREATER 3 AND QT_VERSION_MINOR GREATER 2) + # Is the QTOpenGL module present? + if(QT_QTOPENGL_FOUND) + # QT is OK, now check for OpenGL + find_package(OpenGL) + if(OPENGL_FOUND) + # OpenGL is ok, now check for GLUT + find_package(GLUT) + if(GLUT_FOUND) + # GLUT is ok + + # All the required libraries are OK + set(ARGOS_COMPILE_QTOPENGL true) + if( APPLE ) + include_directories(${OPENGL_INCLUDE_DIR}/Headers) + endif( APPLE ) + include(${QT_USE_FILE}) + + # Now check for SDL (optional) + set(SDL_BUILDING_LIBRARY true) + find_package(SDL) + if(SDL_FOUND) + # SDL is ok + add_definitions(-DQTOPENGL_WITH_SDL) + + else(SDL_FOUND) + message(STATUS "SDL not found. QTOpenGL won't have joystick support.") + endif(SDL_FOUND) + + else(GLUT_FOUND) + message(STATUS "GLUT not found. Skipping compilation of QT-OpenGL visualization.") + endif(GLUT_FOUND) + + else(OPENGL_FOUND) + message(STATUS "OpenGL not found. Skipping compilation of QT-OpenGL visualization.") + endif(OPENGL_FOUND) + + else(QT_QTOPENGL_FOUND) + message(STATUS "QtOpenGL module not found. Skipping compilation of QT-OpenGL visualization.") + endif(QT_QTOPENGL_FOUND) + + else(QT_VERSION_MAJOR GREATER 3 AND QT_VERSION_MINOR GREATER 2) + message(STATUS "Minimum required version for Qt (>= 4.3) not found. Skipping compilation of QT-OpenGL visualization.") + endif(QT_VERSION_MAJOR GREATER 3 AND QT_VERSION_MINOR GREATER 2) + +else(QT4_FOUND) + message(STATUS "Qt4 not found, skipping compilation of QT-OpenGL visualization.") +endif(QT4_FOUND) diff --git a/src/cmake/FindGSL.cmake b/src/cmake/FindGSL.cmake new file mode 100644 index 00000000..856faa01 --- /dev/null +++ b/src/cmake/FindGSL.cmake @@ -0,0 +1,179 @@ +# Try to find gnu scientific library GSL +# See +# http://www.gnu.org/software/gsl/ and +# http://gnuwin32.sourceforge.net/packages/gsl.htm +# +# Once run this will define: +# +# GSL_FOUND = system has GSL lib +# +# GSL_LIBRARIES = full path to the libraries +# on Unix/Linux with additional linker flags from "gsl-config --libs" +# +# CMAKE_GSL_CXX_FLAGS = Unix compiler flags for GSL, essentially "`gsl-config --cxxflags`" +# +# GSL_INCLUDE_DIR = where to find headers +# +# GSL_LINK_DIRECTORIES = link directories, useful for rpath on Unix +# GSL_EXE_LINKER_FLAGS = rpath on Unix +# +# Felix Woelk 07/2004 +# Jan Woetzel +# +# www.mip.informatik.uni-kiel.de +# -------------------------------- + +IF(WIN32) + # JW tested with gsl-1.8, Windows XP, MSVS 7.1, MSVS 8.0 + SET(GSL_POSSIBLE_ROOT_DIRS + ${GSL_ROOT_DIR} + $ENV{GSL_ROOT_DIR} + ${GSL_DIR} + ${GSL_HOME} + $ENV{GSL_DIR} + $ENV{GSL_HOME} + $ENV{EXTERN_LIBS_DIR}/gsl + $ENV{EXTRA} + # "C:/home/jw/source2/gsl-1.8" + ) + FIND_PATH(GSL_INCLUDE_DIR + NAMES gsl/gsl_cdf.h gsl/gsl_randist.h + PATHS ${GSL_POSSIBLE_ROOT_DIRS} + PATH_SUFFIXES include + DOC "GSL header include dir" + ) + + FIND_LIBRARY(GSL_GSL_LIBRARY + NAMES gsl libgsl + PATHS ${GSL_POSSIBLE_ROOT_DIRS} + PATH_SUFFIXES lib + DOC "GSL library dir" ) + + FIND_LIBRARY(GSL_GSLCBLAS_LIBRARY + NAMES gslcblas libgslcblas + PATHS ${GSL_POSSIBLE_ROOT_DIRS} + PATH_SUFFIXES lib + DOC "GSL cblas library dir" ) + + SET(GSL_LIBRARIES ${GSL_GSL_LIBRARY}) + + #MESSAGE("DBG\n" + # "GSL_GSL_LIBRARY=${GSL_GSL_LIBRARY}\n" + # "GSL_GSLCBLAS_LIBRARY=${GSL_GSLCBLAS_LIBRARY}\n" + # "GSL_LIBRARIES=${GSL_LIBRARIES}") + + +ELSE(WIN32) + + IF(UNIX) + SET(GSL_CONFIG_PREFER_PATH + "$ENV{GSL_DIR}/bin" + "$ENV{GSL_DIR}" + "$ENV{GSL_HOME}/bin" + "$ENV{GSL_HOME}" + CACHE STRING "preferred path to GSL (gsl-config)") + FIND_PROGRAM(GSL_CONFIG gsl-config + ${GSL_CONFIG_PREFER_PATH} + /usr/bin/ + /opt/local/bin + ) + # MESSAGE("DBG GSL_CONFIG ${GSL_CONFIG}") + + IF (GSL_CONFIG) + + MESSAGE(STATUS "GSL using gsl-config ${GSL_CONFIG}") + # set CXXFLAGS to be fed into CXX_FLAGS by the user: + EXEC_PROGRAM(${GSL_CONFIG} + ARGS --cflags + OUTPUT_VARIABLE GSL_CXX_FLAGS ) + #SET(GSL_CXX_FLAGS "`${GSL_CONFIG} --cflags`") + + # set INCLUDE_DIRS to prefix+include + EXEC_PROGRAM(${GSL_CONFIG} + ARGS --prefix + OUTPUT_VARIABLE GSL_PREFIX) + SET(GSL_INCLUDE_DIR ${GSL_PREFIX}/include CACHE STRING INTERNAL) + + # set link libraries and link flags + + #SET(GSL_LIBRARIES "`${GSL_CONFIG} --libs`") + + # extract link dirs for rpath + EXEC_PROGRAM(${GSL_CONFIG} + ARGS --libs + OUTPUT_VARIABLE GSL_CONFIG_LIBS ) + SET(GSL_LIBRARIES "${GSL_CONFIG_LIBS}") + + # split off the link dirs (for rpath) + # use regular expression to match wildcard equivalent "-L*" + # with is a space or a semicolon + STRING(REGEX MATCHALL "[-][L]([^ ;])+" + GSL_LINK_DIRECTORIES_WITH_PREFIX + "${GSL_CONFIG_LIBS}" ) + # MESSAGE("DBG GSL_LINK_DIRECTORIES_WITH_PREFIX=${GSL_LINK_DIRECTORIES_WITH_PREFIX}") + + # remove prefix -L because we need the pure directory for LINK_DIRECTORIES + + IF (GSL_LINK_DIRECTORIES_WITH_PREFIX) + STRING(REGEX REPLACE "[-][L]" "" GSL_LINK_DIRECTORIES ${GSL_LINK_DIRECTORIES_WITH_PREFIX} ) + ENDIF (GSL_LINK_DIRECTORIES_WITH_PREFIX) + SET(GSL_EXE_LINKER_FLAGS "-Wl,-rpath,${GSL_LINK_DIRECTORIES}" CACHE STRING INTERNAL) + # MESSAGE("DBG GSL_LINK_DIRECTORIES=${GSL_LINK_DIRECTORIES}") + # MESSAGE("DBG GSL_EXE_LINKER_FLAGS=${GSL_EXE_LINKER_FLAGS}") + + # ADD_DEFINITIONS("-DHAVE_GSL") + # SET(GSL_DEFINITIONS "-DHAVE_GSL") + MARK_AS_ADVANCED( + GSL_CXX_FLAGS + GSL_INCLUDE_DIR + GSL_LIBRARIES + GSL_LINK_DIRECTORIES + GSL_DEFINITIONS + ) + MESSAGE(STATUS "Using GSL from ${GSL_PREFIX}") + + ELSE(GSL_CONFIG) + + INCLUDE(UsePkgConfig) #needed for PKGCONFIG(...) + + MESSAGE(STATUS "GSL using pkgconfig") + # PKGCONFIG(gsl includedir libdir linkflags cflags) + PKGCONFIG(gsl GSL_INCLUDE_DIR GSL_LINK_DIRECTORIES GSL_LIBRARIES GSL_CXX_FLAGS) + IF(GSL_INCLUDE_DIR) + MARK_AS_ADVANCED( + GSL_CXX_FLAGS + GSL_INCLUDE_DIR + GSL_LIBRARIES + GSL_LINK_DIRECTORIES + ) + + ELSE(GSL_INCLUDE_DIR) + MESSAGE("FindGSL.cmake: gsl-config/pkg-config gsl not found. Please set it manually. GSL_CONFIG=${GSL_CONFIG}") + ENDIF(GSL_INCLUDE_DIR) + + ENDIF(GSL_CONFIG) + + ENDIF(UNIX) +ENDIF(WIN32) + + +IF(GSL_LIBRARIES) + IF(GSL_INCLUDE_DIR OR GSL_CXX_FLAGS) + + SET(GSL_FOUND 1) + + ENDIF(GSL_INCLUDE_DIR OR GSL_CXX_FLAGS) +ENDIF(GSL_LIBRARIES) + + +# ========================================== +IF(NOT GSL_FOUND) + # make FIND_PACKAGE friendly + IF(NOT GSL_FIND_QUIETLY) + IF(GSL_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "GSL required, please specify it's location.") + ELSE(GSL_FIND_REQUIRED) + MESSAGE(STATUS "ERROR: GSL was not found.") + ENDIF(GSL_FIND_REQUIRED) + ENDIF(NOT GSL_FIND_QUIETLY) +ENDIF(NOT GSL_FOUND) diff --git a/src/cmake/FindPthreads.cmake b/src/cmake/FindPthreads.cmake new file mode 100644 index 00000000..a34638b7 --- /dev/null +++ b/src/cmake/FindPthreads.cmake @@ -0,0 +1,91 @@ +# Find the Pthreads library +# This module searches for the Pthreads library (including the +# pthreads-win32 port). +# +# This module defines these variables: +# +# PTHREADS_FOUND - True if the Pthreads library was found +# PTHREADS_LIBRARY - The location of the Pthreads library +# PTHREADS_INCLUDE_DIR - The include directory of the Pthreads library +# PTHREADS_DEFINITIONS - Preprocessor definitions to define (HAVE_PTHREAD_H is a fairly common one) +# +# This module responds to the PTHREADS_EXCEPTION_SCHEME +# variable on Win32 to allow the user to control the +# library linked against. The Pthreads-win32 port +# provides the ability to link against a version of the +# library with exception handling. IT IS NOT RECOMMENDED +# THAT YOU CHANGE PTHREADS_EXCEPTION_SCHEME TO ANYTHING OTHER THAN +# "C" because most POSIX thread implementations do not support stack +# unwinding. +# +# PTHREADS_EXCEPTION_SCHEME +# C = no exceptions (default) +# (NOTE: This is the default scheme on most POSIX thread +# implementations and what you should probably be using) +# CE = C++ Exception Handling +# SE = Structure Exception Handling (MSVC only) +# + +# +# Define a default exception scheme to link against +# and validate user choice. +# +IF(NOT DEFINED PTHREADS_EXCEPTION_SCHEME) + # Assign default if needed + SET(PTHREADS_EXCEPTION_SCHEME "C") +ELSE(NOT DEFINED PTHREADS_EXCEPTION_SCHEME) + # Validate + IF(NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "C" AND + NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "CE" AND + NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "SE") + + MESSAGE(FATAL_ERROR "See documentation for FindPthreads.cmake, only C, CE, and SE modes are allowed") + + ENDIF(NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "C" AND + NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "CE" AND + NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "SE") + + IF(NOT MSVC AND PTHREADS_EXCEPTION_SCHEME STREQUAL "SE") + MESSAGE(FATAL_ERROR "Structured Exception Handling is only allowed for MSVC") + ENDIF(NOT MSVC AND PTHREADS_EXCEPTION_SCHEME STREQUAL "SE") + +ENDIF(NOT DEFINED PTHREADS_EXCEPTION_SCHEME) + +# +# Find the header file +# +FIND_PATH(PTHREADS_INCLUDE_DIR pthread.h) + +# +# Find the library +# +SET(names) +IF(MSVC) + SET(names + pthreadV${PTHREADS_EXCEPTION_SCHEME}2 + pthread + ) +ELSEIF(MINGW) + SET(names + pthreadG${PTHREADS_EXCEPTION_SCHEME}2 + pthread + ) +ELSE(MSVC) # Unix / Cygwin / Apple / Etc. + SET(names pthread) +ENDIF(MSVC) + +FIND_LIBRARY(PTHREADS_LIBRARY ${names} + DOC "The Portable Threads Library") + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Pthreads DEFAULT_MSG + PTHREADS_LIBRARY PTHREADS_INCLUDE_DIR) + +IF(PTHREADS_INCLUDE_DIR AND PTHREADS_LIBRARY) + SET(PTHREADS_DEFINITIONS -DHAVE_PTHREAD_H) + SET(PTHREADS_INCLUDE_DIRS ${PTHREADS_INCLUDE_DIR}) + SET(PTHREADS_LIBRARIES ${PTHREADS_LIBRARY}) +ENDIF(PTHREADS_INCLUDE_DIR AND PTHREADS_LIBRARY) + +MARK_AS_ADVANCED(PTHREADS_INCLUDE_DIR) +MARK_AS_ADVANCED(PTHREADS_LIBRARY) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt new file mode 100644 index 00000000..fab678ba --- /dev/null +++ b/src/core/CMakeLists.txt @@ -0,0 +1,217 @@ +# +# Project name +# +project(argos3core) + +# +# Configuration for CPack's tar.bz2 generator +# +set(CPACK_GENERATOR TBZ2) +set(CPACK_PACKAGE_VERSION_MAJOR "3") +set(CPACK_PACKAGE_VERSION_MINOR "0") +set(CPACK_PACKAGE_VERSION_PATCH "0") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ARGoS3 core package") +include(CPack) + +# +# Include path is points to base source dir +# +include_directories(${CMAKE_SOURCE_DIR}) + +# +# Headers +# +# argos3/core/utility +set(ARGOS3_HEADERS_UTILITY + utility/string_utilities.h) +# argos3/core/utility/configuration +set(ARGOS3_HEADERS_UTILITY_CONFIGURATION + utility/configuration/argos_configuration.h + utility/configuration/argos_exception.h + utility/configuration/base_configurable_resource.h + utility/configuration/command_line_arg_parser.h + utility/configuration/memento.h) +# argos3/core/utility/configuration/tinyxml +set(ARGOS3_HEADERS_UTILITY_CONFIGURATION_TINYXML + utility/configuration/tinyxml/ticpp.h + utility/configuration/tinyxml/ticpprc.h + utility/configuration/tinyxml/tinystr.h + utility/configuration/tinyxml/tinyxml.h) +# argos3/core/utility/datatypes +set(ARGOS3_HEADERS_UTILITY_DATATYPES + utility/datatypes/any.h + utility/datatypes/byte_array.h + utility/datatypes/color.h + utility/datatypes/datatypes.h) +# argos3/core/utility/logging +set(ARGOS3_HEADERS_UTILITY_LOGGING + utility/logging/argos_colored_text.h + utility/logging/argos_log.h) +# argos3/core/utility/plugins +set(ARGOS3_HEADERS_UTILITY_PLUGINS + utility/plugins/factory.h + utility/plugins/dynamic_loading.h + utility/plugins/factory_impl.h) +# argos3/core/utility/profiler +set(ARGOS3_HEADERS_UTILITY_PROFILER + utility/profiler/profiler.h) +# argos3/core/utility/math +set(ARGOS3_HEADERS_UTILITY_MATH + utility/math/angles.h + utility/math/general.h + utility/math/matrix.h + utility/math/quaternion.h + utility/math/range.h + utility/math/ray.h + utility/math/rotationmatrix2.h + utility/math/rotationmatrix3.h + utility/math/segment.h + utility/math/squarematrix.h + utility/math/vector2.h + utility/math/vector3.h + utility/math/rng.h) +# argos3/core/control_interface +set(ARGOS3_HEADERS_CONTROLINTERFACE + control_interface/ci_actuator.h + control_interface/ci_controller.h + control_interface/ci_robot.h + control_interface/ci_sensor.h) +# argos3/core/simulator +set(ARGOS3_HEADERS_SIMULATOR + simulator/simulated_actuator.h + simulator/simulated_sensor.h + simulator/argos_command_line_arg_parser.h + simulator/loop_functions.h + simulator/simulator.h) +# argos3/core/simulator/entity +set(ARGOS3_HEADERS_SIMULATOR_ENTITY + simulator/entity/composable_entity.h + simulator/entity/controllable_entity.h + simulator/entity/embodied_entity.h + simulator/entity/entity.h + simulator/entity/floor_entity.h + simulator/entity/medium_entity.h + simulator/entity/positional_entity.h + simulator/entity/wifi_medium_entity.h) +# argos3/core/simulator/physics_engine +set(ARGOS3_HEADERS_SIMULATOR_PHYSICSENGINE + simulator/physics_engine/physics_engine.h + simulator/physics_engine/physics_engine_entity.h) +# argos3/core/simulator/visualization +set(ARGOS3_HEADERS_SIMULATOR_VISUALIZATION + simulator/visualization/visualization.h) +# argos3/core/simulator/space +set(ARGOS3_HEADERS_SIMULATOR_SPACE + simulator/space/space.h + simulator/space/space_hash.h + simulator/space/space_hash_native.h + simulator/space/space_multi_thread_h_dispatch.h + simulator/space/space_multi_thread_scatter_gather.h + simulator/space/space_no_threads.h) + +# +# Source files +# +set(ARGOS3_SOURCES + ${ARGOS3_HEADERS_UTILITY} + utility/string_utilities.cpp + ${ARGOS3_HEADERS_UTILITY_CONFIGURATION} + utility/configuration/command_line_arg_parser.cpp + ${ARGOS3_HEADERS_UTILITY_CONFIGURATION_TINYXML} + utility/configuration/tinyxml/ticpp.cpp + utility/configuration/tinyxml/tinystr.cpp + utility/configuration/tinyxml/tinyxml.cpp + utility/configuration/tinyxml/tinyxmlerror.cpp + utility/configuration/tinyxml/tinyxmlparser.cpp + ${ARGOS3_HEADERS_UTILITY_DATATYPES} + utility/datatypes/byte_array.cpp + utility/datatypes/color.cpp + ${ARGOS3_HEADERS_UTILITY_LOGGING} + utility/logging/argos_log.cpp + ${ARGOS3_HEADERS_UTILITY_PLUGINS} + ${ARGOS3_HEADERS_UTILITY_PROFILER} + utility/profiler/profiler.cpp + ${ARGOS3_HEADERS_UTILITY_MATH} + utility/math/angles.cpp + utility/math/rotationmatrix2.cpp + utility/math/rotationmatrix3.cpp + utility/math/segment.cpp + utility/math/vector2.cpp + utility/math/vector3.cpp + utility/math/rng.cpp + ${ARGOS3_HEADERS_CONTROLINTERFACE} + control_interface/ci_robot.cpp + ${ARGOS3_HEADERS_SIMULATOR} + simulator/argos_command_line_arg_parser.cpp + simulator/loop_functions.cpp + simulator/simulator.cpp + ${ARGOS3_HEADERS_SIMULATOR_ENTITY} + simulator/entity/controllable_entity.cpp + simulator/entity/embodied_entity.cpp + simulator/entity/entity.cpp + simulator/entity/floor_entity.cpp + simulator/entity/positional_entity.cpp + ${ARGOS3_HEADERS_SIMULATOR_PHYSICSENGINE} + simulator/physics_engine/physics_engine.cpp + ${ARGOS3_HEADERS_SIMULATOR_VISUALIZATION} + simulator/visualization/visualization.cpp + ${ARGOS3_HEADERS_SIMULATOR_SPACE} + simulator/space/space.cpp + simulator/space/space_multi_thread_h_dispatch.cpp + simulator/space/space_multi_thread_scatter_gather.cpp + simulator/space/space_no_threads.cpp) + +# Compile dynamic library loading only if enabled +if(ARGOS3_DYNAMIC_LIBRARY_LOADING) + set(ARGOS3_SOURCES ${ARGOS3_SOURCES} utility/plugins/dynamic_loading.cpp) +endif(ARGOS3_DYNAMIC_LIBRARY_LOADING) + +# +# Create core ARGoS3 library +# +add_library(argos3core SHARED ${ARGOS3_SOURCES}) +set_target_properties(argos3core PROPERTIES VERSION 3) +if(ARGOS3_DYNAMIC_LIBRARY_LOADING) + target_link_libraries(argos3core dl) +endif(ARGOS3_DYNAMIC_LIBRARY_LOADING) +if(GSL_FOUND) + target_link_libraries(argos3core ${GSL_LIBRARIES}) +endif(GSL_FOUND) + + +# +# Core library installation +# +install(FILES ${ARGOS3_HEADERS_UTILITY} DESTINATION include/argos3/core/utility) +install(FILES ${ARGOS3_HEADERS_UTILITY_CONFIGURATION} DESTINATION include/argos3/core/utility/configuration) +install(FILES ${ARGOS3_HEADERS_UTILITY_CONFIGURATION_TINYXML} DESTINATION include/argos3/core/utility/configuration/tinyxml) +install(FILES ${ARGOS3_HEADERS_UTILITY_DATATYPES} DESTINATION include/argos3/core/utility/datatypes) +install(FILES ${ARGOS3_HEADERS_UTILITY_LOGGING} DESTINATION include/argos3/core/utility/logging) +install(FILES ${ARGOS3_HEADERS_UTILITY_PLUGINS} DESTINATION include/argos3/core/utility/plugins) +install(FILES ${ARGOS3_HEADERS_UTILITY_PROFILER} DESTINATION include/argos3/core/utility/profiler) +install(FILES ${ARGOS3_HEADERS_UTILITY_MATH} DESTINATION include/argos3/core/utility/math) +install(FILES ${ARGOS3_HEADERS_CONTROLINTERFACE} DESTINATION include/argos3/core/control_interface) +install(FILES ${ARGOS3_HEADERS_SIMULATOR} DESTINATION include/argos3/core/simulator) +install(FILES ${ARGOS3_HEADERS_SIMULATOR_ENTITY} DESTINATION include/argos3/core/simulator/entity) +install(FILES ${ARGOS3_HEADERS_SIMULATOR_PHYSICSENGINE} DESTINATION include/argos3/core/simulator/physics_engine) +install(FILES ${ARGOS3_HEADERS_SIMULATOR_VISUALIZATION} DESTINATION include/argos3/core/simulator/visualization) +install(FILES ${ARGOS3_HEADERS_SIMULATOR_SPACE} DESTINATION include/argos3/core/simulator/space) +install(TARGETS argos3core + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib/argos3 + ARCHIVE DESTINATION lib/argos3) + +if(ARGOS3_BUILD_FOR STREQUAL "SIMULATOR") + # + # Create core ARGoS3 executable + # + add_executable(argos3 simulator/main.cpp) + target_link_libraries(argos3 argos3core) + # + # Core ARGoS3 installation + # + install(TARGETS argos3 + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib/argos3 + ARCHIVE DESTINATION lib/argos3) +endif(ARGOS3_BUILD_FOR STREQUAL "SIMULATOR") diff --git a/src/core/control_interface/ci_actuator.h b/src/core/control_interface/ci_actuator.h new file mode 100644 index 00000000..a3333f6c --- /dev/null +++ b/src/core/control_interface/ci_actuator.h @@ -0,0 +1,39 @@ +/** + * @file argos3/core/control_interface/ci_actuator.h + * + * @brief This file provides the basic interface for all actuators. + * + * @author Carlo Pinciroli - + */ + +#ifndef CI_ACTUATOR_H +#define CI_ACTUATOR_H + +namespace argos { + class CCI_Actuator; +} + +#include +#include +#include + +#include + +namespace argos { + + class CCI_Actuator : public CBaseConfigurableResource, + public CMemento { + + public: + + typedef std::map > TMap; + + public: + + virtual ~CCI_Actuator() = 0; + + }; + +} + +#endif diff --git a/src/core/control_interface/ci_controller.h b/src/core/control_interface/ci_controller.h new file mode 100644 index 00000000..be3a06ac --- /dev/null +++ b/src/core/control_interface/ci_controller.h @@ -0,0 +1,97 @@ +/* + * 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; version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @file argos3/core/control_interface/ci_controller.h + * + * @brief This file provides the definition of + * + * @author Carlo Pinciroli - + */ + +#ifndef CCI_CONTROLLER_H +#define CCI_CONTROLLER_H + +namespace argos { + class CCI_Controller; +} + +#include +#include +#include +#include + +namespace argos { + + class CCI_Controller : public CBaseConfigurableResource, + public CMemento { + + public: + + virtual ~CCI_Controller() { + if (m_pcRobot != NULL) delete m_pcRobot; + } + + virtual void Init(TConfigurationNode& t_node) {} + + virtual void ControlStep() {} + + virtual void Reset() {} + + virtual void Destroy() {} + + inline CCI_Robot& GetRobot() { + return *m_pcRobot; + } + + inline void SetRobot(CCI_Robot& pc_robot) { + m_pcRobot = &pc_robot; + } + + inline const std::string& GetControllerId() const { + return m_strControllerId; + } + + inline void SetControllerId(const std::string& str_controller_id) { + m_strControllerId = str_controller_id; + } + + virtual bool IsControllerFinished() const { + return false; + } + + virtual void SaveState(CByteArray& c_buffer) {} + virtual void LoadState(CByteArray& c_buffer) {} + + private: + + CCI_Robot* m_pcRobot; + std::string m_strControllerId; + + }; + +} + +#define REGISTER_CONTROLLER(CLASSNAME, LABEL) \ + REGISTER_SYMBOL(CCI_Controller, \ + CLASSNAME, \ + LABEL, \ + "undefined", \ + "undefined", \ + "undefined", \ + "undefined", \ + "undefined") + +#endif diff --git a/src/core/control_interface/ci_robot.cpp b/src/core/control_interface/ci_robot.cpp new file mode 100644 index 00000000..a8e37cf7 --- /dev/null +++ b/src/core/control_interface/ci_robot.cpp @@ -0,0 +1,53 @@ +/** + * @file argos3/core/control_interface/ci_robot.h + * + * @author Carlo Pinciroli - + */ + +#include "ci_robot.h" + +namespace argos { + + /****************************************/ + /****************************************/ + + CCI_Robot::~CCI_Robot() { + /* Delete actuators*/ + for(CCI_Actuator::TMap::iterator itActuators = m_mapActuators.begin(); + itActuators != m_mapActuators.end(); + ++itActuators) { + delete itActuators->second; + } + m_mapActuators.clear(); + + /* Delete sensors */ + for(CCI_Sensor::TMap::iterator itSensors = m_mapSensors.begin(); + itSensors != m_mapSensors.end(); + ++itSensors) { + delete itSensors->second; + } + m_mapSensors.clear(); + } + + /****************************************/ + /****************************************/ + + CCI_Actuator* CCI_Robot::GetActuator(const std::string& str_actuator_type) { + CCI_Actuator::TMap::const_iterator it = m_mapActuators.find(str_actuator_type); + if (it != m_mapActuators.end()) return it->second; + THROW_ARGOSEXCEPTION("Unknown actuator type " << str_actuator_type << " requested in controller"); + } + + /****************************************/ + /****************************************/ + + CCI_Sensor* CCI_Robot::GetSensor(const std::string& str_sensor_type) { + CCI_Sensor::TMap::const_iterator it = m_mapSensors.find(str_sensor_type); + if (it != m_mapSensors.end()) return it->second; + THROW_ARGOSEXCEPTION("Unknown sensor type " << str_sensor_type << " requested in controller"); + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/control_interface/ci_robot.h b/src/core/control_interface/ci_robot.h new file mode 100644 index 00000000..e982981f --- /dev/null +++ b/src/core/control_interface/ci_robot.h @@ -0,0 +1,80 @@ +/** + * @file argos3/core/control_interface/ci_robot.h + * + * @brief This file provides the definition of the robot class. + * + * @author Carlo Pinciroli - + */ + +#ifndef CI_ROBOT_H +#define CI_ROBOT_H + +/* To avoid dependency problems when including */ +namespace argos { + class CCI_Robot; +} + +#include +#include + +#include +#include + +namespace argos { + + class CCI_Robot { + + public: + + virtual ~CCI_Robot(); + + virtual CCI_Actuator* InsertActuator(const std::string& str_actuator_type) { + return NULL; + } + + virtual CCI_Sensor* InsertSensor(const std::string& str_sensor_type) { + return NULL; + } + + virtual CCI_Actuator* GetActuator(const std::string& str_actuator_type); + + virtual CCI_Sensor* GetSensor(const std::string& str_sensor_type); + + inline CCI_Actuator::TMap& GetAllActuators() { + return m_mapActuators; + } + + inline CCI_Sensor::TMap& GetAllSensors() { + return m_mapSensors; + } + + inline void AddActuator(const std::string& str_actuator_type, + CCI_Actuator* pc_actuator) { + m_mapActuators[str_actuator_type] = pc_actuator; + } + + inline void AddSensor(const std::string& str_sensor_type, + CCI_Sensor* pc_sensor) { + m_mapSensors[str_sensor_type] = pc_sensor; + } + + inline const std::string& GetRobotId() const { + return m_strRobotId; + } + + inline void SetRobotId(const std::string& str_robot_id) { + m_strRobotId = str_robot_id; + } + + protected: + + CCI_Actuator::TMap m_mapActuators; + CCI_Sensor::TMap m_mapSensors; + + std::string m_strRobotId; + + }; + +} + +#endif diff --git a/src/core/control_interface/ci_sensor.h b/src/core/control_interface/ci_sensor.h new file mode 100644 index 00000000..6ba1c70b --- /dev/null +++ b/src/core/control_interface/ci_sensor.h @@ -0,0 +1,39 @@ +/** + * @file argos3/core/control_interface/ci_sensor.h + * + * @brief This file provides the basic interface for all actuators. + * + * @author Carlo Pinciroli - + */ + +#ifndef CI_SENSOR_H +#define CI_SENSOR_H + +namespace argos { + class CCI_Sensor; +} + +#include +#include +#include + +#include + +namespace argos { + + class CCI_Sensor : public CBaseConfigurableResource, + public CMemento { + + public: + + typedef std::map > TMap; + + public: + + virtual ~CCI_Sensor() = 0; + + }; + +} + +#endif diff --git a/src/core/simulator/argos_command_line_arg_parser.cpp b/src/core/simulator/argos_command_line_arg_parser.cpp new file mode 100644 index 00000000..becf4296 --- /dev/null +++ b/src/core/simulator/argos_command_line_arg_parser.cpp @@ -0,0 +1,167 @@ +/** + * @file argos3/core/simulator/argos_command_line_arg_parser.cpp + * + * @author Carlo Pinciroli - + */ + +#include "argos_command_line_arg_parser.h" +//#include + +namespace argos { + + /****************************************/ + /****************************************/ + + CARGoSCommandLineArgParser::CARGoSCommandLineArgParser(CSimulator& c_simulator) : + m_cSimulator(c_simulator), + m_eAction(ACTION_UNKNOWN), + m_pcInitLogStream(NULL), + m_pcInitLogErrStream(NULL) { + AddFlag( + 'h', + "help", + "display this usage information", + m_bHelpWanted + ); + AddFlag( + 'n', + "no-color", + "do not use colored output [OPTIONAL]", + m_bNonColoredLog + ); + AddArgument( + 'c', + "config-file", + "the experiment XML configuration file", + m_strExperimentConfigFile + ); + AddArgument( + 'q', + "query", + "query the available plugins", + m_strQuery + ); + AddArgument( + 'l', + "log-file", + "output log to file [OPTIONAL]", + m_strLogFileName + ); + AddArgument( + 'e', + "logerr-file", + "output logerr to file [OPTIONAL]", + m_strLogErrFileName + ); + } + + /****************************************/ + /****************************************/ + + CARGoSCommandLineArgParser::~CARGoSCommandLineArgParser() { + if(m_cLogFile.is_open()) { + LOG.GetStream().rdbuf(m_pcInitLogStream); + m_cLogFile.close(); + } + if(m_cLogErrFile.is_open()) { + LOGERR.GetStream().rdbuf(m_pcInitLogErrStream); + m_cLogErrFile.close(); + } + } + + /****************************************/ + /****************************************/ + + void CARGoSCommandLineArgParser::Parse(SInt32 n_argc, + char** ppch_argv) { + CCommandLineArgParser::Parse(n_argc, ppch_argv); + /* Configure LOG/LOGERR coloring */ + if(m_bNonColoredLog) { + LOG.DisableColoredOutput(); + LOGERR.DisableColoredOutput(); + } + + /* Check whether LOG and LOGERR should go to files */ + if(m_strLogFileName != "") { + LOG.DisableColoredOutput(); + m_cLogFile.open(m_strLogFileName.c_str(), std::ios::trunc | std::ios::out); + if(m_cLogFile.fail()) { + THROW_ARGOSEXCEPTION("Error opening file \"" << m_strLogFileName << "\""); + } + m_pcInitLogStream = LOG.GetStream().rdbuf(); + LOG.GetStream().rdbuf(m_cLogFile.rdbuf()); + } + if(m_strLogErrFileName != "") { + LOGERR.DisableColoredOutput(); + m_cLogErrFile.open(m_strLogErrFileName.c_str(), std::ios::trunc | std::ios::out); + if(m_cLogErrFile.fail()) { + THROW_ARGOSEXCEPTION("Error opening file \"" << m_strLogErrFileName << "\""); + } + m_pcInitLogErrStream = LOGERR.GetStream().rdbuf(); + LOGERR.GetStream().rdbuf(m_cLogErrFile.rdbuf()); + } + + /* Check that either -h, -c or -q was passed (strictly one of them) */ + if(m_strExperimentConfigFile == "" && + m_strQuery == "" && + ! m_bHelpWanted) { + THROW_ARGOSEXCEPTION("No --help, --config-file or --query options specified."); + } + if((m_strExperimentConfigFile != "" && m_strQuery != "") || + (m_strExperimentConfigFile != "" && m_bHelpWanted) || + (m_strQuery != "" && m_bHelpWanted)) { + THROW_ARGOSEXCEPTION("Options --help, --config-file and --query are mutually exclusive."); + } + + if(m_strExperimentConfigFile != "") { + m_eAction = ACTION_RUN_EXPERIMENT; + } + + if(m_strQuery != "") { + m_eAction = ACTION_QUERY; + } + + if(m_bHelpWanted) { + m_eAction = ACTION_SHOW_HELP; + } + + } + + /****************************************/ + /****************************************/ + + void CARGoSCommandLineArgParser::PrintUsage(CARGoSLog& c_log) { + c_log << "Usage: argos [OPTIONS]" << std::endl; + c_log << "The ARGOS simulator, the official simulator of the Swarmanoid Project." << std::endl; + c_log << "Current version: 2.0" << std::endl; + c_log << std::endl; + c_log << " -h | --help display this usage information" << std::endl; + c_log << " -c FILE | --config-file FILE the experiment XML configuration file" << std::endl; + c_log << " -q QUERY | --query QUERY query the available plugins." << std::endl; + c_log << " -n | --no-color FILE do not use colored output [OPTIONAL]" << std::endl; + c_log << " -l | --log-file FILE redirect LOG to FILE [OPTIONAL]" << std::endl; + c_log << " -e | --logerr-file FILE redirect LOGERR to FILE [OPTIONAL]" << std::endl << std::endl; + c_log << "The options --config-file and --query are mutually exclusive. Either you use" << std::endl; + c_log << "the first, and thus you run an experiment, or you use the second to query the" << std::endl; + c_log << "plugins." << std::endl << std::endl; + c_log << "EXAMPLES" << std::endl << std::endl; + c_log << "To run an experiment, type:" << std::endl << std::endl; + c_log << " ./argos -c /path/to/myconfig.xml" << std::endl << std::endl; + c_log << "To query the plugins, type:" << std::endl << std::endl; + c_log << " ./argos -q QUERY" << std::endl << std::endl; + c_log << "where QUERY can have the following values:" << std::endl << std::endl; + c_log << " all print a list of all the available plugins" << std::endl; + c_log << " actuators print a list of all the available actuators" << std::endl; + c_log << " sensors print a list of all the available sensors" << std::endl; + c_log << " physics_engines print a list of all the available physics engines" << std::endl; + c_log << " visualizations print a list of all the available visualizations" << std::endl; + c_log << " entities print a list of all the available entities" << std::endl << std::endl; + c_log << "Alternatively, QUERY can be the name of a specific plugin as returned by the" << std::endl; + c_log << "above commands. In this case, you get a complete description of the matching" << std::endl; + c_log << "plugins." << std::endl << std::endl; + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/simulator/argos_command_line_arg_parser.h b/src/core/simulator/argos_command_line_arg_parser.h new file mode 100644 index 00000000..a7708328 --- /dev/null +++ b/src/core/simulator/argos_command_line_arg_parser.h @@ -0,0 +1,80 @@ +/** + * @file argos3/core/simulator/argos_command_line_arg_parser.h + * + * @author Carlo Pinciroli - + */ + +#ifndef ARGOS_COMMAND_LINE_ARG_PARSER_H +#define ARGOS_COMMAND_LINE_ARG_PARSER_H + +namespace argos { + class CARGoSCommandLineArgParser; + class CSimulator; +} + +#include +#include + +namespace argos { + + class CARGoSCommandLineArgParser : public CCommandLineArgParser { + + public: + + enum EAction { + ACTION_UNKNOWN = 0, + ACTION_SHOW_HELP, + ACTION_RUN_EXPERIMENT, + ACTION_QUERY + }; + + public: + + CARGoSCommandLineArgParser(CSimulator& c_simulator); + virtual ~CARGoSCommandLineArgParser(); + + virtual void PrintUsage(CARGoSLog& c_log); + + virtual void Parse(SInt32 n_argc, + char** ppch_argv); + + inline EAction GetAction() { + return m_eAction; + } + + inline const std::string& GetExperimentConfigFile() { + return m_strExperimentConfigFile; + } + + inline const std::string& GetQuery() { + return m_strQuery; + } + + inline bool IsLogColored() { + return ! m_bNonColoredLog; + } + + inline bool IsHelpWanted() { + return m_bHelpWanted; + } + + private: + + CSimulator& m_cSimulator; + EAction m_eAction; + std::string m_strExperimentConfigFile; + std::string m_strQuery; + std::string m_strLogFileName; + std::ofstream m_cLogFile; + std::streambuf* m_pcInitLogStream; + std::string m_strLogErrFileName; + std::ofstream m_cLogErrFile; + std::streambuf* m_pcInitLogErrStream; + bool m_bNonColoredLog; + bool m_bHelpWanted; + + }; + +} + +#endif diff --git a/src/core/simulator/entity/composable_entity.h b/src/core/simulator/entity/composable_entity.h new file mode 100644 index 00000000..5d50567d --- /dev/null +++ b/src/core/simulator/entity/composable_entity.h @@ -0,0 +1,45 @@ +/** + * @file + * + * @brief This file contains the definition of an entity. + * + * This file contains the class definition of an + * entity, that is, the basic class that provides the interface for the + * simulation of all the objects in the environment. + * + * @author Carlo Pinciroli - + */ + +#ifndef COMPOSABLE_ENTITY_H +#define COMPOSABLE_ENTITY_H + +namespace argos { + class CComposableEntity; +} + +#include + +namespace argos { + class CComposableEntity : public CEntity { + + public: + + CComposableEntity(CEntity* pc_parent) : + CEntity(pc_parent) {} + virtual ~CComposableEntity() {} + + virtual CEntity& GetComponent(const std::string& str_component) = 0; + template E& GetComponent(const std::string& str_component) { + return *(dynamic_cast(&GetComponent(str_component))); + } + virtual bool HasComponent(const std::string& str_component) = 0; + + inline virtual std::string GetTypeDescription() const { + return "composable_entity"; + } + + virtual void UpdateComponents() = 0; + }; +} + +#endif diff --git a/src/core/simulator/entity/controllable_entity.cpp b/src/core/simulator/entity/controllable_entity.cpp new file mode 100644 index 00000000..4d6bdce7 --- /dev/null +++ b/src/core/simulator/entity/controllable_entity.cpp @@ -0,0 +1,131 @@ +/** + * @file + * + * @author Carlo Pinciroli - + */ + +#include "controllable_entity.h" +#include +#include + +namespace argos { + + /****************************************/ + /****************************************/ + + CControllableEntity::~CControllableEntity() + { + if(m_pcController != NULL) { + delete m_pcController; + } + } + + /****************************************/ + /****************************************/ + + void CControllableEntity::Init(TConfigurationNode& t_tree) + { + try { + /* Init parent */ + CEntity::Init(t_tree); + /* Get the controller of the controllable */ + GetNodeAttribute(t_tree, "controller", m_strControllerId); + } + catch(CARGoSException& ex) { + THROW_ARGOSEXCEPTION_NESTED("Failed to initialize controllable entity \"" << GetId() << "\".", ex); + } + } + + /****************************************/ + /****************************************/ + + void CControllableEntity::Reset() + { + /* Clear rays */ + m_vecCheckedRays.clear(); + m_vecIntersectionPoints.clear(); + + /* Reset sensors */ + for(std::map::iterator it = m_mapSensors.begin(); + it != m_mapSensors.end(); ++it) { + it->second->Reset(); + } + /* Reset actuators */ + for(std::map::iterator it = m_mapActuators.begin(); + it != m_mapActuators.end(); ++it) { + it->second->Reset(); + } + /* Reset user-defined controller */ + m_pcController->Reset(); + } + + /****************************************/ + /****************************************/ + + void CControllableEntity::Destroy() + { + /* Clear rays */ + m_vecCheckedRays.clear(); + m_vecIntersectionPoints.clear(); + + /* Destroy sensors */ + for(std::map::iterator it = m_mapSensors.begin(); + it != m_mapSensors.end(); ++it) { + it->second->Destroy(); + } + /* Destroy actuators */ + for(std::map::iterator it = m_mapActuators.begin(); + it != m_mapActuators.end(); ++it) { + it->second->Destroy(); + } + /* Destroy user-defined controller */ + if(m_pcController) { + m_pcController->Destroy(); + } + } + + /****************************************/ + /****************************************/ + + void CControllableEntity::SetController(CCI_Controller& pc_controller) { + /* Set the controller */ + m_pcController = &pc_controller; + /* Set the simulated actuator list */ + for(CCI_Actuator::TMap::iterator it = m_pcController->GetRobot().GetAllActuators().begin(); + it != m_pcController->GetRobot().GetAllActuators().end(); ++it) { + m_mapActuators[it->first] = dynamic_cast(it->second); + } + /* Set the simulated sensor list */ + for(CCI_Sensor::TMap::iterator it = m_pcController->GetRobot().GetAllSensors().begin(); + it != m_pcController->GetRobot().GetAllSensors().end(); ++it) { + m_mapSensors[it->first] = dynamic_cast(it->second); + } + } + + /****************************************/ + /****************************************/ + + void CControllableEntity::Sense() { + m_vecCheckedRays.clear(); + m_vecIntersectionPoints.clear(); + + for(std::map::iterator it = m_mapSensors.begin(); + it != m_mapSensors.end(); ++it) { + it->second->Update(); + } + } + + /****************************************/ + /****************************************/ + + void CControllableEntity::Act() { + for(std::map::iterator it = m_mapActuators.begin(); + it != m_mapActuators.end(); ++it) { + it->second->Update(); + } + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/simulator/entity/controllable_entity.h b/src/core/simulator/entity/controllable_entity.h new file mode 100644 index 00000000..8fd7a618 --- /dev/null +++ b/src/core/simulator/entity/controllable_entity.h @@ -0,0 +1,98 @@ +/** + * @file + * + * @author Carlo Pinciroli - + */ + +#ifndef CONTROLLABLE_ENTITY_H +#define CONTROLLABLE_ENTITY_H + +namespace argos { + class CControllableEntity; + class CSimulatedActuator; + class CSimulatedSensor; +} + +#include +#include +#include + +namespace argos { + + class CControllableEntity : public CEntity { + + public: + CControllableEntity(CEntity* pc_parent) : + CEntity(pc_parent), + m_pcController(NULL) {} + virtual ~CControllableEntity(); + + virtual void Init(TConfigurationNode& t_tree); + virtual void Reset(); + virtual void Destroy(); + + inline const std::string& GetControllerId() const { + return m_strControllerId; + } + + inline CCI_Controller& GetController() { + return *m_pcController; + } + void SetController(CCI_Controller& pc_controller); + + virtual void Sense(); + + inline void ControlStep() { + if(m_pcController != NULL) { + m_pcController->ControlStep(); + } + else { + THROW_ARGOSEXCEPTION("Entity " << GetId() << " does not have any controller associated. Did you add it to the section?"); + } + } + + virtual void Act(); + + inline virtual std::string GetTypeDescription() const { + return "controllable_entity"; + } + + inline void AddCheckedRay(bool b_obstructed, + const CRay& c_ray) { + m_vecCheckedRays.push_back(std::make_pair(b_obstructed, c_ray)); + } + + inline void AddIntersectionPoint(const CRay& c_ray, + Real f_t_on_ray) { + CVector3 cPoint; + c_ray.GetPoint(cPoint, f_t_on_ray); + m_vecIntersectionPoints.push_back(cPoint); + } + + inline std::vector< std::pair >& GetCheckedRays() { + return m_vecCheckedRays; + } + + inline std::vector< CVector3 >& GetIntersectionPoints() { + return m_vecIntersectionPoints; + } + + protected: + + std::string m_strControllerId; + CCI_Controller* m_pcController; + + std::map m_mapActuators; + std::map m_mapSensors; + + std::vector< std::pair > m_vecCheckedRays; + std::vector< CVector3 > m_vecIntersectionPoints; + + }; + + typedef std::vector TControllableEntityVector; + typedef std::map TControllableEntityMap; + +} + +#endif diff --git a/src/core/simulator/entity/embodied_entity.cpp b/src/core/simulator/entity/embodied_entity.cpp new file mode 100644 index 00000000..0f2b6f06 --- /dev/null +++ b/src/core/simulator/entity/embodied_entity.cpp @@ -0,0 +1,131 @@ +#include "embodied_entity.h" +#include "composable_entity.h" +#include +#include +#include +#include + +namespace argos { + + /****************************************/ + /****************************************/ + + void CEmbodiedEntity::Init(TConfigurationNode& t_tree) { + CPositionalEntity::Init(t_tree); + m_bBoundingBoxRecalculationNeeded = true; + m_bMovable = true; + } + + /****************************************/ + /****************************************/ + + void CEmbodiedEntity::Reset() { + CPositionalEntity::Reset(); + + /* Reset collision data */ + ClearCollisionDetected(); + m_unNumCollisions = 0; + m_bBoundingBoxRecalculationNeeded = true; + } + + /****************************************/ + /****************************************/ + + bool CEmbodiedEntity::CheckIntersectionWithRay(Real& f_distance, + const CRay& c_ray) const { + for(UInt32 i = 0; i < m_tPhysicsEngineEntityVector.size(); ++i) + if(m_tPhysicsEngineEntityVector[i]->CheckIntersectionWithRay(f_distance, c_ray)) { + return true; + } + return false; + } + + /****************************************/ + /****************************************/ + + bool CEmbodiedEntity::MoveTo(const CVector3& c_position, + const CQuaternion& c_orientation, + bool b_check_only) { + bool bNoCollision = true; + for(CPhysicsEngineEntity::TVector::const_iterator it = m_tPhysicsEngineEntityVector.begin(); + it != m_tPhysicsEngineEntityVector.end() && bNoCollision; ++it) { + if(! (*it)->MoveTo(c_position, c_orientation, b_check_only)) { + bNoCollision = false; + } + } + if(bNoCollision && !b_check_only) { + /* Update space position */ + SetPosition(c_position); + SetOrientation(c_orientation); + if( HasParent() ) { + CComposableEntity* pcEntity = dynamic_cast(&GetParent()); + if( pcEntity != NULL ) { + pcEntity->UpdateComponents(); + } + } + return true; + } + else { + /* No Collision or check only, undo changes */ + for(CPhysicsEngineEntity::TVector::const_iterator it = m_tPhysicsEngineEntityVector.begin(); + it != m_tPhysicsEngineEntityVector.end(); ++it) { + (*it)->MoveTo(GetPosition(), GetOrientation()); + } + if(!bNoCollision) { + /* Collision */ + return false; + } + else { + /* No collision, it was a check-only */ + return true; + } + } + } + + /****************************************/ + /****************************************/ + + void CalculateBoundingBoxFromHalfSize(CEmbodiedEntity::SBoundingBox& s_bounding_box, + const CVector3& c_half_size, + const CVector3& c_center_pos, + const CRotationMatrix3& c_orientation) { + CVector3 cExtent(Abs(c_orientation(0) * c_half_size.GetX()) + + Abs(c_orientation(1) * c_half_size.GetY()) + + Abs(c_orientation(2) * c_half_size.GetZ()), + Abs(c_orientation(3) * c_half_size.GetX()) + + Abs(c_orientation(4) * c_half_size.GetY()) + + Abs(c_orientation(5) * c_half_size.GetZ()), + Abs(c_orientation(6) * c_half_size.GetX()) + + Abs(c_orientation(7) * c_half_size.GetY()) + + Abs(c_orientation(8) * c_half_size.GetZ())); + s_bounding_box.MinCorner = c_center_pos; + s_bounding_box.MinCorner -= cExtent; + s_bounding_box.MaxCorner = c_center_pos; + s_bounding_box.MaxCorner += cExtent; + } + + /****************************************/ + /****************************************/ + + void CEmbodiedEntitySpaceHashUpdater::operator()(CAbstractSpaceHash& c_space_hash, + CEmbodiedEntity& c_element) { + /* Update the bounding box */ + c_element.UpdateBoundingBox(); + /* Translate the min corner of the bounding box into the map's coordinate */ + c_space_hash.SpaceToHashTable(m_nMinX, m_nMinY, m_nMinZ, c_element.GetBoundingBox().MinCorner); + /* Translate the max corner of the bounding box into the map's coordinate */ + c_space_hash.SpaceToHashTable(m_nMaxX, m_nMaxY, m_nMaxZ, c_element.GetBoundingBox().MaxCorner); + /* Finally, go through the affected cells and update them */ + for(SInt32 nK = m_nMinZ; nK <= m_nMaxZ; ++nK) { + for(SInt32 nJ = m_nMinY; nJ <= m_nMaxY; ++nJ) { + for(SInt32 nI = m_nMinX; nI <= m_nMaxX; ++nI) { + c_space_hash.UpdateCell(nI, nJ, nK, c_element); + } + } + } + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/simulator/entity/embodied_entity.h b/src/core/simulator/entity/embodied_entity.h new file mode 100644 index 00000000..4f17493c --- /dev/null +++ b/src/core/simulator/entity/embodied_entity.h @@ -0,0 +1,220 @@ +/** + * @file + * + * @author Carlo Pinciroli - + */ + +#ifndef EMBODIED_ENTITY_H +#define EMBODIED_ENTITY_H + +namespace argos { + class CEmbodiedEntity; + class CRotationMatrix3; +} + +#include +#include +#include +#include +#include +#include +#include + +namespace argos { + + class CEmbodiedEntity : public CPositionalEntity { + + public: + + struct SBoundingBox { + CVector3 MinCorner; + CVector3 MaxCorner; + + inline bool Intersects(const SBoundingBox& s_bb) const { + return + (MinCorner.GetX() < s_bb.MaxCorner.GetX()) && (MaxCorner.GetX() > s_bb.MinCorner.GetX()) && + (MinCorner.GetY() < s_bb.MaxCorner.GetY()) && (MaxCorner.GetY() > s_bb.MinCorner.GetY()) && + (MinCorner.GetZ() < s_bb.MaxCorner.GetZ()) && (MaxCorner.GetZ() > s_bb.MinCorner.GetZ()); + } + }; + + public: + + CEmbodiedEntity(CEntity* pc_parent) : + CPositionalEntity(pc_parent), + m_bCollisionDetected(false), + m_unNumCollisions(0), + m_pcCollidingEntity(NULL) {} + virtual ~CEmbodiedEntity() {} + + virtual void Init(TConfigurationNode& t_tree); + virtual void Reset(); + + inline virtual bool IsCollisionDetected() const { + return m_bCollisionDetected; + } + + inline virtual void SetCollisionDetected(CEmbodiedEntity& c_entity) { + m_pcCollidingEntity = &c_entity; + m_bCollisionDetected = true; + ++m_unNumCollisions; + } + + inline virtual void ClearCollisionDetected() { + m_bCollisionDetected = false; + m_pcCollidingEntity = NULL; + } + + inline bool IsMovable() const { + return m_bMovable; + } + + inline void SetMovable(bool b_movable) { + m_bMovable = b_movable; + } + + inline virtual UInt32 GetCollisionNumber() { + return m_unNumCollisions; + } + + inline virtual CEmbodiedEntity& GetCollidingEntity() { + return *m_pcCollidingEntity; + } + + inline virtual void SetPosition(const CVector3& c_position) { + CPositionalEntity::SetPosition(c_position); + m_bBoundingBoxRecalculationNeeded = true; + } + + inline virtual void SetOrientation(const CQuaternion& c_orientation) { + CPositionalEntity::SetOrientation(c_orientation); + m_bBoundingBoxRecalculationNeeded = true; + } + + inline virtual void UpdateBoundingBox() { + if(m_bBoundingBoxRecalculationNeeded) { + CalculateBoundingBox(); + m_bBoundingBoxRecalculationNeeded = false; + } + } + + inline SBoundingBox& GetBoundingBox() { + return m_sBoundingBox; + } + + inline virtual void AddPhysicsEngine(CPhysicsEngine& c_physics_engine) { + m_tEngines.push_back(&c_physics_engine); + } + + inline virtual void RemovePhysicsEngine(CPhysicsEngine& c_physics_engine) { + CPhysicsEngine::TVector::iterator it = std::find(m_tEngines.begin(), + m_tEngines.end(), + &c_physics_engine); + if(it == m_tEngines.end()) { + THROW_ARGOSEXCEPTION("Engine \"" << c_physics_engine.GetId() << "\" not found when removing it from entity id = \"" << GetId() << "\""); + } + m_tEngines.erase(it); + } + + inline virtual CPhysicsEngine& GetPhysicsEngine(UInt32 un_index) const { + if(un_index >= m_tEngines.size()) { + THROW_ARGOSEXCEPTION("Index out of bound for physics engine query for entity " << GetId() << + ". Passed index = " << un_index << ", but " << + m_tEngines.size() << " engines were associated to this entity."); + } + return *(m_tEngines[un_index]); + } + + inline virtual UInt32 GetPhysicsEngineNum() const { + return m_tEngines.size(); + } + + inline virtual void AddPhysicsEngineEntity(const std::string& str_engine_id, + CPhysicsEngineEntity& c_physics_entity) { + m_tPhysicsEngineEntityMap[str_engine_id] = &c_physics_entity; + m_tPhysicsEngineEntityVector.push_back(&c_physics_entity); + } + + inline virtual void RemovePhysicsEngineEntity(const std::string& str_engine_id) { + CPhysicsEngineEntity::TMap::iterator itMap = m_tPhysicsEngineEntityMap.find(str_engine_id); + if(itMap == m_tPhysicsEngineEntityMap.end()) { + THROW_ARGOSEXCEPTION("Entity \"" << GetId() << "\" has no associated entity in physics engine " << str_engine_id); + } + CPhysicsEngineEntity::TVector::iterator itVec = std::find(m_tPhysicsEngineEntityVector.begin(), + m_tPhysicsEngineEntityVector.end(), + itMap->second); + m_tPhysicsEngineEntityMap.erase(itMap); + m_tPhysicsEngineEntityVector.erase(itVec); + } + + inline virtual const CPhysicsEngineEntity& GetPhysicsEngineEntity(const std::string& str_engine_id) const { + CPhysicsEngineEntity::TMap::const_iterator it = m_tPhysicsEngineEntityMap.find(str_engine_id); + if(it == m_tPhysicsEngineEntityMap.end()) { + THROW_ARGOSEXCEPTION("Entity \"" << GetId() << "\" has no associated entity in physics engine \"" << str_engine_id << "\""); + } + return *(it->second); + } + + virtual bool CheckIntersectionWithRay(Real& f_distance, + const CRay& c_ray) const; + + virtual bool MoveTo(const CVector3& c_position, + const CQuaternion& c_orientation, + bool b_check_only = false); + + protected: + + virtual void CalculateBoundingBox() = 0; + + protected: + + CPhysicsEngine::TVector m_tEngines; + CPhysicsEngineEntity::TMap m_tPhysicsEngineEntityMap; + CPhysicsEngineEntity::TVector m_tPhysicsEngineEntityVector; + bool m_bCollisionDetected; + UInt32 m_unNumCollisions; + CEmbodiedEntity* m_pcCollidingEntity; + SBoundingBox m_sBoundingBox; + bool m_bBoundingBoxRecalculationNeeded; + bool m_bMovable; + + }; + + /****************************************/ + /****************************************/ + + typedef std::vector TEmbodiedEntityVector; + typedef std::map TEmbodiedEntityMap; + typedef std::tr1::unordered_set TEmbodiedEntitySet; + + /****************************************/ + /****************************************/ + + void CalculateBoundingBoxFromHalfSize(CEmbodiedEntity::SBoundingBox& s_bounding_box, + const CVector3& c_half_size, + const CVector3& c_center_pos, + const CRotationMatrix3& c_orientation); + + /****************************************/ + /****************************************/ + + class CEmbodiedEntitySpaceHashUpdater : public CSpaceHashUpdater { + + public: + + virtual void operator()(CAbstractSpaceHash& c_space_hash, + CEmbodiedEntity& c_element); + + private: + + SInt32 m_nMinX, m_nMinY, m_nMinZ; + SInt32 m_nMaxX, m_nMaxY, m_nMaxZ; + + }; + + /****************************************/ + /****************************************/ + +} + +#endif diff --git a/src/core/simulator/entity/entity.cpp b/src/core/simulator/entity/entity.cpp new file mode 100644 index 00000000..d761a577 --- /dev/null +++ b/src/core/simulator/entity/entity.cpp @@ -0,0 +1,22 @@ +#include "entity.h" + +namespace argos { + + /****************************************/ + /****************************************/ + + void CEntity::Init(TConfigurationNode& t_tree) + { + try { + /* Get the id of the entity */ + GetNodeAttribute(t_tree, "id", m_strId); + } + catch(CARGoSException& ex) { + THROW_ARGOSEXCEPTION_NESTED("Failed to initialize an entity.", ex); + } + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/simulator/entity/entity.h b/src/core/simulator/entity/entity.h new file mode 100644 index 00000000..dcf37535 --- /dev/null +++ b/src/core/simulator/entity/entity.h @@ -0,0 +1,94 @@ +/** + * @file + * + * @brief This file contains the definition of an entity. + * + * This file contains the class definition of an + * entity, that is, the basic class that provides the interface for the + * simulation of all the objects in the environment. + * + * @author Carlo Pinciroli - + */ + +#ifndef ENTITY_H +#define ENTITY_H + +namespace argos { + class CEntity; + class CPhysicsEngine; + class CSpace; +} + +#include +#include +#include + +#include +#include +#include + +namespace argos { + + class CEntity : public CBaseConfigurableResource { + + public: + + CEntity(CEntity* pc_parent) : + m_pcParent(pc_parent) {} + virtual ~CEntity() {} + + virtual void Init(TConfigurationNode& t_tree); + virtual void Reset() {} + virtual void Destroy() {} + + inline virtual const std::string& GetId() const { + return m_strId; + } + + inline virtual void SetId(const std::string& str_id) { + m_strId = str_id; + } + + inline bool HasParent() { + return (m_pcParent != NULL); + } + + inline CEntity& GetParent() { + return *m_pcParent; + } + + inline void SetParent(CEntity& c_parent) { + m_pcParent = &c_parent; + } + + virtual std::string GetTypeDescription() const = 0; + + private: + + std::string m_strId; + CEntity* m_pcParent; + + }; + + typedef std::vector TEntityVector; + typedef std::map TEntityMap; + +} + +#define REGISTER_ENTITY(CLASSNAME, \ + LABEL, \ + AUTHOR, \ + VERSION, \ + BRIEF_DESCRIPTION, \ + LONG_DESCRIPTION, \ + STATUS) \ + REGISTER_SYMBOL(CEntity, \ + CLASSNAME, \ + LABEL, \ + AUTHOR, \ + VERSION, \ + BRIEF_DESCRIPTION, \ + LONG_DESCRIPTION, \ + STATUS) + +#endif diff --git a/src/core/simulator/entity/floor_entity.cpp b/src/core/simulator/entity/floor_entity.cpp new file mode 100644 index 00000000..046bc312 --- /dev/null +++ b/src/core/simulator/entity/floor_entity.cpp @@ -0,0 +1,268 @@ +/** + * @file + * + * @author Carlo Pinciroli - + */ + +#include "floor_entity.h" +#include +#include +#include +#include + +namespace argos { + + /****************************************/ + /****************************************/ + + class CFloorColorFromImageFile : public CFloorEntity::CFloorColorSource { + + public: + + CFloorColorFromImageFile(const std::string& str_file_name, + Real f_arena_width, + Real f_arena_height) : + m_cHalfArenaSize(f_arena_width * 0.5f, + f_arena_height * 0.5f) { + if(!m_cImage.load(str_file_name.c_str())) { + THROW_ARGOSEXCEPTION("Could not load image \"" << + str_file_name << + "\""); + } + m_fArenaToImageCoordinateXFactor = m_cImage.getWidth() / f_arena_width; + m_fArenaToImageCoordinateYFactor = m_cImage.getHeight() / f_arena_height; + } + + virtual ~CFloorColorFromImageFile() {} + + virtual CColor GetColorAtPoint(Real f_x, + Real f_y) { + /* Compute coordinates on the image */ + UInt32 x = (f_x + m_cHalfArenaSize.GetX()) * m_fArenaToImageCoordinateXFactor; + UInt32 y = (f_y + m_cHalfArenaSize.GetY()) * m_fArenaToImageCoordinateYFactor; + /* Check the bit depth */ + if(m_cImage.getBitsPerPixel() <= 8) { + RGBQUAD* ptColorPalette; + BYTE tPixelIndex; + /* 1, 4 or 8 bits per pixel */ + if(! m_cImage.getPixelIndex(x, y, &tPixelIndex)) { + THROW_ARGOSEXCEPTION("Unable to access image pixel at (" << x << "," << y << + "). Image size (" << m_cImage.getWidth() << "," << + m_cImage.getHeight() << ")"); + } + ptColorPalette = m_cImage.getPalette(); + return CColor(ptColorPalette[tPixelIndex].rgbRed, + ptColorPalette[tPixelIndex].rgbGreen, + ptColorPalette[tPixelIndex].rgbBlue); + } + else { + /* 16, 24 or 32 bits per pixel */ + RGBQUAD tColorPixel; + if(! m_cImage.getPixelColor(x, y, &tColorPixel)) { + THROW_ARGOSEXCEPTION("Unable to access image pixel at (" << x << "," << y << + "). Image size (" << m_cImage.getWidth() << "," << + m_cImage.getHeight() << ")"); + } + return CColor(tColorPixel.rgbRed, + tColorPixel.rgbGreen, + tColorPixel.rgbBlue); + } + } + + virtual void SaveAsImage(const std::string& str_path) { + m_strImageFileName = str_path; + m_cImage.save(str_path.c_str()); + } + + virtual std::string GetImageFileName() const { + return m_strImageFileName; + } + + private: + + fipImage m_cImage; + Real m_fArenaToImageCoordinateXFactor; + Real m_fArenaToImageCoordinateYFactor; + CVector2 m_cHalfArenaSize; + std::string m_strImageFileName; + + }; + + /****************************************/ + /****************************************/ + + class CFloorColorFromLoopFunctions : public CFloorEntity::CFloorColorSource { + + public: + + CFloorColorFromLoopFunctions(UInt32 un_pixels_per_meter, + Real f_arena_width, + Real f_arena_height) : + m_cLoopFunctions(CSimulator::GetInstance().GetLoopFunctions()), + m_unPixelsPerMeter(un_pixels_per_meter), + m_cHalfArenaSize(f_arena_width * 0.5f, + f_arena_height * 0.5f) { + } + + virtual ~CFloorColorFromLoopFunctions() { + } + + virtual CColor GetColorAtPoint(Real f_x, + Real f_y) { + return m_cLoopFunctions.GetFloorColor(CVector2(f_x, f_y)); + } + + virtual void SaveAsImage(const std::string& str_path) { + fipImage cImage(FIT_BITMAP, m_unPixelsPerMeter * m_cHalfArenaSize.GetX()*2, m_unPixelsPerMeter * m_cHalfArenaSize.GetY()*2, 24); + Real fFactor = 1.0f / static_cast(m_unPixelsPerMeter); + CVector2 cFloorPos; + CColor cARGoSPixel; + RGBQUAD tFIPPixel; + for(UInt32 y = 0; y < cImage.getHeight(); ++y) { + for(UInt32 x = 0; x < cImage.getWidth(); ++x) { + cFloorPos.Set(x * fFactor, y * fFactor); + cFloorPos -= m_cHalfArenaSize; + cARGoSPixel = m_cLoopFunctions.GetFloorColor(cFloorPos); + tFIPPixel.rgbRed = cARGoSPixel.GetRed(); + tFIPPixel.rgbGreen = cARGoSPixel.GetGreen(); + tFIPPixel.rgbBlue = cARGoSPixel.GetBlue(); + cImage.setPixelColor(x, y, &tFIPPixel); + } + } + if(!cImage.save(str_path.c_str())) { + THROW_ARGOSEXCEPTION("Cannot save image \"" << str_path << "\" for floor entity."); + } + } + + private: + + CLoopFunctions& m_cLoopFunctions; + UInt32 m_unPixelsPerMeter; + CVector2 m_cHalfArenaSize; + }; + +/****************************************/ +/****************************************/ + + CFloorEntity::CFloorEntity() : + CEntity(NULL), + m_pcColorSource(NULL), + m_bHasChanged(true) {} + +/****************************************/ +/****************************************/ + + CFloorEntity::~CFloorEntity() { + if(m_pcColorSource != NULL) { + delete m_pcColorSource; + } + } + +/****************************************/ +/****************************************/ + + void CFloorEntity::Init(TConfigurationNode& t_tree) { + /* Init parent */ + CEntity::Init(t_tree); + /* Get arena size */ + m_cFloorSize = CSimulator::GetInstance().GetSpace().GetArenaSize(); + /* Parse XML */ + GetNodeAttribute(t_tree, "source", m_strColorSource); + if(m_strColorSource == "image") { + std::string strPath; + GetNodeAttribute(t_tree, "path", strPath); + strPath = ExpandARGoSInstallDir(strPath); + m_pcColorSource = new CFloorColorFromImageFile(strPath, + m_cFloorSize.GetX(), + m_cFloorSize.GetY()); + } + else if(m_strColorSource == "loop_functions") { + GetNodeAttribute(t_tree, "pixels_per_meter", m_unPixelsPerMeter); + m_pcColorSource = new CFloorColorFromLoopFunctions(m_unPixelsPerMeter, + m_cFloorSize.GetX(), + m_cFloorSize.GetY()); + } + else { + THROW_ARGOSEXCEPTION("Unknown image source \"" << + m_strColorSource << + "\" for the floor entity \"" << + GetId() << + "\""); + } + } + +/****************************************/ +/****************************************/ + + void CFloorEntity::Reset() { + /* Reset parent */ + CEntity::Reset(); + + /* Recompute the color source from the loop function */ + if(m_strColorSource == "loop_functions") { + delete m_pcColorSource; + m_pcColorSource = new CFloorColorFromLoopFunctions(m_unPixelsPerMeter, + m_cFloorSize.GetX(), + m_cFloorSize.GetY()); + } + } + +/****************************************/ +/****************************************/ + + REGISTER_ENTITY(CFloorEntity, + "floor", + "Carlo Pinciroli [cpinciro@ulb.ac.be]", + "1.0", + "It contains the properties of the arena floor.", + "The floor entity contains the properties of the arena floor. In the current\n" + "implementation, it contains only the color of the floor. The floor color is\n" + "detected by the robots' ground sensors.\n\n" + "REQUIRED XML CONFIGURATION\n\n" + " \n" + " ...\n" + " \n" + " ...\n" + " \n\n" + "The 'id' attribute is necessary and must be unique among the entities. If two\n" + "entities share the same id, initialization aborts.\n" + "The 'source' attribute specifies where to get the color of the floor from. Its\n" + "value, here denoted as SOURCE, can assume the following values:\n\n" + " image The color is calculated from the passed image file\n" + " loop_functions The color is calculated calling the loop functions\n\n" + "When 'source' is set to 'image', as showed in the following example, you have\n" + "to specify the image path in the additional attribute 'path':\n\n" + " \n" + " ...\n" + " \n" + " ...\n" + " \n\n" + "Many image formats are available, such as PNG, JPG, BMP, GIF and many more.\n" + "Refer to the FreeImage webpage for a complete list of supported image formats\n" + "(http://freeimage.sourceforge.net/features.html).\n\n" + "When 'source' is set to 'loop_functions', as showed in the following example,\n" + "an image is implicitly created to be used as texture for graphical\n" + "visualizations. The algorithm that creates the texture needs to convert from\n" + "meters (in the arena) to pixels (of the texture). You control how many pixels\n" + "per meter are used with the attribute 'pixels_per_meter'. Clearly, the higher\n" + "value, the higher the quality, but also the slower the algorithm and the bigger\n" + "the texture. The algorithm is called only once at init time, so the fact that\n" + "it is slow is not so important. However, the image size is limited by OpenGL.\n" + "Every implementation has its own limit, and you should check yours if any\n" + "texture-related problem arises. Now for the example:\n\n" + " \n" + " ...\n" + " \n" + " ...\n" + " \n\n" + "OPTIONAL XML CONFIGURATION\n\n" + "None for the time being.\n", + "Ready for use" + ); + +} diff --git a/src/core/simulator/entity/floor_entity.h b/src/core/simulator/entity/floor_entity.h new file mode 100644 index 00000000..6f1c1839 --- /dev/null +++ b/src/core/simulator/entity/floor_entity.h @@ -0,0 +1,83 @@ +/** + * @file + * + * @author Carlo Pinciroli - + */ + +#ifndef FLOOR_ENTITY_H +#define FLOOR_ENTITY_H + +namespace argos { + class CFloorEntity; +} + +#include +#include +#include +#include + +namespace argos { + + class CFloorEntity : public CEntity { + + public: + + class CFloorColorSource { + + public: + virtual ~CFloorColorSource() {} + virtual CColor GetColorAtPoint(Real f_x, + Real f_y) = 0; + + virtual void SaveAsImage(const std::string& str_path) = 0; + + }; + + public: + + CFloorEntity(); + virtual ~CFloorEntity(); + + virtual void Init(TConfigurationNode& t_tree); + virtual void Reset(); + + inline CColor GetColorAtPoint(Real f_x, + Real f_y) { + ARGOS_ASSERT(m_pcColorSource != NULL, + "The floor entity \"" << + GetId() << + "\" has no associated color source."); + return m_pcColorSource->GetColorAtPoint(f_x, f_y); + } + + inline bool HasChanged() const { + return m_bHasChanged; + } + + inline void SetChanged() { + m_bHasChanged = true; + } + + inline void ClearChanged() { + m_bHasChanged = false; + } + + inline void SaveAsImage(const std::string& str_path) { + m_pcColorSource->SaveAsImage(str_path); + } + + inline virtual std::string GetTypeDescription() const { + return "floor_entity"; + } + + private: + + CFloorColorSource* m_pcColorSource; + CVector3 m_cFloorSize; + std::string m_strColorSource; + unsigned int m_unPixelsPerMeter; + bool m_bHasChanged; + }; +} + +#endif diff --git a/src/core/simulator/entity/medium_entity.h b/src/core/simulator/entity/medium_entity.h new file mode 100644 index 00000000..51b046ec --- /dev/null +++ b/src/core/simulator/entity/medium_entity.h @@ -0,0 +1,36 @@ +/** + * @file + * + * @author Carlo Pinciroli - + */ + +#ifndef MEDIUM_ENTITY_H +#define MEDIUM_ENTITY_H + +namespace argos { + class CMediumEntity; +} + +#include + +namespace argos { + class CMediumEntity : public CEntity { + + public: + + CMediumEntity(CEntity* pc_parent) : + CEntity(pc_parent) {} + virtual ~CMediumEntity() {} + + virtual void Update() = 0; + + inline virtual std::string GetTypeDescription() const { + return "medium_entity"; + } + + }; + + typedef std::vector TMediumEntityVector; +} + +#endif diff --git a/src/core/simulator/entity/positional_entity.cpp b/src/core/simulator/entity/positional_entity.cpp new file mode 100644 index 00000000..4a606a5e --- /dev/null +++ b/src/core/simulator/entity/positional_entity.cpp @@ -0,0 +1,60 @@ +#include "positional_entity.h" +#include +#include +#include + +namespace argos { + + /****************************************/ + /****************************************/ + + void CPositionalEntity::Init(TConfigurationNode& t_tree) + { + try { + /* Initialize base entity */ + CEntity::Init(t_tree); + /* Get the position of the entity */ + GetNodeAttribute(t_tree, "position", m_cPosition); + m_cInitPosition = m_cPosition; + /* Get the orientation of the entity */ + std::string strOrientationEuler; + GetNodeAttribute(t_tree, "orientation", strOrientationEuler); + CDegrees pcOrientationEuler[3]; + ParseValues(strOrientationEuler, 3, pcOrientationEuler, ','); + m_cOrientation.FromEulerAngles(ToRadians(pcOrientationEuler[0]), + ToRadians(pcOrientationEuler[1]), + ToRadians(pcOrientationEuler[2])); + m_cInitOrientation = m_cOrientation; + + } + catch(CARGoSException& ex) { + THROW_ARGOSEXCEPTION_NESTED("Failed to initialize entity \"" << GetId() << "\".", ex); + } + } + + /****************************************/ + /****************************************/ + + void CPositionalEntity::Reset() { + /* Reset the entity's initial position and orientation */ + SetPosition(m_cInitPosition); + SetOrientation(m_cInitOrientation); + } + + /****************************************/ + /****************************************/ + + bool CPositionalEntity::MoveTo(const CVector3& c_position, + const CQuaternion& c_orientation, + bool b_check_only) { + if(! b_check_only) { + SetPosition(c_position); + SetOrientation(c_orientation); + } + return true; + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/simulator/entity/positional_entity.h b/src/core/simulator/entity/positional_entity.h new file mode 100644 index 00000000..03b40693 --- /dev/null +++ b/src/core/simulator/entity/positional_entity.h @@ -0,0 +1,80 @@ +/** + * @file + * + * @author Carlo Pinciroli - + */ + +#ifndef POSITIONAL_ENTITY_H +#define POSITIONAL_ENTITY_H + +namespace argos { + class CPositionalEntity; +} + +#include +#include +#include + +namespace argos { + class CPositionalEntity : public CEntity { + + public: + + CPositionalEntity(CEntity* pc_parent) : + CEntity(pc_parent) {} + virtual ~CPositionalEntity() {} + + virtual void Init(TConfigurationNode& t_tree); + virtual void Reset(); + + inline virtual const CVector3& GetPosition() const { + return m_cPosition; + } + + inline virtual const CVector3& GetInitPosition() const { + return m_cInitPosition; + } + + inline virtual void SetPosition(const CVector3& c_position) { + m_cPosition = c_position; + } + + inline virtual void SetInitPosition(const CVector3& c_position) { + m_cInitPosition = c_position; + } + + inline virtual const CQuaternion& GetOrientation() const { + return m_cOrientation; + } + + inline virtual const CQuaternion& GetInitOrientation() const { + return m_cInitOrientation; + } + + inline virtual void SetOrientation(const CQuaternion c_orientation) { + m_cOrientation = c_orientation; + } + + inline virtual void SetInitOrientation(const CQuaternion c_orientation) { + m_cInitOrientation = c_orientation; + } + + virtual bool MoveTo(const CVector3& c_position, + const CQuaternion& c_orientation, + bool b_check_only = false); + + inline virtual std::string GetTypeDescription() const { + return "positional_entity"; + } + + private: + + CVector3 m_cPosition; + CVector3 m_cInitPosition; + CQuaternion m_cOrientation; + CQuaternion m_cInitOrientation; + + }; +} + +#endif diff --git a/src/core/simulator/entity/wifi_medium_entity.h b/src/core/simulator/entity/wifi_medium_entity.h new file mode 100644 index 00000000..0b5a8189 --- /dev/null +++ b/src/core/simulator/entity/wifi_medium_entity.h @@ -0,0 +1,34 @@ +/** + * @file + * + * @author Carlo Pinciroli - + */ + +#ifndef WIFI_MEDIUM_ENTITY_H +#define WIFI_MEDIUM_ENTITY_H + +namespace argos { + class CWiFiMediumEntity; +} + +#include + +namespace argos { + class CWiFiMediumEntity : public CMediumEntity { + + public: + + CWiFiMediumEntity(CEntity* pc_parent) : + CMediumEntity(pc_parent) {} + virtual ~CWiFiMediumEntity() {} + + virtual void Update() {} + + inline virtual std::string GetTypeDescription() const { + return "wifi_medium_entity"; + } + + }; +} + +#endif diff --git a/src/core/simulator/extra/entity.h b/src/core/simulator/extra/entity.h new file mode 100644 index 00000000..ab5824bf --- /dev/null +++ b/src/core/simulator/extra/entity.h @@ -0,0 +1,35 @@ +#ifndef ENTITY_H +#define ENTITY_H + +#include + +namespace argos { + + class CEntity { + public: + virtual ~CEntity() {} + virtual void Operation() = 0; + }; + + + typedef CFactory TFactoryEntity; + +#define REGISTER_ENTITY(CLASSNAME, \ + LABEL, \ + AUTHOR, \ + VERSION, \ + BRIEF_DESCRIPTION, \ + LONG_DESCRIPTION, \ + STATUS) \ + REGISTER_SYMBOL(CEntity, \ + CLASSNAME, \ + LABEL, \ + AUTHOR, \ + VERSION, \ + BRIEF_DESCRIPTION, \ + LONG_DESCRIPTION, \ + STATUS) + +} + +#endif diff --git a/src/core/simulator/extra/old_main.cpp b/src/core/simulator/extra/old_main.cpp new file mode 100644 index 00000000..66a8383a --- /dev/null +++ b/src/core/simulator/extra/old_main.cpp @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +int main() { + +} + +// int main() { +// /* +// * INIT +// */ +// argos::CDynamicLoading cDL; +// cDL.LoadLibrary("./libplugin.so"); +// argos::TFactoryEntity::Print(std::cout); +// argos::CEntity* pcEA = argos::TFactoryEntity::New("entity_a"); +// argos::CEntity* pcEB = argos::TFactoryEntity::New("entity_b"); +// argos::COperation* pcOA = argos::TFactoryOperation::New("operation_a"); +// argos::COperation* pcOB = argos::TFactoryOperation::New("operation_b"); +// pcOA->Execute(pcEA); +// pcOA->Execute(pcEB); +// pcOB->Execute(pcEA); +// pcOB->Execute(pcEB); +// /* +// * CLEANUP +// */ +// delete pcOA; +// delete pcOB; +// delete pcEA; +// delete pcEB; +// return 0; +// } diff --git a/src/core/simulator/extra/operation.h b/src/core/simulator/extra/operation.h new file mode 100644 index 00000000..1ed692d3 --- /dev/null +++ b/src/core/simulator/extra/operation.h @@ -0,0 +1,35 @@ +#ifndef OPERATION_H +#define OPERATION_H + +#include + +namespace argos { + + class CEntity; + + class COperation { + public: + virtual ~COperation() {} + virtual void Execute(CEntity* e) = 0; + }; + + typedef CFactory TFactoryOperation; +#define REGISTER_OPERATION(CLASSNAME, \ + LABEL, \ + AUTHOR, \ + VERSION, \ + BRIEF_DESCRIPTION, \ + LONG_DESCRIPTION, \ + STATUS) \ + REGISTER_SYMBOL(COperation, \ + CLASSNAME, \ + LABEL, \ + AUTHOR, \ + VERSION, \ + BRIEF_DESCRIPTION, \ + LONG_DESCRIPTION, \ + STATUS) + +} + +#endif diff --git a/src/core/simulator/loop_functions.cpp b/src/core/simulator/loop_functions.cpp new file mode 100644 index 00000000..1b96aaad --- /dev/null +++ b/src/core/simulator/loop_functions.cpp @@ -0,0 +1,99 @@ +/** + * @file argos3/core/simulator/loop_functions.cpp + * + * @author Carlo Pinciroli - + */ + +#include "loop_functions.h" +#include + +namespace argos { + + /****************************************/ + /****************************************/ + + template T& AddEntityToSpace(TConfigurationNode& t_tree) { + /* Create an entity of the wanted type */ + try { + T* pcEntity = dynamic_cast(CFactory::New(t_tree.Value())); + /* Initialize the entity */ + pcEntity->Init(t_tree); + /* Add the entity to the space */ + CSimulator::GetInstance().GetSpace().AddEntity(*pcEntity); + /* Return the entity */ + return *pcEntity; + } + catch(CARGoSException ex) { + THROW_ARGOSEXCEPTION_NESTED("Loop functions: error to creating entity type \"" << t_tree.Value() << "\"", ex); + } + } + + /****************************************/ + /****************************************/ + + void AddEntityToPhysics(CEntity& c_entity, + const std::vector& str_engines) { + /* Add the entity to the physics engine(s) */ + for(size_t i = 0; i < str_engines.size(); ++i) { + CSimulator::GetInstance().GetPhysicsEngine(str_engines[i]).AddEntity(c_entity); + } + } + + /****************************************/ + /****************************************/ + + template void AssignController(T& c_entity) { + /* Get a reference to the controllable entity */ + CControllableEntity& cControllableEntity = c_entity.GetControllableEntity(); + /* Look for the controller with the right id in the XML */ + TConfigurationNode tControllersTree; + tControllersTree = GetNode(CSimulator::GetInstance().GetConfigurationRoot(), "controllers"); + bool found = false; + TConfigurationNodeIterator itControllers; + std::string strControllerId; + itControllers = itControllers.begin(&tControllersTree); + while(!found && itControllers != itControllers.end()) { + GetNodeAttribute(*itControllers, "id", strControllerId); + if(strControllerId == cControllableEntity.GetControllerId()) { + found = true; + } + else { + ++itControllers; + } + } + /* Did we find the controller? */ + ARGOS_ASSERT(found, + "[FATAL] The entity \"" << + c_entity.GetId() << "\" has been associated with a controller with id \"" << + cControllableEntity.GetControllerId() << + "\", but a controller with this id wasn't found in the section of the XML file."); + /* Now itControllers points to the right controller subtree */ + /* Get the parameters subtree */ + TConfigurationNode tControllerParameters; + tControllerParameters = GetNode(*itControllers, "parameters"); + // /* Create the controller */ + // CCI_Controller* pcController = + // CSimulator::GetInstance(). + // GetDynamicLinkingManager(). + // NewController(c_entity, + // cControllableEntity.GetControllerId(), + // tControllerParameters); + + // /* Set the controller to the entity */ + // cControllableEntity.SetController(*pcController); + } + + /****************************************/ + /****************************************/ + + void CLoopFunctions::RemoveEntity(CEntity& c_entity) { + /* Remove the entity from space */ + m_cSpace.RemoveEntity(c_entity); + /* free memory */ + delete &c_entity; + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/simulator/loop_functions.h b/src/core/simulator/loop_functions.h new file mode 100644 index 00000000..15bc69db --- /dev/null +++ b/src/core/simulator/loop_functions.h @@ -0,0 +1,91 @@ +/** + * @file argos3/core/simulator/loop_functions.h + * + * @brief This file provides the definition of the loop functions. + * + * @author Carlo Pinciroli - + */ + +#ifndef LOOP_FUNCTIONS_H +#define LOOP_FUNCTIONS_H + +namespace argos { + class CLoopFunctions; + class CFloorEntity; +} + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace argos { + + class CLoopFunctions : public CBaseConfigurableResource { + + protected: + + CSimulator& m_cSimulator; + CSpace& m_cSpace; + + public: + + struct SAdditionalLED { + CVector3 Position; + CColor Color; + + SAdditionalLED() {} + + SAdditionalLED(const CVector3& c_position, + const CColor& c_color) : + Position(c_position), + Color(c_color) {} + }; + + public: + + CLoopFunctions() : + m_cSimulator(CSimulator::GetInstance()), + m_cSpace(m_cSimulator.GetSpace()) { + } + virtual ~CLoopFunctions() {} + + inline virtual void Init(TConfigurationNode& t_tree) {} + inline virtual void Reset() {} + inline virtual void Destroy() {} + + inline virtual void PrePhysicsEngineStep() {} + inline virtual void PostPhysicsEngineStep() {} + + inline virtual bool IsExperimentFinished() { + return false; + } + + inline virtual CColor GetFloorColor(const CVector2& c_position_on_plane) { + return CColor::BLACK; + } + + protected: + + inline virtual bool MoveEntity(CPositionalEntity& c_entity, + const CVector3& c_position, + const CQuaternion& c_orientation, + bool b_check_only) { + return c_entity.MoveTo(c_position, c_orientation, b_check_only); + } + + inline virtual void RemoveEntity(const std::string& str_entity_id) { + CEntity& entity = m_cSpace.GetEntity(str_entity_id); + RemoveEntity(entity); + } + + virtual void RemoveEntity(CEntity& c_entity); + + }; +} + +#endif diff --git a/src/core/simulator/main.cpp b/src/core/simulator/main.cpp new file mode 100644 index 00000000..e0c1e6f3 --- /dev/null +++ b/src/core/simulator/main.cpp @@ -0,0 +1,84 @@ +/** + * @file argos3/core/simulator/main.cpp + * + * @brief This file provides the standard main() function to run the ARGoS + * simulator. + * + * In this file the standard main() function is defined. It provides the + * basic functionalities to run the ARGoS simulator: parses the command line, + * loads the experiment, runs the simulation and disposes all the data. + * + * @author Carlo Pinciroli - + */ + +#include +#include +#include +#include + +using namespace argos; + +/** + * @brief The standard main() function to run the ARGoS simulator. + * + * This main() function provides tha basic functionalities to run the ARGoS + * simulator: parses the command line, loads the experiment, runs the + * simulation and disposes all the data. + * + * @param argc the number of command line arguments given at the shell. + * @param argv the actual command line arguments. + * + * @return 0 if everything OK; 1 in case of errors. + * + */ +int main(int n_argc, char** ppch_argv) { + /* Create a new instance of the simulator */ + CSimulator& cSimulator = CSimulator::GetInstance(); + /* Configure the command line options */ + CARGoSCommandLineArgParser cACLAP(cSimulator); + + try { + /* Check the presence of the environment variable ARGOSINSTALLDIR */ + char* pchARGoSInstallDir = ::getenv("ARGOSINSTALLDIR"); + if (pchARGoSInstallDir == NULL) { + THROW_ARGOSEXCEPTION("Unable to find the required environment variable \"ARGOSINSTALLDIR\""); + } + cSimulator.SetInstallationDirectory(pchARGoSInstallDir); + + /* Parse command line */ + cACLAP.Parse(n_argc, ppch_argv); + + switch(cACLAP.GetAction()) { + case CARGoSCommandLineArgParser::ACTION_RUN_EXPERIMENT: + cSimulator.SetExperimentFileName(cACLAP.GetExperimentConfigFile()); + cSimulator.LoadExperiment(); + cSimulator.Execute(); + break; + + case CARGoSCommandLineArgParser::ACTION_QUERY: + QueryPlugins(cACLAP.GetQuery()); + break; + + case CARGoSCommandLineArgParser::ACTION_SHOW_HELP: + cACLAP.PrintUsage(LOG); + break; + + case CARGoSCommandLineArgParser::ACTION_UNKNOWN: + /* Should never get here */ + break; + } + + /* Done, destroy stuff */ + cSimulator.Destroy(); + } + catch(std::exception& ex) { + /* A fatal error occurred: dispose of data, print error and exit */ + cSimulator.Destroy(); + LOGERR << "[FATAL] " << ex.what() << std::endl; + LOG.Flush(); + LOGERR.Flush(); + return 1; + } + /* Everything's ok, exit */ + return 0; +} diff --git a/src/core/simulator/physics_engine/physics_engine.cpp b/src/core/simulator/physics_engine/physics_engine.cpp new file mode 100644 index 00000000..ee5500e8 --- /dev/null +++ b/src/core/simulator/physics_engine/physics_engine.cpp @@ -0,0 +1,53 @@ +/** + * @file argos3/core/simulator/physics_engine/physics_engine.cpp + * + * @author Carlo Pinciroli - + */ + +#include +#include "physics_engine.h" +#include +#include +#include + +namespace argos { + + /* The default value of the simulation clock tick */ + Real CPhysicsEngine::m_fSimulationClockTick = 0.1f; + Real CPhysicsEngine::m_fInverseSimulationClockTick = 1.0f / CPhysicsEngine::m_fSimulationClockTick; + + /****************************************/ + /****************************************/ + + void CPhysicsEngine::Init(TConfigurationNode& t_tree) { + /* Get physics engine id from the XML */ + GetNodeAttribute(t_tree, "id", m_strId); + } + + /****************************************/ + /****************************************/ + + Real CPhysicsEngine::GetSimulationClockTick() { + return m_fSimulationClockTick; + } + + /****************************************/ + /****************************************/ + + Real CPhysicsEngine::GetInverseSimulationClockTick() { + return m_fInverseSimulationClockTick; + } + + /****************************************/ + /****************************************/ + + void CPhysicsEngine::SetSimulationClockTick(Real f_simulation_clock_tick) { + LOG << "[INFO] Using simulation clock tick = " << f_simulation_clock_tick << std::endl; + m_fSimulationClockTick = f_simulation_clock_tick; + m_fInverseSimulationClockTick = 1.0f / f_simulation_clock_tick; + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/simulator/physics_engine/physics_engine.h b/src/core/simulator/physics_engine/physics_engine.h new file mode 100644 index 00000000..167c6ec6 --- /dev/null +++ b/src/core/simulator/physics_engine/physics_engine.h @@ -0,0 +1,104 @@ +/** + * @file argos3/core/simulator/physics_engine/physics_engine.h + * + * @author Carlo Pinciroli - + */ + +#ifndef PHYSICS_ENGINE_H +#define PHYSICS_ENGINE_H + +namespace argos { + class CPhysicsEngine; + class CPhysicsEngineEntity; + class CEntity; +} + +#include +#include +#include +#include +#include +#include + +namespace argos { + + class CPhysicsEngine : public CBaseConfigurableResource { + + public: + + typedef std::vector TVector; + typedef std::map > TMap; + + public: + + CPhysicsEngine() : + m_fPhysicsEngineClock(0.0) {} + virtual ~CPhysicsEngine() {} + + virtual void Init(TConfigurationNode& t_tree); + virtual void Reset() {} + virtual void Destroy() {} + + virtual void Update() = 0; + + virtual UInt32 GetNumPhysicsEngineEntities() = 0; + virtual void AddEntity(CEntity& c_entity) = 0; + virtual void RemoveEntity(CEntity& c_entity) = 0; + inline virtual bool IsEntityTransferNeeded() const { + return false; + } + virtual void TransferEntities() {} + + static Real GetSimulationClockTick(); + static Real GetInverseSimulationClockTick(); + static void SetSimulationClockTick(Real f_simulation_clock_tick); + + inline void SetPhysicsEngineClock(Real f_physics_engine_clock) { + m_fPhysicsEngineClock = f_physics_engine_clock; + } + inline Real GetPhysicsEngineClock() const { + return m_fPhysicsEngineClock; + } + + inline const std::string& GetId() const { + return m_strId; + } + void SetId(const std::string& str_id) { + m_strId = str_id; + } + + protected: + + /** The physics engine's id. */ + std::string m_strId; + + /** How long a clock tick lasts (in seconds) */ + static Real m_fSimulationClockTick; + + /** The inverse of m_fSimulationClockTick */ + static Real m_fInverseSimulationClockTick; + + /** The current clock as seen by this physics engine */ + Real m_fPhysicsEngineClock; + + }; + +} + +#define REGISTER_PHYSICS_ENGINE(CLASSNAME, \ + LABEL, \ + AUTHOR, \ + VERSION, \ + BRIEF_DESCRIPTION, \ + LONG_DESCRIPTION, \ + STATUS) \ + REGISTER_SYMBOL(CPhysicsEngine, \ + CLASSNAME, \ + LABEL, \ + AUTHOR, \ + VERSION, \ + BRIEF_DESCRIPTION, \ + LONG_DESCRIPTION, \ + STATUS) + +#endif diff --git a/src/core/simulator/physics_engine/physics_engine_entity.h b/src/core/simulator/physics_engine/physics_engine_entity.h new file mode 100644 index 00000000..7ec7d5b7 --- /dev/null +++ b/src/core/simulator/physics_engine/physics_engine_entity.h @@ -0,0 +1,59 @@ +/** + * @file argos3/core/simulator/physics_engine/physics_engine_entity.h + * + * @brief This file provides the definition of the physics engine entity interface. + * + * @author Carlo Pinciroli - + */ + +#ifndef PHYSICS_ENGINE_ENTITY_H +#define PHYSICS_ENGINE_ENTITY_H + +namespace argos { + class CPhysicsEngineEntity; + class CEmbodiedEntity; + class CRay; + class CVector3; + class CQuaternion; +} + +#include +#include +#include +#include + +namespace argos { + + class CPhysicsEngineEntity { + + public: + + typedef std::map TMap; + typedef std::vector TVector; + + public: + + CPhysicsEngineEntity(CEmbodiedEntity& c_entity) : + m_pcEmbodiedEntity(&c_entity) {} + virtual ~CPhysicsEngineEntity() {} + + inline CEmbodiedEntity& GetEmbodiedEntity() const { + return *m_pcEmbodiedEntity; + } + + virtual bool CheckIntersectionWithRay(Real& f_t_on_ray, + const CRay& c_ray) const = 0; + + virtual bool MoveTo(const CVector3& c_position, + const CQuaternion& c_orientation, + bool b_check_only = false) = 0; + + protected: + + CEmbodiedEntity* m_pcEmbodiedEntity; + + }; + +} + +#endif diff --git a/src/core/simulator/simulated_actuator.h b/src/core/simulator/simulated_actuator.h new file mode 100644 index 00000000..0bc6f833 --- /dev/null +++ b/src/core/simulator/simulated_actuator.h @@ -0,0 +1,40 @@ +/** + * @file argos3/core/simulator/simulated_actuator.h + * + * @brief This file provides the definition of + * + * @author Carlo Pinciroli - + */ + +#ifndef SIMULATED_ACTUATOR_H +#define SIMULATED_ACTUATOR_H + +namespace argos { + class CSimulatedActuator; + class CEntity; +} + +#include + +#include +#include + +namespace argos { + + class CSimulatedActuator : virtual public CCI_Actuator { + + public: + + virtual ~CSimulatedActuator() {} + + virtual CEntity& GetEntity() = 0; + virtual void SetEntity(CEntity& c_entity) = 0; + + virtual void Update() = 0; + virtual void Reset() = 0; + + }; + +} + +#endif diff --git a/src/core/simulator/simulated_sensor.h b/src/core/simulator/simulated_sensor.h new file mode 100644 index 00000000..8bd384c0 --- /dev/null +++ b/src/core/simulator/simulated_sensor.h @@ -0,0 +1,42 @@ +/** + * @file argos3/core/simulator/simulated_sensor.h + * + * @brief This file provides the definition of + * + * @author Carlo Pinciroli - + */ + +#ifndef SIMULATED_SENSOR_H +#define SIMULATED_SENSOR_H + +/* To avoid dependency problems when including */ +namespace argos { + class CSimulatedSensor; + class CEntity; +} + +#include +#include +#include + +namespace argos { + + class CSimulatedSensor { + + public: + + virtual ~CSimulatedSensor() {} + + virtual CEntity& GetEntity() = 0; + virtual void SetEntity(CEntity& c_entity) = 0; + + virtual void Update() = 0; + virtual void Reset() = 0; + + virtual void Destroy() {} + + }; + +} + +#endif diff --git a/src/core/simulator/simulator.cpp b/src/core/simulator/simulator.cpp new file mode 100644 index 00000000..8027e68f --- /dev/null +++ b/src/core/simulator/simulator.cpp @@ -0,0 +1,540 @@ +/** + * @file argos3/core/simulator/simulator.cpp + * + * @author Carlo Pinciroli - + */ + +#include "simulator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const std::string CONFIGURATION_CLOCKTICK = "clocktick"; +const std::string CONFIGURATION_MAXCLOCK = "maxclock"; +const std::string CONFIGURATION_RANDOMSEED = "random_seed"; +const std::string CONFIGURATION_CONTROLLER_PATH = "controller_path"; +const std::string CONFIGURATION_MAPPING_ENGINE_ID = "id"; +const std::string CONFIGURATION_MAPPING_ENTITY_ID = "id"; + +namespace argos { + + /****************************************/ + /****************************************/ + + CSimulator::CSimulator() : + m_pcVisualization(NULL), + m_pcSpace(NULL), + m_pcLoopFunctions(NULL), + m_unMaxSimulationClock(0), + m_bWasRandomSeedSet(false), + m_unThreads(0), + m_pcProfiler(NULL), + m_bHumanReadableProfile(true) {} + + /****************************************/ + /****************************************/ + + CSimulator::~CSimulator() { + if(IsProfiling()) { + delete m_pcProfiler; + } + /* Delete the visualization */ + if(m_pcVisualization != NULL) delete m_pcVisualization; + /* Delete all the physics engines */ + for(CPhysicsEngine::TMap::iterator it = m_mapPhysicsEngines.begin(); it + != m_mapPhysicsEngines.end(); ++it) + delete it->second; + m_mapPhysicsEngines.clear(); + m_vecPhysicsEngines.clear(); + /* Delete the space and the dynamic linking manager */ + if(m_pcSpace != NULL) { + delete m_pcSpace; + } + } + + /****************************************/ + /****************************************/ + + CSimulator& CSimulator::GetInstance() { + static std::auto_ptr pcSimulatorInstance(new CSimulator()); + return *(pcSimulatorInstance.get()); + } + + /****************************************/ + /****************************************/ + + void CSimulator::LoadExperiment() { + /* Build configuration tree */ + m_tConfiguration.LoadFile(m_strExperimentConfigFileName); + m_tConfigurationRoot = *m_tConfiguration.FirstChildElement(); + /* Init the experiment */ + Init(); + LOG.Flush(); + LOGERR.Flush(); + } + + /****************************************/ + /****************************************/ + + void CSimulator::Init() { + /* General configuration */ + InitFramework(GetNode(m_tConfigurationRoot, "framework")); + + /* TODO: MANAGE CREATION OF LOOP FUNCTIONS */ + + /* Space */ + InitSpace(GetNode(m_tConfigurationRoot, "arena")); + + /* Physics engines */ + InitPhysics(GetNode(m_tConfigurationRoot, "physics_engines")); + + /* Initialise the mapping between physics engines and entities */ + InitPhysicsEntitiesMapping(m_tConfigurationRoot); + + /* Call user init function */ + m_pcLoopFunctions->Init(GetNode(m_tConfigurationRoot, "loop_functions")); + + /* Initialise visualization */ + InitVisualization(GetNode(m_tConfigurationRoot, "visualization")); + + /* Start profiling, if needed */ + if(IsProfiling()) { + m_pcProfiler->Start(); + } + } + + /****************************************/ + /****************************************/ + + void CSimulator::Reset() { + /* if random seed is 0 or is not specified, init with the current timeval */ + if(m_bWasRandomSeedSet) { + CRandom::SetSeedOf("argos", m_unRandomSeed); + } + else { + /* Prepare the default value based on the current clock time */ + struct timeval sTimeValue; + ::gettimeofday(&sTimeValue, NULL); + UInt32 unSeed = static_cast(sTimeValue.tv_usec); + CRandom::SetSeedOf("argos", unSeed); + m_unRandomSeed = unSeed; + LOG << "[INFO] Using random seed = " << m_unRandomSeed << std::endl; + } + CRandom::GetCategory("argos").ResetRNGs(); + + /* Reset the space */ + m_pcSpace->Reset(); + + /* Reset the physics engines */ + for(CPhysicsEngine::TMap::iterator it = m_mapPhysicsEngines.begin(); + it != m_mapPhysicsEngines.end(); ++it) { + it->second->Reset(); + } + + /* Reset the loop functions */ + m_pcLoopFunctions->Reset(); + + LOG.Flush(); + LOGERR.Flush(); + } + + /****************************************/ + /****************************************/ + + void CSimulator::Destroy() { + /* Call user destroy function */ + if (m_pcLoopFunctions != NULL) { + m_pcLoopFunctions->Destroy(); + delete m_pcLoopFunctions; + m_pcLoopFunctions = NULL; + } + + /* Destroy the visualization */ + if(m_pcVisualization != NULL) m_pcVisualization->Destroy(); + + /* Close physics engines */ + for(CPhysicsEngine::TMap::iterator it = m_mapPhysicsEngines.begin(); + it != m_mapPhysicsEngines.end(); ++it) { + it->second->Destroy(); + delete it->second; + } + m_mapPhysicsEngines.clear(); + m_vecPhysicsEngines.clear(); + + /* Destroy simulated space */ + if(m_pcSpace != NULL) { + m_pcSpace->Destroy(); + } + + /* Get rid of ARGoS category */ + if(CRandom::ExistsCategory("argos")) { + CRandom::RemoveCategory("argos"); + } + + /* Stop profiling and flush the data */ + if(IsProfiling()) { + m_pcProfiler->Stop(); + m_pcProfiler->Flush(m_bHumanReadableProfile); + } + + LOG.Flush(); + LOGERR.Flush(); + } + + /****************************************/ + /****************************************/ + + void CSimulator::Execute() { + m_pcVisualization->Execute(); + } + + /****************************************/ + /****************************************/ + + void CSimulator::UpdateSpace() { + /* Increase the simulation clock */ + m_pcSpace->IncreaseSimulationClock(); + /* Call loop functions */ + m_pcLoopFunctions->PrePhysicsEngineStep(); + LOG.Flush(); + LOGERR.Flush(); + /* Update the space */ + m_pcSpace->Update(); + LOG.Flush(); + LOGERR.Flush(); + /* Call loop functions */ + m_pcLoopFunctions->PostPhysicsEngineStep(); + LOG.Flush(); + LOGERR.Flush(); + } + + /****************************************/ + /****************************************/ + + bool CSimulator::IsExperimentFinished() const { + /* + The experiment is considered finished when the simulation clock exceeds + the maximum value set in the XML, or when one of the visualisations asks + to terminate. + */ + + /* Check simulation clock */ + if (m_unMaxSimulationClock > 0 && + m_pcSpace->GetSimulationClock() >= m_unMaxSimulationClock) { + return true; + } + + /* Call loop function */ + return m_pcLoopFunctions->IsExperimentFinished(); + } + + /****************************************/ + /****************************************/ + + void CSimulator::InitFramework(TConfigurationNode& t_tree) { + try { + /* Parse the 'system' node */ + if(NodeExists(t_tree, "system")) { + TConfigurationNode tSystem; + tSystem = GetNode(t_tree, "system"); + GetNodeAttributeOrDefault(tSystem, "threads", m_unThreads, m_unThreads); + if(m_unThreads == 0) { + LOG << "[INFO] Not using threads" << std::endl; + m_pcSpace = new CSpaceNoThreads; + } + else { + LOG << "[INFO] Using " << m_unThreads << " parallel threads" << std::endl; + std::string strThreadingMethod = "scatter-gather"; + GetNodeAttributeOrDefault(tSystem, "method", strThreadingMethod, strThreadingMethod); + if(strThreadingMethod == "scatter-gather") { + LOG << "[INFO] Using threading method \"scatter-gather\"" << std::endl; + m_pcSpace = new CSpaceMultiThreadScatterGather; + } + else if(strThreadingMethod == "h-dispatch") { + LOG << "[INFO] Using threading method \"h-dispatch\"" << std::endl; + m_pcSpace = new CSpaceMultiThreadHDispatch; + } + else { + THROW_ARGOSEXCEPTION("Error parsing the tag. Unknown threading method \"" << strThreadingMethod << "\". Available methods: \"scatter-gather\" and \"h-dispatch\"."); + } + } + } + else { + LOG << "[INFO] Not using threads" << std::endl; + m_pcSpace = new CSpaceNoThreads; + } + + /* Get 'experiment' node */ + TConfigurationNode tExperiment; + tExperiment = GetNode(t_tree, "experiment"); + + /* Parse random seed */ + /* Buffer to hold the random seed */ + GetNodeAttributeOrDefault(tExperiment, + "random_seed", + m_unRandomSeed, + static_cast(0)); + + /* if random seed is 0 or is not specified, init with the current timeval */ + if(m_unRandomSeed != 0) { + CRandom::CreateCategory("argos", m_unRandomSeed); + LOG << "[INFO] Using random seed = " << m_unRandomSeed << std::endl; + m_bWasRandomSeedSet = true; + } + else { + /* Prepare the default value based on the current clock time */ + m_bWasRandomSeedSet = false; + struct timeval sTimeValue; + ::gettimeofday(&sTimeValue, NULL); + UInt32 unSeed = static_cast(sTimeValue.tv_usec); + m_unRandomSeed = unSeed; + CRandom::CreateCategory("argos", unSeed); + LOG << "[INFO] Using random seed = " << unSeed << std::endl; + } + m_pcRNG = CRandom::CreateRNG("argos"); + + /* Set the simulation clock tick length */ + UInt32 unTicksPerSec; + GetNodeAttribute(tExperiment, + "ticks_per_second", + unTicksPerSec); + CPhysicsEngine::SetSimulationClockTick(1.0 / static_cast(unTicksPerSec)); + + /* Set the maximum simulation ticks (in ticks) */ + Real fExpLength; + GetNodeAttributeOrDefault(tExperiment, + "length", + fExpLength, + 0.0f); + m_unMaxSimulationClock = fExpLength * unTicksPerSec; + LOG << "[INFO] Total experiment length in clock ticks = " + << (m_unMaxSimulationClock ? ToString(m_unMaxSimulationClock) : "unlimited") + << std::endl; + + /* Get the profiling tag, if present */ + if(NodeExists(t_tree, "profiling")) { + TConfigurationNode& tProfiling = GetNode(t_tree, "profiling"); + std::string strFile; + GetNodeAttribute(tProfiling, "file", strFile); + std::string strFormat; + GetNodeAttribute(tProfiling, "format", strFormat); + if(strFormat == "human_readable") { + m_bHumanReadableProfile = true; + } + else if(strFormat == "table") { + m_bHumanReadableProfile = false; + } + else { + THROW_ARGOSEXCEPTION("Unrecognized profile format \"" << strFormat << "\". Accepted values are \"human_readable\" and \"table\"."); + } + bool bTrunc = true; + GetNodeAttributeOrDefault(tProfiling, "truncate_file", bTrunc, bTrunc); + m_pcProfiler = new CProfiler(strFile, bTrunc); + } + } + catch(CARGoSException& ex) { + THROW_ARGOSEXCEPTION_NESTED("Failed to initialize the simulator. Parse error inside the tag.", ex); + } + } + + /****************************************/ + /****************************************/ + + void CSimulator::InitSpace(TConfigurationNode& t_tree) { + try { + m_pcSpace->Init(t_tree); + } + catch(CARGoSException& ex) { + THROW_ARGOSEXCEPTION_NESTED("Failed to initialize the space. Error in the subtree.", ex); + } + } + + /****************************************/ + /****************************************/ + + void CSimulator::InitPhysics(TConfigurationNode& t_tree) { + try { + /* Cycle through the physics engines */ + TConfigurationNodeIterator itEngines; + for(itEngines = itEngines.begin(&t_tree); + itEngines != itEngines.end(); + ++itEngines) { + /* Create the physics engine */ + CPhysicsEngine* pcEngine = CFactory::New(itEngines->Value()); + try { + /* Initialize the engine */ + pcEngine->Init(*itEngines); + /* Check that an engine with that ID does not exist yet */ + if(m_mapPhysicsEngines.find(pcEngine->GetId()) == m_mapPhysicsEngines.end()) { + /* Add it to the lists */ + m_mapPhysicsEngines[pcEngine->GetId()] = pcEngine; + m_vecPhysicsEngines.push_back(pcEngine); + } + else { + /* Duplicate id -> error */ + THROW_ARGOSEXCEPTION("A physics engine with id \"" << pcEngine->GetId() << "\" exists already. The ids must be unique!"); + } + } + catch(CARGoSException& ex) { + /* Error while executing engine init, destroy what done to prevent memory leaks */ + pcEngine->Destroy(); + delete pcEngine; + THROW_ARGOSEXCEPTION_NESTED("Error initializing physics engine type \"" << itEngines->Value() << "\"", ex); + } + } + } + catch(CARGoSException& ex) { + THROW_ARGOSEXCEPTION_NESTED("Failed to initialize the physics engines. Parse error in the subtree.", ex); + } + } + + /****************************************/ + /****************************************/ + + void CSimulator::InitVisualization(TConfigurationNode& t_tree) { + try { + /* Consider only the first visualization */ + TConfigurationNodeIterator itVisualization; + itVisualization = itVisualization.begin(&t_tree); + if(itVisualization != NULL) { + /* Create the visualization */ + m_pcVisualization = CFactory::New(itVisualization->Value()); + /* Initialize the visualization */ + m_pcVisualization->Init(*itVisualization); + } + else { + LOGERR << "[WARNING] No visualization selected." << std::endl; + m_pcVisualization = new CVisualization(); + } + } + catch(CARGoSException& ex) { + THROW_ARGOSEXCEPTION_NESTED("Failed to initialize the visualization. Parse error in the subtree.", ex); + } + } + + /****************************************/ + /****************************************/ + + void CSimulator::InitPhysicsEntitiesMapping(TConfigurationNode& t_tree) { + /* Get the base node for the mappings */ + TConfigurationNode tArenaPhysics; + tArenaPhysics = GetNode(t_tree, "arena_physics"); + /* Cycle through the engines */ + TConfigurationNodeIterator itEngines; + std::string strEngineId; + for(itEngines = itEngines.begin(&tArenaPhysics); + itEngines != itEngines.end(); + ++itEngines) { + /* Get the id of the engine */ + GetNodeAttribute(*itEngines, "id", strEngineId); + /* Get a reference to the respective physics engine object */ + CPhysicsEngine::TMap::iterator itEngineMap = m_mapPhysicsEngines.find(strEngineId); + if (itEngineMap == m_mapPhysicsEngines.end()) { + THROW_ARGOSEXCEPTION("Unknown physics engine id \"" << strEngineId << "\" specified in the arena_physics section."); + } + CPhysicsEngine* pcEngine = itEngineMap->second; + /* Cycle through the entities associated to this engine and add them to it */ + TConfigurationNodeIterator itEntities; + std::string strEntityId; + TEntityVector tMatchingEntities; + for(itEntities = itEntities.begin(&(*itEngines)); + itEntities != itEntities.end(); + ++itEntities) { + /* Clear the matching entities */ + tMatchingEntities.clear(); + /* Get the id of the entity */ + GetNodeAttribute(*itEntities, "id", strEntityId); + /* Get a reference to the entities to add */ + m_pcSpace->GetEntitiesMatching(tMatchingEntities, "^" + strEntityId + "$"); + if(tMatchingEntities.size() == 0) { + THROW_ARGOSEXCEPTION("No entity matches the regular expression \"" << strEntityId << "\""); + } + /* Go through the matching entities */ + for(TEntityVector::iterator itMatchingEntities = tMatchingEntities.begin(); + itMatchingEntities != tMatchingEntities.end(); ++itMatchingEntities) { + CEntity& cEntity = **itMatchingEntities; + /* Add the entity to the engine */ + /* + LOG << "[INFO] Adding entity \"" + << cEntity.GetId() + << "\" to engine \"" + << pcEngine->GetId() + << "\"" + << std::endl; + */ + pcEngine->AddEntity(cEntity); + /* Is the entity composable with a controllable component? */ + CComposableEntity* pcComposableEntity = dynamic_cast(&cEntity); + if(pcComposableEntity != NULL && + pcComposableEntity->HasComponent("controllable_entity")) { + /* The entity is controllable, there's more to do: associating a controller */ + CControllableEntity& cControllableEntity = pcComposableEntity->GetComponent("controllable_entity"); + TConfigurationNode tControllerParameters; + /* Try to get the section in the robot entity */ + /* If there's none, get the one set in the controller */ + if(NodeExists(*itEntities, "parameters")) { + /* Look in the section in the entity */ + tControllerParameters = GetNode(*itEntities, "parameters"); + } + else { + /* The previous check failed, so look in the controller now */ + /* Look for the controller with the right id in the XML */ + TConfigurationNode tControllersTree; + tControllersTree = GetNode(t_tree, "controllers"); + bool bFound = false; + TConfigurationNodeIterator itControllers; + std::string strControllerId; + itControllers = itControllers.begin(&tControllersTree); + while(!bFound && itControllers != itControllers.end()) { + GetNodeAttribute(*itControllers, "id", strControllerId); + if(strControllerId == cControllableEntity.GetControllerId()) { + bFound = true; + } + else { + ++itControllers; + } + } + /* Did we find the controller? */ + if(! bFound) { + THROW_ARGOSEXCEPTION( + "[FATAL] The entity \"" << + cControllableEntity.GetId() << "\" has been associated with a controller with id \"" << + cControllableEntity.GetControllerId() << + "\", but a controller with this id wasn't found in the section of the XML file."); + } + /* Now itControllers points to the right controller subtree */ + /* Get the parameters subtree */ + tControllerParameters = GetNode(*itControllers, "parameters"); + } + /* TODO */ + /* Create the controller */ + // CCI_Controller* pcController = + // GetDynamicLinkingManager(). + // NewController(cEntity, + // cControllableEntity.GetControllerId(), + // tControllerParameters); + + // /* Set the controller to the entity */ + // cControllableEntity.SetController(*pcController); + } + } + } + } + m_pcSpace->SetPhysicsEngines(m_vecPhysicsEngines); + } +} diff --git a/src/core/simulator/simulator.h b/src/core/simulator/simulator.h new file mode 100644 index 00000000..de638dba --- /dev/null +++ b/src/core/simulator/simulator.h @@ -0,0 +1,233 @@ +/** + * @mainpage Autonomous Robots GO Swarming + * @author Carlo Pinciroli + * + * @ref README "ARGoS QUICK GUIDE" + * @include README + * + * @version 3.0 + * @see http://iridia.ulb.ac.be/ + * @see http://www.swarmanoid.org/ + * @see http://ascens-ist.eu/ + */ + +/** + * @file argos3/core/simulator/simulator.h + * + * @brief This file provides the definition of class CSimulator, the core class of ARGoS. + * + * This file provides class CSimulator, the core class of ARGoS. + * CSimulator organizes the flow of data coming from the modules of ARGoS + * and provides the main simulation loop. + * + * @author Carlo Pinciroli - + */ + +#ifndef SIMULATOR_H +#define SIMULATOR_H + +namespace argos { + class CSimulator; + class CLoopFunctions; + class CVisualization; + class CSpace; + class CProfiler; +} + +#include +#include +#include +#include +#include +#include + +/** + * @brief The namespace containing all the ARGoS related code. + * + * The namespace containing all the ARGoS related code. + */ +namespace argos { + + /** + * @class CSimulator + * + * @brief The core class of ARGOS. + * + * Class CSimulator is the core class of ARGOS. CSimulator organises the flow + * of data coming from the modules of ARGoS and provides the main simulation + * loop. + * + */ + class CSimulator { + + private: + + /** A reference to the visualization */ + CVisualization* m_pcVisualization; + + /** The vector of physics engines */ + CPhysicsEngine::TMap m_mapPhysicsEngines; + CPhysicsEngine::TVector m_vecPhysicsEngines; + + /** The space */ + CSpace* m_pcSpace; + + /** Reference to the loop functions */ + CLoopFunctions* m_pcLoopFunctions; + + /** The file name of the configuration XML file */ + std::string m_strExperimentConfigFileName; + + /** The maximum simulation clock */ + UInt32 m_unMaxSimulationClock; + + /** The random seed used for initialisation */ + UInt32 m_unRandomSeed; + + /** The random number generator */ + CRandom::CRNG* m_pcRNG; + + /** The flag to remember if the random seed was set at the beginning or init with the time of the day */ + bool m_bWasRandomSeedSet; + + /** XML configuration file */ + ticpp::Document m_tConfiguration; + + /** XML configuration file root node */ + TConfigurationNode m_tConfigurationRoot; + + /** Directory pointed to by ARGOSINSTALLDIR */ + std::string m_strInstallationDirectory; + + /** The number of parallel threads to split up the computation */ + UInt32 m_unThreads; + + /** Profiler class */ + CProfiler* m_pcProfiler; + + /** Profiler format */ + bool m_bHumanReadableProfile; + + private: + + CSimulator(); + + CSimulator(const CSimulator&) {} + + CSimulator& operator=(const CSimulator&) { + return *this; + } + + public: + + ~CSimulator(); + + static CSimulator& GetInstance(); + + inline CSpace& GetSpace() const { + return *m_pcSpace; + } + + inline CPhysicsEngine& GetPhysicsEngine(const std::string& str_id) const { + CPhysicsEngine::TMap::const_iterator it = m_mapPhysicsEngines.find(str_id); + ARGOS_ASSERT(it != m_mapPhysicsEngines.end(), "Physics engine \"" << str_id << "\" not found.") + return *(it->second); + } + + inline CPhysicsEngine::TVector& GetPhysicsEngines() { + return m_vecPhysicsEngines; + } + + inline CVisualization& GetVisualization() { + ARGOS_ASSERT(m_pcVisualization != NULL, "No visualization specified in the XML file."); + return *m_pcVisualization; + } + + inline TConfigurationNode& GetConfigurationRoot() { + return m_tConfigurationRoot; + } + + inline CProfiler& GetProfiler() { + return *m_pcProfiler; + } + + inline bool IsProfiling() const { + return m_pcProfiler != NULL; + } + + void LoadExperiment(); + + void Init(); + + void Reset(); + + inline void Reset(UInt32 un_new_random_seed) { + SetRandomSeed(un_new_random_seed); + Reset(); + } + + void Destroy(); + + void Execute(); + + void UpdateSpace(); + + bool IsExperimentFinished() const; + + inline UInt32 GetRandomSeed() const { + return m_unRandomSeed; + } + + inline void SetRandomSeed(UInt32 un_random_seed) { + m_unRandomSeed = un_random_seed; + m_bWasRandomSeedSet = true; + } + + inline CRandom::CRNG* GetRNG() { + return m_pcRNG; + } + + inline const std::string& GetExperimentFileName() const { + return m_strExperimentConfigFileName; + } + + inline void SetExperimentFileName(const std::string& str_file_name) { + m_strExperimentConfigFileName = str_file_name; + } + + inline CLoopFunctions& GetLoopFunctions() { + return *m_pcLoopFunctions; + } + + inline void SetLoopFunctions(CLoopFunctions& c_loop_functions) { + m_pcLoopFunctions = &c_loop_functions; + } + + inline UInt32 GetMaxSimulationClock() const { + return m_unMaxSimulationClock; + } + + inline void SetInstallationDirectory(const std::string& str_installation_dir) { + m_strInstallationDirectory = str_installation_dir; + } + + inline const std::string& GetInstallationDirectory() const { + return m_strInstallationDirectory; + } + + inline UInt32 GetNumThreads() const { + return m_unThreads; + } + + private: + + void InitFramework(TConfigurationNode& t_tree); + void InitSpace(TConfigurationNode& t_tree); + void InitPhysics(TConfigurationNode& t_tree); + void InitVisualization(TConfigurationNode& t_tree); + void InitPhysicsEntitiesMapping(TConfigurationNode& t_tree); + }; + +} + +#endif diff --git a/src/core/simulator/space/space.cpp b/src/core/simulator/space/space.cpp new file mode 100644 index 00000000..ac0a62a0 --- /dev/null +++ b/src/core/simulator/space/space.cpp @@ -0,0 +1,988 @@ +/** + * @file argos3/core/simulator/space/space.cpp + * + * @brief This file provides the implementation of the Swarmanoid 3D Space. + * + * This file provides the implementation of the Swarmanoid 3D Space. + * + * @author Carlo Pinciroli - + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "space.h" + +namespace argos { + + /****************************************/ + /****************************************/ + + class CSpace::CRayEmbodiedEntityIntersectionMethod { + public: + CRayEmbodiedEntityIntersectionMethod(CSpace& c_space) : m_cSpace(c_space) {} + virtual bool operator()(SEntityIntersectionItem& s_data, + const CRay& c_ray, + const TEmbodiedEntitySet& set_ignored_entities) = 0; + protected: + CSpace& m_cSpace; + }; + + class CSpace::CRayEmbodiedEntityIntersectionSpaceHash : public CSpace::CRayEmbodiedEntityIntersectionMethod { + public: + CRayEmbodiedEntityIntersectionSpaceHash(CSpace& c_space) : CSpace::CRayEmbodiedEntityIntersectionMethod(c_space) {} + virtual bool operator()(CSpace::SEntityIntersectionItem& s_data, + const CRay& c_ray, + const TEmbodiedEntitySet& set_ignored_entities) { + return m_cSpace.GetClosestEmbodiedEntityIntersectedByRaySpaceHash(s_data, c_ray, set_ignored_entities); + } + }; + + class CSpace::CRayEmbodiedEntityIntersectionEntitySweep : public CSpace::CRayEmbodiedEntityIntersectionMethod { + public: + CRayEmbodiedEntityIntersectionEntitySweep(CSpace& c_space) : CSpace::CRayEmbodiedEntityIntersectionMethod(c_space) {} + virtual bool operator()(CSpace::SEntityIntersectionItem& s_data, + const CRay& c_ray, + const TEmbodiedEntitySet& set_ignored_entities) { + return m_cSpace.GetClosestEmbodiedEntityIntersectedByRayEntitySweep(s_data, c_ray, set_ignored_entities); + } + }; + + /****************************************/ + /****************************************/ + + CSpace::CSpace() : + m_unSimulationClock(0), + m_pcEmbodiedEntitiesSpaceHash(NULL), +// m_pcLEDEntitiesSpaceHash(NULL), +// m_pcRABEquippedEntitiesSpaceHash(NULL), + m_pcFloorEntity(NULL), + m_bUseSpaceHash(true), + m_pcRayEmbodiedEntityIntersectionMethod(NULL), + m_ptPhysicsEngines(NULL) { + } + + /****************************************/ + /****************************************/ + + void CSpace::Init(TConfigurationNode& t_tree) + { + /* Get the arena size */ + GetNodeAttribute(t_tree, "size", m_cArenaSize); + + /* Should we use the space hash, and, if so, which one? */ + GetNodeAttributeOrDefault(t_tree, "hashing", m_bUseSpaceHash, true); + if(m_bUseSpaceHash) { + m_pcRayEmbodiedEntityIntersectionMethod = new CRayEmbodiedEntityIntersectionSpaceHash(*this); + m_pcEmbodiedEntitiesSpaceHash = new CSpaceHashNative; +// m_pcLEDEntitiesSpaceHash = new CSpaceHashNative; +// m_pcRABEquippedEntitiesSpaceHash = new CSpaceHashNative; + } + else { + LOG << "[INFO] Space hashing is off." << std::endl; + m_pcRayEmbodiedEntityIntersectionMethod = new CRayEmbodiedEntityIntersectionEntitySweep(*this); + } + + /* Add and initialize all entities in XML */ + + /* Start from the entities placed manually */ + TConfigurationNodeIterator itArenaItem; + for(itArenaItem = itArenaItem.begin(&t_tree); + itArenaItem != itArenaItem.end(); + ++itArenaItem) { + if(itArenaItem->Value() != "distribute" && itArenaItem->Value() != "box_strip") { + CEntity* pcEntity = CFactory::New(itArenaItem->Value()); + pcEntity->Init(*itArenaItem); + AddEntity(*pcEntity); + } + } + + /* Place box strips */ + for(itArenaItem = itArenaItem.begin(&t_tree); + itArenaItem != itArenaItem.end(); + ++itArenaItem) { + if(itArenaItem->Value() == "box_strip") { + AddBoxStrip(*itArenaItem); + } + } + + /* Place the entities to distribute automatically */ + for(itArenaItem = itArenaItem.begin(&t_tree); + itArenaItem != itArenaItem.end(); + ++itArenaItem) { + if(itArenaItem->Value() == "distribute") { + Distribute(*itArenaItem); + } + } + + if(IsUsingSpaceHash()) { + /* Initialize the space hash */ + /* TODO make this automatic, using the average bb size */ + size_t unBuckets; + CVector3 cCellSize; + GetNodeAttributeOrDefault(t_tree, "embodied_entity_space_hash_buckets", unBuckets, 100000u); + GetNodeAttributeOrDefault(t_tree, "embodied_entity_space_hash_cell_size", cCellSize, CVector3(0.2, 0.2, 0.3)); + m_pcEmbodiedEntitiesSpaceHash->SetSize(unBuckets); + m_pcEmbodiedEntitiesSpaceHash->SetCellSize(cCellSize); + LOG << "[INFO] Embodied entity space hash: " << unBuckets << " buckets, cell size = <" << cCellSize << ">." << std::endl; +// GetNodeAttributeOrDefault(t_tree, "led_entity_space_hash_buckets", unBuckets, 100000u); +// GetNodeAttributeOrDefault(t_tree, "led_entity_space_hash_cell_size", cCellSize, CVector3(0.2, 0.2, 0.3)); +// m_pcLEDEntitiesSpaceHash->SetSize(unBuckets); +// m_pcLEDEntitiesSpaceHash->SetCellSize(cCellSize); +// LOG << "[INFO] LED entity space hash: " << unBuckets << " buckets, cell size = <" << cCellSize << ">." << std::endl; +// GetNodeAttributeOrDefault(t_tree, "rab_equipped_entity_space_hash_buckets", unBuckets, 100000u); +// GetNodeAttributeOrDefault(t_tree, "rab_equipped_entity_space_hash_cell_size", cCellSize, CVector3(1, 1, 1)); +// m_pcRABEquippedEntitiesSpaceHash->SetSize(unBuckets); +// m_pcRABEquippedEntitiesSpaceHash->SetCellSize(cCellSize); +// LOG << "[INFO] RAB equipped entity space hash: " << unBuckets << " buckets, cell size = <" << cCellSize << ">." << std::endl; + } + } + + /****************************************/ + /****************************************/ + + void CSpace::Reset() { + /* Reset the simulation clock */ + m_unSimulationClock = 0; + /* Reset the entities */ + for(UInt32 i = 0; i < m_vecEntities.size(); ++i) { + m_vecEntities[i]->Reset(); + } + /* Reset the space hash */ + UpdateSpaceData(); + } + + /****************************************/ + /****************************************/ + + void CSpace::Destroy() { + /* Remove all entities */ + while(!m_vecEntities.empty()) { + m_vecEntities.back()->Destroy(); + delete m_vecEntities.back(); + m_vecEntities.pop_back(); + } + /* Get rid of the ray-embodied entity intersection method */ + delete m_pcRayEmbodiedEntityIntersectionMethod; + /* Get rid of the space hashes, if used */ + delete m_pcEmbodiedEntitiesSpaceHash; +// delete m_pcLEDEntitiesSpaceHash; +// delete m_pcRABEquippedEntitiesSpaceHash; + } + + /****************************************/ + /****************************************/ + + void CSpace::GetEntitiesMatching(TEntityVector& t_buffer, + const std::string& str_pattern) { + for(TEntityVector::iterator it = m_vecEntities.begin(); + it != m_vecEntities.end(); ++it) { + if(MatchPattern((*it)->GetId(), str_pattern)) { + t_buffer.push_back(*it); + } + } + } + + /****************************************/ + /****************************************/ + + static void CleanupFoundEntities(TEmbodiedEntitySet& set_found_entities, + const TEmbodiedEntitySet& set_ignored_entities) { + if(set_ignored_entities.size() == 1) { + set_found_entities.erase(*set_ignored_entities.begin()); + } + else { + for(TEmbodiedEntitySet::const_iterator it = set_ignored_entities.begin(); + it != set_ignored_entities.end(); ++it) { + set_found_entities.erase(*it); + } + } + } + + /****************************************/ + /****************************************/ + + static bool CalculateClosestCollision(CSpace::SEntityIntersectionItem& s_data, + const CRay& c_ray, + const TEmbodiedEntitySet& set_found_entities) { + /* Look for the first entity that actually intersects the ray */ + Real fLowestTSoFar = 0.0f; + TEmbodiedEntitySet::const_iterator itStart = set_found_entities.begin(); + while(itStart != set_found_entities.end() && + ! (*itStart)->CheckIntersectionWithRay(fLowestTSoFar, c_ray)) { + ++itStart; + } + if(itStart != set_found_entities.end()) { + /* The first entity that intersects the ray has been found */ + CEmbodiedEntity* pcClosestEntitySoFar = *itStart; + /* Go through the remaining entities */ + Real fTmpT; + for(TEmbodiedEntitySet::const_iterator it = itStart; + it != set_found_entities.end(); ++it) { + if((*it)->CheckIntersectionWithRay(fTmpT, c_ray)) { + /* Intersection detected, check if it's the closest */ + if(fTmpT < fLowestTSoFar) { + fLowestTSoFar = fTmpT; + pcClosestEntitySoFar = *it; + } + } + } + /* Copy the data about the closest intersection */ + s_data.IntersectedEntity = pcClosestEntitySoFar; + s_data.TOnRay = fLowestTSoFar; + /* Return true to mean an intersection occurred */ + return true; + } + /* No intersection found */ + return false; + } + + /****************************************/ + /****************************************/ + + bool CSpace::GetClosestEmbodiedEntityIntersectedByRay(CSpace::SEntityIntersectionItem& s_data, + const CRay& c_ray, + const TEmbodiedEntitySet& set_ignored_entities) { + return (*m_pcRayEmbodiedEntityIntersectionMethod)(s_data, c_ray, set_ignored_entities); + } + + /****************************************/ + /****************************************/ + + void CSpace::Update() { + /* Update space-related data */ + UpdateSpaceData(); + /* Update the controllable entities */ + UpdateControllableEntities(); + /* Update the physics engines */ + UpdatePhysics(); + /* Update medium entities */ + UpdateMediumEntities(); + } + + /****************************************/ + /****************************************/ + + void CSpace::UpdateMediumEntities() { + for(size_t i = 0; i < m_vecMediumEntities.size(); ++i) { + m_vecMediumEntities[i]->Update(); + } + } + + /****************************************/ + /****************************************/ + + void CSpace::AddEntity(CEntity& c_entity) { + /* Check that the id of the entity is not already present */ + if(m_mapEntities.find(c_entity.GetId()) != m_mapEntities.end()) { + THROW_ARGOSEXCEPTION("Error inserting a " << c_entity.GetTypeDescription() << " entity with id \"" << c_entity.GetId() << "\". An entity with that id exists already."); + } + /* Add the entity to the indexes */ + m_vecEntities.push_back(&c_entity); + m_mapEntities[c_entity.GetId()] = &c_entity; + + /* creating the visitor and adding the entity to the map */ + /* TODO */ + // CSpaceVisitorAdd cVisitor(*this); + // c_entity.Accept(cVisitor); + } + + /****************************************/ + /****************************************/ + + void CSpace::AddControllableEntity(CControllableEntity& c_entity) { + m_vecControllableEntities.push_back(&c_entity); + } + + /****************************************/ + /****************************************/ + + void CSpace::AddMediumEntity(CMediumEntity& c_entity) { + m_vecMediumEntities.push_back(&c_entity); + } + + /****************************************/ + /****************************************/ + + void CSpace::RemoveEntity(CEntity& c_entity) { + TEntityVector::iterator itEntityVec = find(m_vecEntities.begin(), + m_vecEntities.end(), + &c_entity); + TEntityMap::iterator itEntityMap = m_mapEntities.find(c_entity.GetId()); + if(itEntityVec == m_vecEntities.end()) { + THROW_ARGOSEXCEPTION("CSceneGraph::RemoveEntity() : Entity \"" << + c_entity.GetId() << + "\" to remove has not been found in m_vecEntities."); + } + if(itEntityMap == m_mapEntities.end()) { + THROW_ARGOSEXCEPTION("CSceneGraph::RemoveEntity() : Entity \"" << + c_entity.GetId() << + "\" to remove has not been found in m_mapEntities."); + } + + /* Remove the entity from the vector */ + m_vecEntities.erase(itEntityVec); + + /* Remove the entity from the map */ + m_mapEntities.erase(itEntityMap); + + /* creating the visitor and removing the entity from the map */ + /* TODO */ + // CSpaceVisitorRemove cVisitor(*this); + // c_entity.Accept(cVisitor); + } + + /****************************************/ + /****************************************/ + + void CSpace::RemoveControllableEntity(CControllableEntity& c_entity) { + TControllableEntityVector::iterator it = find(m_vecControllableEntities.begin(), + m_vecControllableEntities.end(), + &c_entity); + m_vecControllableEntities.erase(it); + } + + /****************************************/ + /****************************************/ + + void CSpace::RemoveMediumEntity(CMediumEntity& c_entity) { + TMediumEntityVector::iterator it = find(m_vecMediumEntities.begin(), + m_vecMediumEntities.end(), + &c_entity); + m_vecMediumEntities.erase(it); + } + + /****************************************/ + /****************************************/ + + class RealNumberGenerator { + public: + virtual CVector3 operator()(bool b_is_retry) = 0; + }; + + class ConstantGenerator : public RealNumberGenerator { + public: + ConstantGenerator(const CVector3& c_value) : + m_cValue(c_value) {} + + inline virtual CVector3 operator()(bool b_is_retry) { + return m_cValue; + } + private: + CVector3 m_cValue; + + }; + + class UniformGenerator : public RealNumberGenerator { + public: + UniformGenerator(const CVector3& c_min, + const CVector3& c_max) : + m_cMin(c_min), + m_cMax(c_max) {} + inline virtual CVector3 operator()(bool b_is_retry) { + Real fRandX = + m_cMax.GetX() > m_cMin.GetX() ? + CSimulator::GetInstance().GetRNG()->Uniform(CRange(m_cMin.GetX(), m_cMax.GetX())) : + m_cMax.GetX(); + Real fRandY = + m_cMax.GetY() > m_cMin.GetY() ? + CSimulator::GetInstance().GetRNG()->Uniform(CRange(m_cMin.GetY(), m_cMax.GetY())) : + m_cMax.GetY(); + Real fRandZ = + m_cMax.GetZ() > m_cMin.GetZ() ? + CSimulator::GetInstance().GetRNG()->Uniform(CRange(m_cMin.GetZ(), m_cMax.GetZ())) : + m_cMax.GetZ(); + return CVector3(fRandX, fRandY, fRandZ); + } + private: + CVector3 m_cMin; + CVector3 m_cMax; + }; + + class GaussianGenerator : public RealNumberGenerator { + public: + GaussianGenerator(const CVector3& c_mean, + const CVector3& c_std_dev) : + m_cMean(c_mean), + m_cStdDev(c_std_dev) {} + inline virtual CVector3 operator()(bool b_is_retry) { + return CVector3(CSimulator::GetInstance().GetRNG()->Gaussian(m_cStdDev.GetX(), m_cMean.GetX()), + CSimulator::GetInstance().GetRNG()->Gaussian(m_cStdDev.GetY(), m_cMean.GetY()), + CSimulator::GetInstance().GetRNG()->Gaussian(m_cStdDev.GetZ(), m_cMean.GetZ())); + } + private: + CVector3 m_cMean; + CVector3 m_cStdDev; + }; + + class GridGenerator : public RealNumberGenerator { + public: + GridGenerator(const CVector3 c_center, + const UInt32 un_layout[], + const CVector3 c_distances): + m_cCenter(c_center), + m_cDistances(c_distances), + m_unNumEntityPlaced(0) { + + m_unLayout[0] = un_layout[0]; + m_unLayout[1] = un_layout[1]; + m_unLayout[2] = un_layout[2]; + /* Check if layout is sane */ + if( m_unLayout[0] == 0 || m_unLayout[1] == 0 || m_unLayout[2] == 0 ) { + THROW_ARGOSEXCEPTION("'layout' values (distribute position, method 'grid') must all be different than 0"); + } + + } + + virtual CVector3 operator()(bool b_is_retry) { + if(b_is_retry) { + THROW_ARGOSEXCEPTION("Impossible to place entity #" << m_unNumEntityPlaced << " in grid"); + } + + CVector3 cReturn; + + if(m_unNumEntityPlaced < m_unLayout[0] * m_unLayout[1] * m_unLayout[2]) { + cReturn.SetX( m_cCenter.GetX() + ( m_unLayout[0] - 1 ) * m_cDistances.GetX() * 0.5 - ( m_unNumEntityPlaced % m_unLayout[0] ) * m_cDistances.GetX()); + cReturn.SetY( m_cCenter.GetY() + ( m_unLayout[1] - 1 ) * m_cDistances.GetY() * 0.5 - ( m_unNumEntityPlaced / m_unLayout[0] ) % m_unLayout[1] * m_cDistances.GetY()); + cReturn.SetZ( m_cCenter.GetZ() + ( m_unLayout[2] - 1 ) * m_cDistances.GetZ() * 0.5 - ( m_unNumEntityPlaced / ( m_unLayout[0] * m_unLayout[1] ) ) * m_cDistances.GetZ()); + m_unNumEntityPlaced++; + } + else { + THROW_ARGOSEXCEPTION("Distribute position, method 'grid': trying to place more entities than allowed " + "by the 'layout', check your 'quantity' tag"); + } + + return cReturn; + } + + private: + CVector3 m_cCenter; + UInt32 m_unLayout[3]; + CVector3 m_cDistances; + UInt32 m_unNumEntityPlaced; + }; + + /****************************************/ + /****************************************/ + + RealNumberGenerator* CreateGenerator(TConfigurationNode& t_tree) { + std::string strMethod; + GetNodeAttribute(t_tree, "method", strMethod); + if(strMethod == "uniform") { + CVector3 cMin, cMax; + GetNodeAttribute(t_tree, "min", cMin); + GetNodeAttribute(t_tree, "max", cMax); + if(! (cMin <= cMax)) { + THROW_ARGOSEXCEPTION("Uniform generator: the min is not less than or equal to max: " << cMin << " / " << cMax); + } + return new UniformGenerator(cMin, cMax); + } + else if(strMethod == "gaussian") { + CVector3 cMean, cStdDev; + GetNodeAttribute(t_tree, "mean", cMean); + GetNodeAttribute(t_tree, "std_dev", cStdDev); + return new GaussianGenerator(cMean, cStdDev); + } + else if(strMethod == "constant") { + CVector3 cValues; + GetNodeAttribute(t_tree, "values", cValues); + return new ConstantGenerator(cValues); + } + else if(strMethod == "grid") { + CVector3 cCenter,cDistances; + GetNodeAttribute(t_tree, "center", cCenter); + GetNodeAttribute(t_tree, "distances", cDistances); + UInt32 unLayout[3]; + std::string strLayout; + GetNodeAttribute(t_tree, "layout", strLayout); + ParseValues (strLayout, 3, unLayout, ','); + return new GridGenerator(cCenter, unLayout, cDistances); + } + else { + THROW_ARGOSEXCEPTION("Unknown distribution method \"" << strMethod << "\""); + } + } + + /****************************************/ + /****************************************/ + + static CEmbodiedEntity* GetEmbodiedEntity(CEntity* pc_entity) { + /* Is the entity embodied itself? */ + CEmbodiedEntity* pcEmbodiedTest = dynamic_cast(pc_entity); + if(pcEmbodiedTest != NULL) { + return pcEmbodiedTest; + } + /* Is the entity composable with an embodied component? */ + CComposableEntity* pcComposableTest = dynamic_cast(pc_entity); + if(pcComposableTest != NULL) { + if(pcComposableTest->HasComponent("embodied_entity")) { + return &(pcComposableTest->GetComponent("embodied_entity")); + } + } + /* No embodied entity found */ + return NULL; + } + + /****************************************/ + /****************************************/ + + static bool IsEntityColliding(std::tr1::unordered_set& set_entities, + CEmbodiedEntity* pc_entity) { + /* We check for collisions just by checking bounding box intersection. + It's not very precise, but precision does not matter so much when scattering robots + and, besides, this keeps the architecture clean and simple. + When the arena is first initialized, physics engines do not exist yet, so no + precise collision can be checked. If we want to check at physics level, we should + have a two-phase initialization. Maybe in another life. + This method checks all embodied entities for intersection. It's definitely not + optimized, but since it's executed only at init time, nobody really cares. + */ + for(std::tr1::unordered_set::const_iterator it = set_entities.begin(); + it != set_entities.end(); ++it) { + if((*it)->GetBoundingBox().Intersects(pc_entity->GetBoundingBox())) + return true; + } + return false; + } + + /****************************************/ + /****************************************/ + + void CSpace::Distribute(TConfigurationNode& t_tree) { + /* Get the needed nodes */ + TConfigurationNode cPositionNode; + cPositionNode = GetNode(t_tree, "position"); + TConfigurationNode cOrientationNode; + cOrientationNode = GetNode(t_tree, "orientation"); + TConfigurationNode cEntityNode; + cEntityNode = GetNode(t_tree, "entity"); + /* Create the real number generators */ + RealNumberGenerator* pcPositionGenerator = CreateGenerator(cPositionNode); + RealNumberGenerator* pcOrientationGenerator = CreateGenerator(cOrientationNode); + /* How many entities? */ + UInt32 unQuantity; + GetNodeAttribute(cEntityNode, "quantity", unQuantity); + /* How many trials before failing? */ + UInt32 unMaxTrials; + GetNodeAttribute(cEntityNode, "max_trials", unMaxTrials); + /* Get the (optional) entity base numbering */ + UInt64 unBaseNum = 0; + GetNodeAttributeOrDefault(cEntityNode, "base_num", unBaseNum, unBaseNum); + /* Get the entity type to add (take only the first, ignore additional if any) */ + TConfigurationNodeIterator itEntity; + itEntity = itEntity.begin(&cEntityNode); + if(itEntity == itEntity.end()) { + THROW_ARGOSEXCEPTION("No entity to distribute specified."); + } + /* Get the entity base ID */ + std::string strBaseId; + GetNodeAttribute(*itEntity, "id", strBaseId); + /* Add the requested entities */ + for(UInt32 i = 0; i < unQuantity; ++i) { + /* Copy the entity XML tree */ + TConfigurationNode tEntityTree = *itEntity; + /* Set progressive ID */ + SetNodeAttribute(tEntityTree, "id", strBaseId + "_" + ToString(i+unBaseNum)); + /* Go on until the entity is placed with no collisions or + the max number of trials has been exceeded */ + UInt32 unTrials = 0; + bool bDone = false; + bool bRetry = false; + CEntity* pcEntity; + do { + /* Create entity */ + pcEntity = CFactory::New(tEntityTree.Value()); + /* Set the position */ + SetNodeAttribute(tEntityTree, "position", (*pcPositionGenerator)(bRetry)); + /* Set the orientation */ + SetNodeAttribute(tEntityTree, "orientation", (*pcOrientationGenerator)(bRetry)); + /* Init the entity */ + pcEntity->Init(tEntityTree); + /* Get its embodied component */ + CEmbodiedEntity* pcEmbodiedEntity = GetEmbodiedEntity(pcEntity); + if(pcEmbodiedEntity == NULL) { + THROW_ARGOSEXCEPTION("Cannot distribute entities that are not embodied, and \"" << tEntityTree.Value() << "\" is not."); + } + /* Check if it's colliding with anything else */ + if(IsEntityColliding(m_pcEmbodiedEntitiesSpaceHash->GetElements(), pcEmbodiedEntity)) { + /* Set retry to true */ + bRetry = true; + /* Get rid of the entity */ + pcEntity->Destroy(); + delete pcEntity; + /* Increase the trial count */ + ++unTrials; + /* Too many trials? */ + if(unTrials > unMaxTrials) { + /* Yes, bomb out */ + THROW_ARGOSEXCEPTION("Exceeded max trials when trying to distribute objects of type " << + tEntityTree.Value() << " with base id \"" << + strBaseId << "\". I managed to place only " << i << " objects."); + } + /* Retry with a new position */ + } + else { + /* No collision, we're done with this entity */ + bDone = true; + } + } + while(!bDone); + /* Add entity */ + AddEntity(*pcEntity); + } + /* Delete the generators, now unneeded */ + delete pcPositionGenerator; + delete pcOrientationGenerator; + } + + /****************************************/ + /****************************************/ + + void CSpace::AddBoxStrip(TConfigurationNode& t_tree){ + + // this function can convert a inkscape path into a box strip + // in inkscape: preference -> svg output -> deny relative coordinates + + // read parameters + std::string strBaseId; + GetNodeAttribute(t_tree, "id", strBaseId); + std::string strCoordinates; + GetNodeAttribute(t_tree, "coordinates", strCoordinates); + Real fScale; + GetNodeAttributeOrDefault(t_tree, "scale", fScale, 1.0f); + Real fBoxWidth; + GetNodeAttribute(t_tree, "boxwidth", fBoxWidth); + Real fBoxHeight; + GetNodeAttribute(t_tree, "boxheight", fBoxHeight); + CVector2 cTranslate; + GetNodeAttributeOrDefault(t_tree, "translate", cTranslate, CVector2(0,0)); + bool bClose; + GetNodeAttributeOrDefault(t_tree, "close", bClose, false); + bool bRoundEdges; + GetNodeAttributeOrDefault(t_tree, "roundedge", bRoundEdges, true); + + std::vector vecCoordinates; + Tokenize(strCoordinates, vecCoordinates,", "); + + CVector2 cA, cB, cC; + + for (UInt32 i = 0; i < vecCoordinates.size() - (bClose ? 0 : 2); i += 2) { + + // read the next three points in strip + cA.Set(FromString(vecCoordinates[i]), FromString(vecCoordinates[i+1])); + cA = cA * fScale + cTranslate; + cB.Set(FromString(vecCoordinates[(i+2) % vecCoordinates.size()]), FromString(vecCoordinates[(i+3) % vecCoordinates.size()])); + cB = cB * fScale + cTranslate; + cC.Set(FromString(vecCoordinates[(i+4) % vecCoordinates.size()]), FromString(vecCoordinates[(i+5) % vecCoordinates.size()])); + cC = cC * fScale + cTranslate; + + CEntity* pcEntity = CFactory::New("box"); + + CVector3 cOrientationAngles(ToDegrees((cB-cA).Angle()).GetValue(), 0., 0.); + /* Build the XML tree */ + TConfigurationNode tRootNode("box"); + SetNodeAttribute(tRootNode, "id", strBaseId+ "_" + ToString(i)); + SetNodeAttribute(tRootNode, "position", CVector3(((cA+cB)/2.).GetX(), ((cA+cB)/2.).GetY(), 0.)); + SetNodeAttribute(tRootNode, "orientation", cOrientationAngles); + SetNodeAttribute(tRootNode, "size", CVector3((cA-cB).Length(), fBoxWidth, fBoxHeight)); + SetNodeAttribute(tRootNode, "movable", false); + SetNodeAttribute(tRootNode, "mass", 1); + pcEntity->Init(tRootNode); + AddEntity(*pcEntity); + + if (bRoundEdges && (bClose || i < vecCoordinates.size()-4)){ + + pcEntity = CFactory::New("box"); + + cOrientationAngles.SetX(ToDegrees((cB-cA).Angle()+(cB-cC).Angle()).GetValue() / 2.); + /* Build the XML tree */ + SetNodeAttribute(tRootNode, "id", strBaseId+ "_" + ToString(i + vecCoordinates.size())); + SetNodeAttribute(tRootNode, "orientation", cOrientationAngles); + + CVector2 cPerp; + cPerp.SetFromAngleAndLength((cB-cA).Angle() + CRadians::PI_OVER_TWO, fBoxWidth/2); + cPerp += cB; + + CVector2 cPerp2; + cPerp2.SetFromAngleAndLength((cB-cC).Angle() - CRadians::PI_OVER_TWO, fBoxWidth/2); + cPerp2 += cB; + + SetNodeAttribute(tRootNode, "position", CVector3(cB.GetX(), cB.GetY(), 0.)); + SetNodeAttribute(tRootNode, "size", CVector3(sqrt(fBoxWidth*fBoxWidth - Distance(cPerp,cPerp2)*Distance(cPerp,cPerp2)),Distance(cPerp,cPerp2), fBoxHeight)); + + pcEntity->Init(tRootNode); + AddEntity(*pcEntity); + } + } + } + + /****************************************/ + /****************************************/ + + bool CSpace::GetClosestEmbodiedEntityIntersectedByRaySpaceHash(SEntityIntersectionItem& s_data, + const CRay& c_ray, + const TEmbodiedEntitySet& set_ignored_entities) { + /* Buffer for embodied entities found along the ray */ + TEmbodiedEntitySet tFoundEntities; + /* Transform ray start and end position into cell coordinates */ + SInt32 nI1, nJ1, nK1, nI2, nJ2, nK2; + m_pcEmbodiedEntitiesSpaceHash->SpaceToHashTable(nI1, nJ1, nK1, c_ray.GetStart()); + m_pcEmbodiedEntitiesSpaceHash->SpaceToHashTable(nI2, nJ2, nK2, c_ray.GetEnd()); + /* Go through cells one by one, from start to end. + Stop as soon as an entity is found. + If the loop is completed, it means no entities were found -> no intersection. + */ + /* Calculate deltas for later use */ + SInt32 nDI(Abs(nI2 - nI1)); + SInt32 nDJ(Abs(nJ2 - nJ1)); + SInt32 nDK(Abs(nK2 - nK1)); + /* Calculate the increment for each direction */ + SInt32 nSI(nI2 >= nI1 ? 1 : -1); + SInt32 nSJ(nJ2 >= nJ1 ? 1 : -1); + SInt32 nSK(nK2 >= nK1 ? 1 : -1); + /* Set the starting cell */ + SInt32 nI(nI1), nJ(nJ1), nK(nK1); + if(nDI >= nDJ && nDI >= nDK) { + /* I is the driving axis */ + /* Calculate error used to know when to move on other axes */ + SInt32 nEJ(3 * nDJ - nDI); + SInt32 nEK(3 * nDK - nDI); + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + /* Check if the found entities have an intersection. + If so, we can stop. Otherwise, we continue. */ + if(! set_ignored_entities.empty()) { + CleanupFoundEntities(tFoundEntities, set_ignored_entities); + } + if(! tFoundEntities.empty() && + CalculateClosestCollision(s_data, + c_ray, + tFoundEntities)) { + return true; + } + /* Cycle through cells */ + for(SInt32 nCell = nDI; nCell > 0; --nCell) { + /* Clean up the found entities */ + tFoundEntities.clear(); + /* Advance on driving axis */ + nI += nSI; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + /* Advance on other axes, if necessary */ + if(nEJ > 0 && nEK > 0) { + /* Advance on both the other axes */ + if(nEJ * nDK > nEK * nDJ) { + nJ += nSJ; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + nK += nSK; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + } + else { + nK += nSK; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + nJ += nSJ; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + } + nEJ += 2 * (nDJ - nDI); + nEK += 2 * (nDK - nDI); + } + else if(nEJ > 0) { + /* Advance only on J */ + nJ += nSJ; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + nEJ += 2 * (nDJ - nDI); + nEK += 2 * nDK; + } + else { + nEJ += 2 * nDJ; + if(nEK > 0) { + /* Advance only on K */ + nK += nSK; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + nEK += 2 * (nDK - nDI); + } + else { + nEK += 2 * nDK; + } + } + /* Check if the found entities have an intersection. + If so, we can stop. Otherwise, we continue. */ + if(! set_ignored_entities.empty()) { + CleanupFoundEntities(tFoundEntities, set_ignored_entities); + } + if(! tFoundEntities.empty() && + CalculateClosestCollision(s_data, + c_ray, + tFoundEntities)) { + return true; + } + } + } + else if(nDJ >= nDI && nDJ >= nDK) { + /* J is the driving axis */ + /* Calculate error used to know when to move on other axes */ + SInt32 nEI(3 * nDI - nDJ); + SInt32 nEK(3 * nDK - nDJ); + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + /* Check if the found entities have an intersection. + If so, we can stop. Otherwise, we continue. */ + if(! set_ignored_entities.empty()) { + CleanupFoundEntities(tFoundEntities, set_ignored_entities); + } + if(! tFoundEntities.empty() && + CalculateClosestCollision(s_data, + c_ray, + tFoundEntities)) { + return true; + } + /* Cycle through cells */ + for(SInt32 nCell = nDJ; nCell > 0; --nCell) { + /* Clean up the found entities */ + tFoundEntities.clear(); + /* Advance on driving axis */ + nJ += nSJ; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + /* Advance on other axes, if necessary */ + if(nEI > 0 && nEK > 0) { + /* Advance on both the other axes */ + if(nEI * nDK > nEK * nDI) { + nI += nSI; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + nK += nSK; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + } + else { + nK += nSK; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + nI += nSI; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + } + nEI += 2 * (nDI - nDJ); + nEK += 2 * (nDK - nDJ); + } + else if(nEI > 0) { + /* Advance only on I */ + nI += nSI; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + nEI += 2 * (nDI - nDJ); + nEK += 2 * nDK; + } + else { + nEI += 2 * nDI; + if(nEK > 0) { + /* Advance only on K */ + nK += nSK; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + nEK += 2 * (nDK - nDJ); + } + else { + nEK += 2 * nDK; + } + } + /* Check if the found entities have an intersection. + If so, we can stop. Otherwise, we continue. */ + if(! set_ignored_entities.empty()) { + CleanupFoundEntities(tFoundEntities, set_ignored_entities); + } + if(! tFoundEntities.empty() && + CalculateClosestCollision(s_data, + c_ray, + tFoundEntities)) { + return true; + } + } + } + else { + /* K is the driving axis */ + /* Calculate error used to know when to move on other axes */ + SInt32 nEI(3 * nDI - nDK); + SInt32 nEJ(3 * nDJ - nDK); + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + /* Check if the found entities have an intersection. + If so, we can stop. Otherwise, we continue. */ + if(! set_ignored_entities.empty()) { + CleanupFoundEntities(tFoundEntities, set_ignored_entities); + } + if(! tFoundEntities.empty() && + CalculateClosestCollision(s_data, + c_ray, + tFoundEntities)) { + return true; + } + /* Cycle through cells */ + for(SInt32 nCell = nDK; nCell > 0; --nCell) { + /* Clean up the found entities */ + tFoundEntities.clear(); + /* Advance on driving axis */ + nK += nSK; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + /* Advance on other axes, if necessary */ + if(nEI > 0 && nEJ > 0) { + /* Advance on both the other axes */ + if(nEI * nDJ > nEJ * nDI) { + nI += nSI; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + nJ += nSJ; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + } + else { + nJ += nSJ; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + nI += nSI; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + } + nEI += 2 * (nDI - nDK); + nEJ += 2 * (nDJ - nDK); + } + else if(nEI > 0) { + /* Advance only on I */ + nI += nSI; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + nEI += 2 * (nDI - nDK); + nEJ += 2 * nDJ; + } + else { + nEI += 2 * nDI; + if(nEJ > 0) { + /* Advance only on J */ + nJ += nSJ; + m_pcEmbodiedEntitiesSpaceHash->CheckCell(nI, nJ, nK, tFoundEntities); + nEJ += 2 * (nDJ - nDK); + } + else { + nEJ += 2 * nDJ; + } + } + /* Check if the found entities have an intersection. + If so, we can stop. Otherwise, we continue. */ + if(! set_ignored_entities.empty()) { + CleanupFoundEntities(tFoundEntities, set_ignored_entities); + } + if(! tFoundEntities.empty() && + CalculateClosestCollision(s_data, + c_ray, + tFoundEntities)) { + return true; + } + } + } + /* End of the loop reached and no entities found. */ + return false; + } + + /****************************************/ + /****************************************/ + + bool CSpace::GetClosestEmbodiedEntityIntersectedByRayEntitySweep(SEntityIntersectionItem& s_data, + const CRay& c_ray, + const TEmbodiedEntitySet& set_ignored_entities) { + return false; + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/simulator/space/space.h b/src/core/simulator/space/space.h new file mode 100644 index 00000000..c99e2b38 --- /dev/null +++ b/src/core/simulator/space/space.h @@ -0,0 +1,283 @@ +/** + * @file argos3/core/simulator/space/space.h + * + * @brief This file provides the definition of the space. + * + * @author Carlo Pinciroli - + */ + +#ifndef SPACE_H +#define SPACE_H + +namespace argos { + class CSpace; + class CRay; + class CFloorEntity; +} + +#include +#include +#include +#include +#include +#include +#include +//#include +//#include + +namespace argos { + + class CSpace : public CBaseConfigurableResource { + + public: + + /** Maps for quick access to physical entities (robots, objects) */ + typedef std::map > TAnyEntityMap; + typedef std::map > TMapOfAnyEntityMaps; + + template struct SEntityIntersectionItem { + E* IntersectedEntity; + Real TOnRay; + + SEntityIntersectionItem() : + IntersectedEntity(NULL), + TOnRay(0.0f) {} + + SEntityIntersectionItem(E* pc_entity, Real f_t_on_ray) : + IntersectedEntity(pc_entity), + TOnRay(f_t_on_ray) {} + + inline bool operator<(const SEntityIntersectionItem& s_item) { + return TOnRay < s_item.TOnRay; + } + }; + + template struct SEntityIntersectionData { + bool Intersection; + std::vector*> IntersectedEntities; + + SEntityIntersectionData() : + Intersection(false) {} + + SEntityIntersectionData(std::vector*>& c_entities) : + Intersection(c_entities.size() > 0), + IntersectedEntities(c_entities) {} + }; + + protected: + + class CRayEmbodiedEntityIntersectionMethod; + class CRayEmbodiedEntityIntersectionSpaceHash; + class CRayEmbodiedEntityIntersectionEntitySweep; + + /****************************************/ + /****************************************/ + + public: + + CSpace(); + virtual ~CSpace() {} + + virtual void Init(TConfigurationNode& t_tree); + virtual void Reset(); + virtual void Destroy(); + + inline UInt32 GetNumberEntities() const { + return m_vecEntities.size(); + } + + inline TEntityVector& GetEntityVector() { + return m_vecEntities; + } + + inline CEntity& GetEntity(const std::string& str_id) { + TEntityMap::const_iterator it = m_mapEntities.find(str_id); + if ( it != m_mapEntities.end()) { + return *(it->second); + } + THROW_ARGOSEXCEPTION("Unknown entity id \"" << str_id << + "\" when requesting entity from space."); + } + + void GetEntitiesMatching(TEntityVector& t_buffer, + const std::string& str_pattern); + + bool GetClosestEmbodiedEntityIntersectedByRay(SEntityIntersectionItem& s_data, + const CRay& c_ray, + const TEmbodiedEntitySet& set_ignored_entities = TEmbodiedEntitySet()); + + inline TEntityMap& GetAllEntities() { + return m_mapEntities; + } + + inline TMapOfAnyEntityMaps& GetEntitiesMapOfMaps() { + return m_mapOfMapsEntities; + } + + inline TAnyEntityMap& GetEntitiesByType(const std::string str_type) { + TMapOfAnyEntityMaps::iterator itEntities = m_mapOfMapsEntities.find(str_type); + if (itEntities != m_mapOfMapsEntities.end()){ + return itEntities->second; + } + THROW_ARGOSEXCEPTION("Entity map for type \"" << str_type << "\" not found."); + } + + inline CFloorEntity& GetFloorEntity() { + if(m_pcFloorEntity == NULL) { + THROW_ARGOSEXCEPTION("No floor entity has been added to the space."); + } + return *m_pcFloorEntity; + } + + inline void SetFloorEntity(CFloorEntity& c_floor_entity) { + m_pcFloorEntity = &c_floor_entity; + } + + inline bool IsUsingSpaceHash() const { + return m_bUseSpaceHash; + } + + inline virtual void SetPhysicsEngines(CPhysicsEngine::TVector& t_engines) { + m_ptPhysicsEngines = &t_engines; + } + + virtual void Update(); + + virtual void AddEntity(CEntity& c_entity); + virtual void AddControllableEntity(CControllableEntity& c_entity); + virtual void AddMediumEntity(CMediumEntity& c_entity); + + virtual void RemoveEntity(CEntity& c_entity); + virtual void RemoveControllableEntity(CControllableEntity& c_entity); + virtual void RemoveMediumEntity(CMediumEntity& c_entity); + + inline UInt32 GetSimulationClock() const { + return m_unSimulationClock; + } + + inline void SetSimulationClock(UInt32 un_simulation_clock) { + m_unSimulationClock = un_simulation_clock; + } + + inline void IncreaseSimulationClock(UInt32 un_increase = 1) { + m_unSimulationClock += un_increase; + } + + inline const CVector3& GetArenaSize() const { + return m_cArenaSize; + } + + inline void SetArenaSize(const CVector3& c_size) { + m_cArenaSize = c_size; + } + + inline CSpaceHash& GetEmbodiedEntitiesSpaceHash() { + if(IsUsingSpaceHash()) { + return *m_pcEmbodiedEntitiesSpaceHash; + } + else { + THROW_ARGOSEXCEPTION("Attempted to access the space hash of embodied entities, but in the XML the user chose not to use it. Maybe you use a sensor or an actuator that references it directly?"); + } + } + +/* + inline CSpaceHash& GetLEDEntitiesSpaceHash() { + if(IsUsingSpaceHash()) { + return *m_pcLEDEntitiesSpaceHash; + } + else { + THROW_ARGOSEXCEPTION("Attempted to access the space hash of LED entities, but in the XML the user chose not to use it. Maybe you use a sensor or an actuator that references it directly?"); + } + } +*/ + +/* + inline CSpaceHash& GetRABEquippedEntitiesSpaceHash() { + if(IsUsingSpaceHash()) { + return *m_pcRABEquippedEntitiesSpaceHash; + } + else { + THROW_ARGOSEXCEPTION("Attempted to access the space hash of RAB equipped entities, but in the XML the user chose not to use it. Maybe you use a sensor or an actuator that references it directly?"); + } + } +*/ + + protected: + + inline void UpdateSpaceData() { + if(IsUsingSpaceHash()) { + m_pcEmbodiedEntitiesSpaceHash->Update(); +// m_pcLEDEntitiesSpaceHash->Update(); +// m_pcRABEquippedEntitiesSpaceHash->Update(); + } + } + + virtual void UpdateControllableEntities() = 0; + virtual void UpdatePhysics() = 0; + + void UpdateMediumEntities(); + + void Distribute(TConfigurationNode& t_tree); + + void AddBoxStrip(TConfigurationNode& t_tree); + + bool GetClosestEmbodiedEntityIntersectedByRaySpaceHash(SEntityIntersectionItem& s_data, + const CRay& c_ray, + const TEmbodiedEntitySet& set_ignored_entities); + + bool GetClosestEmbodiedEntityIntersectedByRayEntitySweep(SEntityIntersectionItem& s_data, + const CRay& c_ray, + const TEmbodiedEntitySet& set_ignored_entities); + + protected: + + /** The current simulation clock */ + UInt32 m_unSimulationClock; + + /** Arena size */ + CVector3 m_cArenaSize; + + /** A vector of entities. */ + TEntityVector m_vecEntities; + + /** A map of entities. */ + TEntityMap m_mapEntities; + + /** A map of maps of all the simulated entities. + The map stores several submaps with the key equals to the + type description of an entity. The submaps store the entities + according to their id */ + TMapOfAnyEntityMaps m_mapOfMapsEntities; + + /** The space hash of embodied entities */ + CSpaceHash* m_pcEmbodiedEntitiesSpaceHash; + + /** The space hash of LED entities */ +// CSpaceHash* m_pcLEDEntitiesSpaceHash; + + /** The space hash of RAB equipped entities */ +// CSpaceHash* m_pcRABEquippedEntitiesSpaceHash; + + /** A vector of controllable entities */ + TControllableEntityVector m_vecControllableEntities; + + /** A vector of medium entities */ + TMediumEntityVector m_vecMediumEntities; + + /** The floor entity */ + CFloorEntity* m_pcFloorEntity; + + /** True if the space hash should be used */ + bool m_bUseSpaceHash; + + /** Method to calculate the ray-embodied entity intersection */ + CRayEmbodiedEntityIntersectionMethod* m_pcRayEmbodiedEntityIntersectionMethod; + + /** A reference to the list of physics engines */ + CPhysicsEngine::TVector* m_ptPhysicsEngines; + + }; + +} + +#endif diff --git a/src/core/simulator/space/space_hash.h b/src/core/simulator/space/space_hash.h new file mode 100644 index 00000000..5bb8074a --- /dev/null +++ b/src/core/simulator/space/space_hash.h @@ -0,0 +1,171 @@ +/** + * @file argos3/simulator/space/space_hash.h + * + * @author Carlo Pinciroli - + */ + +#ifndef SPACE_HASH_H +#define SPACE_HASH_H + +namespace argos { + class CSpace; + class CVector3; + class CRay; +} + +#include +#include +#include +#include + +namespace argos { + + /****************************************/ + /****************************************/ + + template class CAbstractSpaceHash { + + public: + + typedef std::tr1::unordered_set TElementList; + + public: + + CAbstractSpaceHash() : m_unSize(0) {} + + virtual ~CAbstractSpaceHash() {} + + inline virtual void AddElement(Element& c_element) { + m_tElements.insert(&c_element); + } + + inline TElementList& GetElements() { + return m_tElements; + } + + inline void RemoveElement(Element& c_element) { + typename TElementList::iterator it = m_tElements.find(&c_element); + if(it != m_tElements.end()) { + m_tElements.erase(it); + } + else { + THROW_ARGOSEXCEPTION("Element not found when removing it from space hash."); + } + } + + inline size_t GetSize() { + return m_unSize; + } + + inline CVector3& GetCellSize() { + return m_cCellSize; + } + + inline CVector3& GetInvCellSize() { + return m_cInvCellSize; + } + + inline virtual void SetSize(size_t un_size) { + m_unSize = un_size; + } + + inline virtual void SetCellSize(const CVector3& c_cell_size) { + m_cCellSize = c_cell_size; + m_cInvCellSize.Set(1.0f / m_cCellSize.GetX(), + 1.0f / m_cCellSize.GetY(), + 1.0f / m_cCellSize.GetZ()); + } + + virtual void Update() = 0; + + virtual void UpdateCell(SInt32 n_x, + SInt32 n_y, + SInt32 n_z, + Element& c_element) = 0; + + inline virtual SInt32 SpaceToHashTable(Real c_coord, + UInt32 un_axis) { + return RoundClosestToZero(c_coord * GetInvCellSize()[un_axis]); + } + + inline virtual Real HashTableToSpace(SInt32 n_coord, + UInt32 un_axis) { + return n_coord * m_cCellSize[un_axis]; + } + + inline virtual void SpaceToHashTable(SInt32& n_i, + SInt32& n_j, + SInt32& n_k, + const CVector3& c_pos) { + n_i = RoundClosestToZero(c_pos.GetX() * CAbstractSpaceHash::GetInvCellSize().GetX()); + n_j = RoundClosestToZero(c_pos.GetY() * CAbstractSpaceHash::GetInvCellSize().GetY()); + n_k = RoundClosestToZero(c_pos.GetZ() * CAbstractSpaceHash::GetInvCellSize().GetZ()); + } + + virtual bool CheckCell(SInt32 n_i, + SInt32 n_j, + SInt32 n_k, + TElementList& t_elements) = 0; + + protected: + + inline UInt32 CoordinateHash(SInt32 n_i, + SInt32 n_j, + SInt32 n_k) { + return + ((73856093u * n_i) ^ + (19349663u * n_j) ^ + (83492791u * n_k)) % + m_unSize; + } + + private: + + TElementList m_tElements; + size_t m_unSize; + CVector3 m_cCellSize; + CVector3 m_cInvCellSize; + + }; + + /****************************************/ + /****************************************/ + + template class CSpaceHashUpdater { + + public: + + virtual ~CSpaceHashUpdater() {} + + virtual void operator()(CAbstractSpaceHash& c_space_hash, + Element& c_element) = 0; + + }; + + /****************************************/ + /****************************************/ + + template class CSpaceHash : public CAbstractSpaceHash { + + public: + + inline virtual void Update() { + /* Go through all the entities */ + for(typename CAbstractSpaceHash::TElementList::const_iterator el = CAbstractSpaceHash::GetElements().begin(); + el != CAbstractSpaceHash::GetElements().end(); ++el) { + m_cUpdater(*this, **el); + } + } + + private: + + Updater m_cUpdater; + + }; + + /****************************************/ + /****************************************/ + +} + +#endif diff --git a/src/core/simulator/space/space_hash_native.h b/src/core/simulator/space/space_hash_native.h new file mode 100644 index 00000000..e66ef0cf --- /dev/null +++ b/src/core/simulator/space/space_hash_native.h @@ -0,0 +1,188 @@ +/** + * @file argos3/simulator/space/space_hash_native.h + * + * @author Carlo Pinciroli - + */ + +#ifndef SPACE_HASH_NATIVE_H +#define SPACE_HASH_NATIVE_H + +#include + +namespace argos { + + template class CSpaceHashNative : public CSpaceHash { + + private: + + struct SBucket { + + struct SBucketData { + Element* Elem; + SInt32 I,J,K; + SBucketData* Next; + + SBucketData(Element& c_element, + SInt32 n_i, + SInt32 n_j, + SInt32 n_k, + SBucketData* ps_next = NULL) : + Elem(&c_element), + I(n_i), + J(n_j), + K(n_k), + Next(ps_next) {} + + }; + + UInt64 StoreTimestamp; + SBucketData* ElementList; + + SBucket() : + StoreTimestamp(0), + ElementList(NULL) {} + + ~SBucket() { + Clear(); + } + + inline bool Empty() const { + return (ElementList == NULL); + } + + inline void Clear() { + if(!Empty()) { + SBucketData* psCur = ElementList; + SBucketData* psNext = psCur->Next; + do { + delete psCur; + psCur = psNext; + if(psCur) psNext = psCur->Next; + } while(psCur); + ElementList = NULL; + } + } + + inline void Add(Element& c_element, + SInt32 n_i, + SInt32 n_j, + SInt32 n_k) { + if(Empty()) ElementList = new SBucketData(c_element, n_i, n_j, n_k); + else ElementList = new SBucketData(c_element, n_i, n_j, n_k, ElementList); + } + + inline bool Exists(const Element& c_element, + SInt32 n_i, + SInt32 n_j, + SInt32 n_k) { + SBucketData* psCur = ElementList; + while(psCur) { + if(psCur->Elem == &c_element && + psCur->I == n_i && + psCur->J == n_j && + psCur->K == n_k) return true; + psCur = psCur->Next; + } + return false; + } + + }; + + public: + + CSpaceHashNative() : + m_psBuckets(NULL), + m_unCurrentStoreTimestamp(0) {} + + ~CSpaceHashNative() { + Clear(); + delete[] m_psBuckets; + } + + inline void Clear() { + for(size_t i = 0; i < CSpaceHash::GetSize(); ++i) { + m_psBuckets[i].Clear(); + } + } + + inline virtual void SetSize(size_t un_size) { + CSpaceHash::SetSize(un_size); + m_psBuckets = new SBucket[CSpaceHash::GetSize()]; + // TODO: rehash! + } + + inline virtual void Update() { + /* Set the current store time stamp */ + m_unCurrentStoreTimestamp++; + /* Call base class method */ + CSpaceHash::Update(); + } + + inline virtual void UpdateCell(SInt32 n_i, + SInt32 n_j, + SInt32 n_k, + Element& c_element) { + /* Calculate the hash of the current position */ + SInt32 nHash = CSpaceHash::CoordinateHash(n_i, n_j, n_k); + /* Get a reference to the bucket */ + SBucket& sBucket = m_psBuckets[nHash]; + /* Check if the bucket's content is obsolete */ + if(sBucket.StoreTimestamp == m_unCurrentStoreTimestamp) { + /* Add the current element to the bucket */ + if(! sBucket.Exists(c_element, n_i, n_j, n_k)) { + sBucket.Add(c_element, n_i, n_j, n_k); + } + } + else { + /* The bucket's content is obsolete, erase it */ + sBucket.Clear(); + /* Set the store timestamp to the current time */ + sBucket.StoreTimestamp = m_unCurrentStoreTimestamp; + /* Add the current element to the bucket */ + sBucket.Add(c_element, n_i, n_j, n_k); + } + } + + inline virtual bool CheckCell(SInt32 n_i, + SInt32 n_j, + SInt32 n_k, + typename CSpaceHash::TElementList& t_elements) { + /* In the beginning, no new elements have been found */ + bool bNewElements = false; + /* Calculate the hash of the current position */ + SInt32 nHash = CSpaceHash::CoordinateHash(n_i, n_j, n_k); + /* Get a reference to the bucket */ + SBucket& sBucket = m_psBuckets[nHash]; + /* Visit the bucket IF: + 1. its data is up-to-date AND + 2. it is not empty + */ + if((sBucket.StoreTimestamp == m_unCurrentStoreTimestamp) && /* 1. */ + !sBucket.Empty()) /* 2. */ { + /* Check the bucket's elements */ + for(typename SBucket::SBucketData* psCur = sBucket.ElementList; + psCur; + psCur = psCur->Next) { + /* Check that the element is in the wanted cell */ + if(n_i == psCur->I && + n_j == psCur->J && + n_k == psCur->K) { + /* We have a new element to add to the list */ + bNewElements = true; + t_elements.insert(psCur->Elem); + } + } + } + return bNewElements; + } + + private: + + SBucket* m_psBuckets; + UInt64 m_unCurrentStoreTimestamp; + + }; + +} + +#endif diff --git a/src/core/simulator/space/space_multi_thread_h_dispatch.cpp b/src/core/simulator/space/space_multi_thread_h_dispatch.cpp new file mode 100644 index 00000000..566fb309 --- /dev/null +++ b/src/core/simulator/space/space_multi_thread_h_dispatch.cpp @@ -0,0 +1,337 @@ +/** + * @file argos3/simulator/space/space_multi_thread_h_dispatch.cpp + * + * @brief This file provides the implementation of the Swarmanoid 3D Space. + * + * This file provides the implementation of the Swarmanoid 3D Space. + * + * @author Carlo Pinciroli - + */ + +#include "space_multi_thread_h_dispatch.h" +#include +#include + +#include + +namespace argos { + + /****************************************/ + /****************************************/ + + struct SCleanupThreadData { + pthread_mutex_t* StartSenseControlPhaseMutex; + pthread_mutex_t* StartActPhaseMutex; + pthread_mutex_t* StartPhysicsPhaseMutex; + pthread_mutex_t* FetchTaskMutex; + }; + + static void CleanupThread(void* p_data) { + CSimulator& cSimulator = CSimulator::GetInstance(); + if(cSimulator.IsProfiling()) { + cSimulator.GetProfiler().CollectThreadResourceUsage(); + } + SCleanupThreadData& sData = + *reinterpret_cast(p_data); + pthread_mutex_unlock(sData.FetchTaskMutex); + pthread_mutex_unlock(sData.StartSenseControlPhaseMutex); + pthread_mutex_unlock(sData.StartActPhaseMutex); + pthread_mutex_unlock(sData.StartPhysicsPhaseMutex); + } + + void* LaunchThreadHDispatch(void* p_data) { + /* Set up thread-safe buffers for this new thread */ + LOG.AddThreadSafeBuffer(); + LOGERR.AddThreadSafeBuffer(); + /* Make this thread cancellable */ + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + /* Get a handle to the thread launch data */ + CSpaceMultiThreadHDispatch::SThreadLaunchData* psData = reinterpret_cast(p_data); + /* Create cancellation data */ + SCleanupThreadData sCancelData; + sCancelData.StartSenseControlPhaseMutex = &(psData->Space->m_tStartSenseControlPhaseMutex); + sCancelData.StartActPhaseMutex = &(psData->Space->m_tStartActPhaseMutex); + sCancelData.StartPhysicsPhaseMutex = &(psData->Space->m_tStartPhysicsPhaseMutex); + sCancelData.FetchTaskMutex = &(psData->Space->m_tFetchTaskMutex); + pthread_cleanup_push(CleanupThread, &sCancelData); + if(psData->ThreadId == 0) { + /* Execute the code for the dispatch thread */ + psData->Space->DispatchThread(psData->ThreadId); + } + else { + /* Execute the code for a slave thread */ + psData->Space->SlaveThread(psData->ThreadId); + } + /* Dispose of cancellation data */ + pthread_cleanup_pop(1); + return NULL; + } + + /****************************************/ + /****************************************/ + + void CSpaceMultiThreadHDispatch::STaskData::Reset() { + Index = 0; + Used = false; + Done = false; + } + + /****************************************/ + /****************************************/ + + void CSpaceMultiThreadHDispatch::Init(TConfigurationNode& t_tree) { + /* Initialize the space */ + CSpace::Init(t_tree); + + /* Initialize thread related structures */ + int nErrors; + /* Init mutexes */ + if((nErrors = pthread_mutex_init(&m_tStartSenseControlPhaseMutex, NULL)) || + (nErrors = pthread_mutex_init(&m_tStartActPhaseMutex, NULL)) || + (nErrors = pthread_mutex_init(&m_tStartPhysicsPhaseMutex, NULL)) || + (nErrors = pthread_mutex_init(&m_tFetchTaskMutex, NULL))) { + THROW_ARGOSEXCEPTION("Error creating thread mutexes " << ::strerror(nErrors)); + } + /* Init conditionals */ + if((nErrors = pthread_cond_init(&m_tStartSenseControlPhaseCond, NULL)) || + (nErrors = pthread_cond_init(&m_tStartActPhaseCond, NULL)) || + (nErrors = pthread_cond_init(&m_tStartPhysicsPhaseCond, NULL)) || + (nErrors = pthread_cond_init(&m_tFetchTaskCond, NULL))) { + THROW_ARGOSEXCEPTION("Error creating thread conditionals " << ::strerror(nErrors)); + } + + /* Reset the idle thread count */ + m_unSenseControlPhaseIdleCounter = CSimulator::GetInstance().GetNumThreads(); + m_unActPhaseIdleCounter = CSimulator::GetInstance().GetNumThreads(); + m_unPhysicsPhaseIdleCounter = CSimulator::GetInstance().GetNumThreads(); + } + + /****************************************/ + /****************************************/ + + void CSpaceMultiThreadHDispatch::Destroy() { + /* Destroy the threads to update the controllable entities */ + int nErrors; + if(m_ptThreads != NULL) { + for(UInt32 i = 0; i <= CSimulator::GetInstance().GetNumThreads(); ++i) { + if((nErrors = pthread_cancel(m_ptThreads[i]))) { + THROW_ARGOSEXCEPTION("Error canceling threads " << ::strerror(nErrors)); + } + } + void** ppJoinResult = new void*[CSimulator::GetInstance().GetNumThreads()+1]; + for(UInt32 i = 0; i <= CSimulator::GetInstance().GetNumThreads(); ++i) { + if((nErrors = pthread_join(m_ptThreads[i], ppJoinResult + i))) { + THROW_ARGOSEXCEPTION("Error joining threads " << ::strerror(nErrors)); + } + if(ppJoinResult[i] != PTHREAD_CANCELED) { + LOGERR << "[WARNING] Thread #" << i<< " not canceled" << std::endl; + } + } + delete[] ppJoinResult; + } + delete[] m_ptThreads; + /* Destroy the thread launch info */ + if(m_psThreadData != NULL) { + for(UInt32 i = 0; i <= CSimulator::GetInstance().GetNumThreads(); ++i) { + delete m_psThreadData[i]; + } + } + delete[] m_psThreadData; + pthread_mutex_destroy(&m_tStartSenseControlPhaseMutex); + pthread_mutex_destroy(&m_tStartActPhaseMutex); + pthread_mutex_destroy(&m_tStartPhysicsPhaseMutex); + pthread_mutex_destroy(&m_tFetchTaskMutex); + pthread_cond_destroy(&m_tStartSenseControlPhaseCond); + pthread_cond_destroy(&m_tStartActPhaseCond); + pthread_cond_destroy(&m_tStartPhysicsPhaseCond); + pthread_cond_destroy(&m_tFetchTaskCond); + + /* Destroy the base space */ + CSpace::Destroy(); + } + + /****************************************/ + /****************************************/ + + void CSpaceMultiThreadHDispatch::SetPhysicsEngines(CPhysicsEngine::TVector& t_engines) { + CSpace::SetPhysicsEngines(t_engines); + StartThreads(); + } + + /****************************************/ + /****************************************/ + +#define MAIN_START_PHASE(PHASE) \ + pthread_mutex_lock(&m_tStart ## PHASE ## PhaseMutex); \ + m_un ## PHASE ## PhaseIdleCounter = 0; \ + m_sTaskData.Reset(); \ + pthread_cond_broadcast(&m_tStart ## PHASE ## PhaseCond); \ + pthread_mutex_unlock(&m_tStart ## PHASE ## PhaseMutex); + +#define MAIN_WAIT_FOR_END_OF(PHASE) \ + pthread_mutex_lock(&m_tStart ## PHASE ## PhaseMutex); \ + while(m_un ## PHASE ## PhaseIdleCounter < CSimulator::GetInstance().GetNumThreads()) { \ + pthread_cond_wait(&m_tStart ## PHASE ## PhaseCond, &m_tStart ## PHASE ## PhaseMutex); \ + } \ + pthread_mutex_unlock(&m_tStart ## PHASE ## PhaseMutex); + + void CSpaceMultiThreadHDispatch::UpdateControllableEntities() { + /* Reset the idle thread count */ + m_unSenseControlPhaseIdleCounter = CSimulator::GetInstance().GetNumThreads(); + m_unActPhaseIdleCounter = CSimulator::GetInstance().GetNumThreads(); + m_unPhysicsPhaseIdleCounter = CSimulator::GetInstance().GetNumThreads(); + /* Sense/control phase */ + MAIN_START_PHASE(SenseControl); + MAIN_WAIT_FOR_END_OF(SenseControl); + /* Act phase */ + MAIN_START_PHASE(Act); + MAIN_WAIT_FOR_END_OF(Act); + } + + /****************************************/ + /****************************************/ + + void CSpaceMultiThreadHDispatch::UpdatePhysics() { + /* Physics phase */ + MAIN_START_PHASE(Physics); + MAIN_WAIT_FOR_END_OF(Physics); + /* Perform entity transfer from engine to engine, if needed */ + for(size_t i = 0; i < m_ptPhysicsEngines->size(); ++i) { + if((*m_ptPhysicsEngines)[i]->IsEntityTransferNeeded()) { + (*m_ptPhysicsEngines)[i]->TransferEntities(); + } + } + } + + /****************************************/ + /****************************************/ + + void CSpaceMultiThreadHDispatch::StartThreads() { + int nErrors; + /* Create the threads to update the controllable entities */ + m_ptThreads = new pthread_t[CSimulator::GetInstance().GetNumThreads() + 1]; + m_psThreadData = new SThreadLaunchData*[CSimulator::GetInstance().GetNumThreads() + 1]; + for(UInt32 i = 0; i <= CSimulator::GetInstance().GetNumThreads(); ++i) { + /* Create the struct with the info to launch the thread */ + m_psThreadData[i] = new SThreadLaunchData(i, this); + /* Create the thread */ + if((nErrors = pthread_create(m_ptThreads + i, + NULL, + LaunchThreadHDispatch, + reinterpret_cast(m_psThreadData[i])))) { + THROW_ARGOSEXCEPTION("Error creating thread: " << ::strerror(nErrors)); + } + } + } + + /****************************************/ + /****************************************/ + +#define THREAD_WAIT_FOR_START_OF(PHASE) \ + pthread_mutex_lock(&m_tStart ## PHASE ## PhaseMutex); \ + while(m_un ## PHASE ## PhaseIdleCounter == CSimulator::GetInstance().GetNumThreads()) { \ + pthread_cond_wait(&m_tStart ## PHASE ## PhaseCond, &m_tStart ## PHASE ## PhaseMutex); \ + } \ + pthread_mutex_unlock(&m_tStart ## PHASE ## PhaseMutex); \ + pthread_testcancel(); + +#define THREAD_DISPATCH_TASK(TASKVEC) \ + if(! (TASKVEC).empty()) { \ + while(! m_sTaskData.Done) { \ + pthread_mutex_lock(&m_tFetchTaskMutex); \ + while(! m_sTaskData.Used) { \ + pthread_cond_wait(&m_tFetchTaskCond, &m_tFetchTaskMutex); \ + } \ + ++m_sTaskData.Index; \ + if(m_sTaskData.Index < (TASKVEC).size()) { \ + m_sTaskData.Used = false; \ + } \ + else { \ + m_sTaskData.Done = true; \ + } \ + pthread_cond_broadcast(&m_tFetchTaskCond); \ + pthread_mutex_unlock(&m_tFetchTaskMutex); \ + pthread_testcancel(); \ + } \ + } \ + else { \ + m_sTaskData.Done = true; \ + pthread_mutex_unlock(&m_tFetchTaskMutex); \ + pthread_testcancel(); \ + } + + /****************************************/ + /****************************************/ + + void CSpaceMultiThreadHDispatch::DispatchThread(UInt32 un_id) { + while(1) { + THREAD_WAIT_FOR_START_OF(SenseControl); + THREAD_DISPATCH_TASK(m_vecControllableEntities); + THREAD_WAIT_FOR_START_OF(Act); + THREAD_DISPATCH_TASK(m_vecControllableEntities); + THREAD_WAIT_FOR_START_OF(Physics); + THREAD_DISPATCH_TASK(*m_ptPhysicsEngines); + } + } + + /****************************************/ + /****************************************/ + +#define THREAD_PERFORM_TASK(PHASE, SNIPPET) \ + while(1) { \ + pthread_mutex_lock(&m_tFetchTaskMutex); \ + while(m_sTaskData.Used && ! m_sTaskData.Done) { \ + pthread_cond_wait(&m_tFetchTaskCond, &m_tFetchTaskMutex); \ + } \ + if(! m_sTaskData.Done) { \ + unTaskIndex = m_sTaskData.Index; \ + m_sTaskData.Used = true; \ + pthread_cond_broadcast(&m_tFetchTaskCond); \ + pthread_mutex_unlock(&m_tFetchTaskMutex); \ + pthread_testcancel(); \ + { \ + SNIPPET; \ + } \ + pthread_testcancel(); \ + } \ + else { \ + pthread_mutex_unlock(&m_tFetchTaskMutex); \ + pthread_testcancel(); \ + pthread_mutex_lock(&m_tStart ## PHASE ## PhaseMutex); \ + ++m_un ## PHASE ## PhaseIdleCounter; \ + pthread_cond_broadcast(&m_tStart ## PHASE ## PhaseCond); \ + pthread_mutex_unlock(&m_tStart ## PHASE ## PhaseMutex); \ + pthread_testcancel(); \ + break; \ + } \ + } \ + pthread_testcancel(); + + void CSpaceMultiThreadHDispatch::SlaveThread(UInt32 un_id) { + /* Task index */ + size_t unTaskIndex; + while(1) { + THREAD_WAIT_FOR_START_OF(SenseControl); + THREAD_PERFORM_TASK( + SenseControl, + m_vecControllableEntities[unTaskIndex]->Sense(); + m_vecControllableEntities[unTaskIndex]->ControlStep(); + ); + THREAD_WAIT_FOR_START_OF(Act); + THREAD_PERFORM_TASK( + Act, + m_vecControllableEntities[unTaskIndex]->Act(); + ); + THREAD_WAIT_FOR_START_OF(Physics); + THREAD_PERFORM_TASK( + Physics, + (*m_ptPhysicsEngines)[unTaskIndex]->Update(); + ); + } + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/simulator/space/space_multi_thread_h_dispatch.h b/src/core/simulator/space/space_multi_thread_h_dispatch.h new file mode 100644 index 00000000..fcbbce59 --- /dev/null +++ b/src/core/simulator/space/space_multi_thread_h_dispatch.h @@ -0,0 +1,108 @@ +/** + * @file argos3/simulator/space/space_multi_thread_h_dispatch.h + * + * @brief This file provides the definition of the Swarmanoid 3D Space. + * + * This file provides the definition of the Swarmanoid 3D Space. + * + * @author Carlo Pinciroli - + */ + +#ifndef SPACE_MULTI_THREAD_H_DISPATCH_H +#define SPACE_MULTI_THREAD_H_DISPATCH_H + +namespace argos { + class CSpace; +} + +#include + +namespace argos { + + class CSpaceMultiThreadHDispatch : public CSpace { + + public: + + CSpaceMultiThreadHDispatch() {} + virtual ~CSpaceMultiThreadHDispatch() {} + + virtual void Init(TConfigurationNode& t_tree); + virtual void Destroy(); + + virtual void SetPhysicsEngines(CPhysicsEngine::TVector& t_engines); + + virtual void UpdateControllableEntities(); + virtual void UpdatePhysics(); + + private: + + void StartThreads(); + void DispatchThread(UInt32 un_id); + void SlaveThread(UInt32 un_id); + friend void* LaunchThreadHDispatch(void* p_data); + + private: + + /** Thread date */ + struct SThreadLaunchData { + UInt32 ThreadId; + CSpaceMultiThreadHDispatch* Space; + + SThreadLaunchData(UInt32 un_thread_id, + CSpaceMultiThreadHDispatch* pc_space) : + ThreadId(un_thread_id), + Space(pc_space) {} + }; + + /** Data associated to a task to be executed */ + struct STaskData { + /** All tasks in arrays. This is the current array index. */ + size_t Index; + /** True when the current index was used already */ + bool Used; + /** True when there are no more tasks to perform */ + bool Done; + + /** Resets the task data */ + void Reset(); + }; + + /** The thread array. Thread #0 is the dispatch one and the others are the slave threads */ + pthread_t* m_ptThreads; + + /** Data structure needed to launch the threads */ + SThreadLaunchData** m_psThreadData; + + /** Data structure needed to coordinate the tasks */ + STaskData m_sTaskData; + + /** Mutex for the start of the sense/control phase */ + pthread_mutex_t m_tStartSenseControlPhaseMutex; + /** Mutex for the start of the act phase */ + pthread_mutex_t m_tStartActPhaseMutex; + /** Mutex for the start of the physics phase */ + pthread_mutex_t m_tStartPhysicsPhaseMutex; + /** Mutex to fetch a task from the dispatcher */ + pthread_mutex_t m_tFetchTaskMutex; + + /** Conditional for the start of the sense/control phase */ + pthread_cond_t m_tStartSenseControlPhaseCond; + /** Conditional for the start of the act phase */ + pthread_cond_t m_tStartActPhaseCond; + /** Conditional for the start of the physics phase */ + pthread_cond_t m_tStartPhysicsPhaseCond; + /** Conditional controlling task fetching from the dispatcher */ + pthread_cond_t m_tFetchTaskCond; + + /** How many threads are idle in the sense/control phase */ + UInt32 m_unSenseControlPhaseIdleCounter; + /** How many threads are idle in the act phase */ + UInt32 m_unActPhaseIdleCounter; + /** How many threads are idle in the physics phase */ + UInt32 m_unPhysicsPhaseIdleCounter; + + }; + +} + +#endif diff --git a/src/core/simulator/space/space_multi_thread_scatter_gather.cpp b/src/core/simulator/space/space_multi_thread_scatter_gather.cpp new file mode 100644 index 00000000..d2e60052 --- /dev/null +++ b/src/core/simulator/space/space_multi_thread_scatter_gather.cpp @@ -0,0 +1,314 @@ +/** + * @file argos3/simulator/space/space_multi_thread_scatter_gather.cpp + * + * @brief This file provides the implementation of the Swarmanoid 3D Space. + * + * This file provides the implementation of the Swarmanoid 3D Space. + * + * @author Carlo Pinciroli - + */ + +#include +#include +#include +#include +#include "space_multi_thread_scatter_gather.h" + +namespace argos { + + /****************************************/ + /****************************************/ + + struct SCleanupUpdateThreadData { + pthread_mutex_t* SenseControlStepConditionalMutex; + pthread_mutex_t* ActConditionalMutex; + pthread_mutex_t* PhysicsConditionalMutex; + }; + + static void CleanupUpdateThread(void* p_data) { + CSimulator& cSimulator = CSimulator::GetInstance(); + if(cSimulator.IsProfiling()) { + cSimulator.GetProfiler().CollectThreadResourceUsage(); + } + SCleanupUpdateThreadData& sData = + *reinterpret_cast(p_data); + pthread_mutex_unlock(sData.SenseControlStepConditionalMutex); + pthread_mutex_unlock(sData.ActConditionalMutex); + pthread_mutex_unlock(sData.PhysicsConditionalMutex); + } + + void* LaunchUpdateThreadScatterGather(void* p_data) { + LOG.AddThreadSafeBuffer(); + LOGERR.AddThreadSafeBuffer(); + CSpaceMultiThreadScatterGather::SUpdateThreadData* psData = reinterpret_cast(p_data); + psData->Space->UpdateThread(psData->ThreadId); + return NULL; + } + + /****************************************/ + /****************************************/ + + void CSpaceMultiThreadScatterGather::Init(TConfigurationNode& t_tree) + { + /* Initialize the space */ + CSpace::Init(t_tree); + + /* Initialize thread related structures */ + int nErrors; + /* First the counters */ + m_unSenseControlStepPhaseDoneCounter = CSimulator::GetInstance().GetNumThreads(); + m_unActPhaseDoneCounter = CSimulator::GetInstance().GetNumThreads(); + m_unPhysicsPhaseDoneCounter = CSimulator::GetInstance().GetNumThreads(); + /* Then the mutexes */ + if((nErrors = pthread_mutex_init(&m_tSenseControlStepConditionalMutex, NULL)) || + (nErrors = pthread_mutex_init(&m_tPhysicsConditionalMutex, NULL)) || + (nErrors = pthread_mutex_init(&m_tActConditionalMutex, NULL))) { + THROW_ARGOSEXCEPTION("Error creating thread mutexes " << ::strerror(nErrors)); + } + /* Finally the conditionals */ + if((nErrors = pthread_cond_init(&m_tSenseControlStepConditional, NULL)) || + (nErrors = pthread_cond_init(&m_tPhysicsConditional, NULL)) || + (nErrors = pthread_cond_init(&m_tActConditional, NULL))) { + THROW_ARGOSEXCEPTION("Error creating thread conditionals " << ::strerror(nErrors)); + } + } + + /****************************************/ + /****************************************/ + + void CSpaceMultiThreadScatterGather::StartThreads() { + int nErrors; + /* Create the threads to update the controllable entities */ + m_ptUpdateThreads = new pthread_t[CSimulator::GetInstance().GetNumThreads()]; + m_psUpdateThreadData = new SUpdateThreadData*[CSimulator::GetInstance().GetNumThreads()]; + for(UInt32 i = 0; i < CSimulator::GetInstance().GetNumThreads(); ++i) { + /* Create the struct with the info to launch the thread */ + m_psUpdateThreadData[i] = new SUpdateThreadData(i, this); + /* Create the thread */ + if((nErrors = pthread_create(m_ptUpdateThreads + i, + NULL, + LaunchUpdateThreadScatterGather, + reinterpret_cast(m_psUpdateThreadData[i])))) { + THROW_ARGOSEXCEPTION("Error creating thread: " << ::strerror(nErrors)); + } + } + } + + /****************************************/ + /****************************************/ + + void CSpaceMultiThreadScatterGather::Destroy() + { + /* Destroy the threads to update the controllable entities */ + int nErrors; + if(m_ptUpdateThreads != NULL) { + for(UInt32 i = 0; i < CSimulator::GetInstance().GetNumThreads(); ++i) { + if((nErrors = pthread_cancel(m_ptUpdateThreads[i]))) { + THROW_ARGOSEXCEPTION("Error canceling controllable entities update threads " << ::strerror(nErrors)); + } + } + void** ppJoinResult = new void*[CSimulator::GetInstance().GetNumThreads()]; + for(UInt32 i = 0; i < CSimulator::GetInstance().GetNumThreads(); ++i) { + if((nErrors = pthread_join(m_ptUpdateThreads[i], ppJoinResult + i))) { + THROW_ARGOSEXCEPTION("Error joining controllable entities update threads " << ::strerror(nErrors)); + } + if(ppJoinResult[i] != PTHREAD_CANCELED) { + LOGERR << "[WARNING] Controllable entities update thread #" << i<< " not canceled" << std::endl; + } + } + delete[] ppJoinResult; + } + delete[] m_ptUpdateThreads; + /* Destroy the thread launch info */ + if(m_psUpdateThreadData != NULL) { + for(UInt32 i = 0; i < CSimulator::GetInstance().GetNumThreads(); ++i) { + delete m_psUpdateThreadData[i]; + } + } + delete[] m_psUpdateThreadData; + pthread_mutex_destroy(&m_tSenseControlStepConditionalMutex); + pthread_mutex_destroy(&m_tActConditionalMutex); + pthread_cond_destroy(&m_tSenseControlStepConditional); + pthread_cond_destroy(&m_tActConditional); + + /* Destroy the base space */ + CSpace::Destroy(); + } + + /****************************************/ + /****************************************/ + + void CSpaceMultiThreadScatterGather::AddControllableEntity(CControllableEntity& c_entity) { + m_bIsControllableEntityAssignmentRecalculationNeeded = true; + CSpace::AddControllableEntity(c_entity); + } + + /****************************************/ + /****************************************/ + + void CSpaceMultiThreadScatterGather::RemoveControllableEntity(CControllableEntity& c_entity) { + m_bIsControllableEntityAssignmentRecalculationNeeded = true; + CSpace::RemoveControllableEntity(c_entity); + } + + /****************************************/ + /****************************************/ + + void CSpaceMultiThreadScatterGather::SetPhysicsEngines(CPhysicsEngine::TVector& t_engines) { + CSpace::SetPhysicsEngines(t_engines); + StartThreads(); + } + + /****************************************/ + /****************************************/ + +#define MAIN_SEND_GO_FOR_PHASE(PHASE) \ + LOG.Flush(); \ + LOGERR.Flush(); \ + pthread_mutex_lock(&m_t ## PHASE ## ConditionalMutex); \ + m_un ## PHASE ## PhaseDoneCounter = 0; \ + pthread_cond_broadcast(&m_t ## PHASE ## Conditional); \ + pthread_mutex_unlock(&m_t ## PHASE ## ConditionalMutex); + +#define MAIN_WAIT_FOR_PHASE_END(PHASE) \ + pthread_mutex_lock(&m_t ## PHASE ## ConditionalMutex); \ + while(m_un ## PHASE ## PhaseDoneCounter < CSimulator::GetInstance().GetNumThreads()) { \ + pthread_cond_wait(&m_t ## PHASE ## Conditional, &m_t ## PHASE ## ConditionalMutex); \ + } \ + pthread_mutex_unlock(&m_t ## PHASE ## ConditionalMutex); + + void CSpaceMultiThreadScatterGather::UpdateControllableEntities() { + MAIN_SEND_GO_FOR_PHASE(SenseControlStep); + MAIN_WAIT_FOR_PHASE_END(SenseControlStep); + MAIN_SEND_GO_FOR_PHASE(Act); + MAIN_WAIT_FOR_PHASE_END(Act); + /* Avoid recalculation at the next time step */ + m_bIsControllableEntityAssignmentRecalculationNeeded = false; + } + + /****************************************/ + /****************************************/ + + void CSpaceMultiThreadScatterGather::UpdatePhysics() { + /* Update the physics engines */ + MAIN_SEND_GO_FOR_PHASE(Physics); + MAIN_WAIT_FOR_PHASE_END(Physics); + /* Perform entity transfer from engine to engine, if needed */ + for(size_t i = 0; i < m_ptPhysicsEngines->size(); ++i) { + if((*m_ptPhysicsEngines)[i]->IsEntityTransferNeeded()) { + (*m_ptPhysicsEngines)[i]->TransferEntities(); + } + } + } + + /****************************************/ + /****************************************/ + +#define THREAD_WAIT_FOR_GO_SIGNAL(PHASE) \ + pthread_mutex_lock(&m_t ## PHASE ## ConditionalMutex); \ + while(m_un ## PHASE ## PhaseDoneCounter == CSimulator::GetInstance().GetNumThreads()) { \ + pthread_cond_wait(&m_t ## PHASE ## Conditional, &m_t ## PHASE ## ConditionalMutex); \ + } \ + pthread_mutex_unlock(&m_t ## PHASE ## ConditionalMutex); \ + pthread_testcancel(); + +#define THREAD_SIGNAL_PHASE_DONE(PHASE) \ + pthread_mutex_lock(&m_t ## PHASE ## ConditionalMutex); \ + ++m_un ## PHASE ## PhaseDoneCounter; \ + pthread_cond_broadcast(&m_t ## PHASE ## Conditional); \ + pthread_mutex_unlock(&m_t ## PHASE ## ConditionalMutex); \ + pthread_testcancel(); + + void CSpaceMultiThreadScatterGather::UpdateThread(UInt32 un_id) { + /* Copy the id */ + UInt32 unId = un_id; + /* Create cancellation data */ + SCleanupUpdateThreadData sCancelData; + sCancelData.SenseControlStepConditionalMutex = &m_tSenseControlStepConditionalMutex; + sCancelData.ActConditionalMutex = &m_tActConditionalMutex; + sCancelData.PhysicsConditionalMutex = &m_tPhysicsConditionalMutex; + pthread_cleanup_push(CleanupUpdateThread, &sCancelData); + /* Calculate whether this thread is in charge of physics engines and how many */ + size_t tPhysicsPortion = (m_ptPhysicsEngines->size() / CSimulator::GetInstance().GetNumThreads()); + if((m_ptPhysicsEngines->size() % CSimulator::GetInstance().GetNumThreads()) != 0) { + ++tPhysicsPortion; + } + CRange cPhysicsRange(0, 1); + bool bUpdateEngines = false; + if(unId * tPhysicsPortion < m_ptPhysicsEngines->size()) { + bUpdateEngines = true; + cPhysicsRange.Set(unId * tPhysicsPortion, + Min(unId * tPhysicsPortion + tPhysicsPortion, + m_ptPhysicsEngines->size())); + } + /* Variables storing the portion of entities to update */ + size_t tEntityPortion; + CRange cEntityRange(0,1); + bool bUpdateEntities = false; + while(1) { + THREAD_WAIT_FOR_GO_SIGNAL(SenseControlStep); + /* Calculate the portion of entities to update, if needed */ + if(m_bIsControllableEntityAssignmentRecalculationNeeded) { + tEntityPortion = (m_vecControllableEntities.size() / CSimulator::GetInstance().GetNumThreads()); + if((m_vecControllableEntities.size() % CSimulator::GetInstance().GetNumThreads()) != 0) { + ++tEntityPortion; + } + /* Cope with the fact that there may be less entities than threads */ + if(unId * tEntityPortion < m_vecControllableEntities.size()) { + /* This thread has entities */ + bUpdateEntities = true; + cEntityRange.Set(unId * tEntityPortion, + Min(unId * tEntityPortion + tEntityPortion, + m_vecControllableEntities.size())); + } + } + /* Cope with the fact that there may be less entities than threads */ + if(bUpdateEntities) { + /* This thread has entities */ + /* Update sensor readings and call controllers */ + for(size_t i = cEntityRange.GetMin(); i < cEntityRange.GetMax(); ++i) { + m_vecControllableEntities[i]->Sense(); + m_vecControllableEntities[i]->ControlStep(); + } + pthread_testcancel(); + THREAD_SIGNAL_PHASE_DONE(SenseControlStep); + /* Actuate control choices */ + THREAD_WAIT_FOR_GO_SIGNAL(Act); + for(size_t i = cEntityRange.GetMin(); i < cEntityRange.GetMax(); ++i) { + m_vecControllableEntities[i]->Act(); + } + pthread_testcancel(); + THREAD_SIGNAL_PHASE_DONE(Act); + } + else { + /* This thread has no entities -> dummy computation */ + /* Update sensor readings */ + /* Call controllers */ + THREAD_WAIT_FOR_GO_SIGNAL(SenseControlStep); + THREAD_SIGNAL_PHASE_DONE(SenseControlStep); + /* Actuate control choices */ + THREAD_WAIT_FOR_GO_SIGNAL(Act); + THREAD_SIGNAL_PHASE_DONE(Act); + } + /* Update physics engines, if this thread has been assigned to them */ + THREAD_WAIT_FOR_GO_SIGNAL(Physics); + if(bUpdateEngines) { + /* This thread has engines, update them */ + for(size_t i = cPhysicsRange.GetMin(); i < cPhysicsRange.GetMax(); ++i) { + (*m_ptPhysicsEngines)[i]->Update(); + } + pthread_testcancel(); + THREAD_SIGNAL_PHASE_DONE(Physics); + } + else { + /* This thread has no engines -> dummy computation */ + THREAD_SIGNAL_PHASE_DONE(Physics); + } + } + pthread_cleanup_pop(1); + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/simulator/space/space_multi_thread_scatter_gather.h b/src/core/simulator/space/space_multi_thread_scatter_gather.h new file mode 100644 index 00000000..a0cb5188 --- /dev/null +++ b/src/core/simulator/space/space_multi_thread_scatter_gather.h @@ -0,0 +1,95 @@ +/** + * @file argos3/simulator/space/space_multi_thread_scatter_gather.h + * + * @brief This file provides the definition of the Swarmanoid 3D Space. + * + * This file provides the definition of the Swarmanoid 3D Space. + * + * @author Carlo Pinciroli - + */ + +#ifndef SPACE_MULTI_THREAD_SCATTER_GATHER_H +#define SPACE_MULTI_THREAD_SCATTER_GATHER_H + +#include +#include + +namespace argos { + + class CSpaceMultiThreadScatterGather : public CSpace { + + /****************************************/ + /****************************************/ + + private: + + struct SUpdateThreadData { + UInt32 ThreadId; + CSpaceMultiThreadScatterGather* Space; + + SUpdateThreadData(UInt32 un_thread_id, + CSpaceMultiThreadScatterGather* pc_space) : + ThreadId(un_thread_id), + Space(pc_space) {} + }; + + /****************************************/ + /****************************************/ + + private: + + /** Data structure needed to launch the threads */ + SUpdateThreadData** m_psUpdateThreadData; + + /** The update threads */ + pthread_t* m_ptUpdateThreads; + + /** Update thread related variables */ + UInt32 m_unSenseControlStepPhaseDoneCounter; + UInt32 m_unActPhaseDoneCounter; + UInt32 m_unPhysicsPhaseDoneCounter; + + /** Update thread conditional mutexes */ + pthread_mutex_t m_tSenseControlStepConditionalMutex; + pthread_mutex_t m_tActConditionalMutex; + pthread_mutex_t m_tPhysicsConditionalMutex; + + /** Update thread conditionals */ + pthread_cond_t m_tSenseControlStepConditional; + pthread_cond_t m_tActConditional; + pthread_cond_t m_tPhysicsConditional; + + /** Flag to know whether the assignment of controllable + entities to threads must be recalculated */ + bool m_bIsControllableEntityAssignmentRecalculationNeeded; + + public: + + CSpaceMultiThreadScatterGather() : + m_psUpdateThreadData(NULL), + m_ptUpdateThreads(NULL), + m_bIsControllableEntityAssignmentRecalculationNeeded(true) {} + virtual ~CSpaceMultiThreadScatterGather() {} + + virtual void Init(TConfigurationNode& t_tree); + virtual void Destroy(); + + virtual void AddControllableEntity(CControllableEntity& c_entity); + virtual void RemoveControllableEntity(CControllableEntity& c_entity); + + virtual void SetPhysicsEngines(CPhysicsEngine::TVector& t_engines); + + virtual void UpdateControllableEntities(); + virtual void UpdatePhysics(); + + private: + + void StartThreads(); + void UpdateThread(UInt32 un_id); + friend void* LaunchUpdateThreadScatterGather(void* p_data); + + }; + +} + +#endif diff --git a/src/core/simulator/space/space_no_threads.cpp b/src/core/simulator/space/space_no_threads.cpp new file mode 100644 index 00000000..3ada392e --- /dev/null +++ b/src/core/simulator/space/space_no_threads.cpp @@ -0,0 +1,48 @@ +/** + * @file argos3/simulator/space/space_no_threads.cpp + * + * @brief This file provides the implementation of the Swarmanoid 3D Space. + * + * This file provides the implementation of the Swarmanoid 3D Space. + * + * @author Carlo Pinciroli - + */ + +#include "space_no_threads.h" +#include + +namespace argos { + + /****************************************/ + /****************************************/ + + void CSpaceNoThreads::UpdateControllableEntities() { + for(size_t i = 0; i < m_vecControllableEntities.size(); ++i) { + m_vecControllableEntities[i]->Sense(); + m_vecControllableEntities[i]->ControlStep(); + } + for(size_t i = 0; i < m_vecControllableEntities.size(); ++i) { + m_vecControllableEntities[i]->Act(); + } + } + + /****************************************/ + /****************************************/ + + void CSpaceNoThreads::UpdatePhysics() { + /* Update the physics engines */ + for(size_t i = 0; i < m_ptPhysicsEngines->size(); ++i) { + (*m_ptPhysicsEngines)[i]->Update(); + } + /* Perform entity transfer from engine to engine, if needed */ + for(size_t i = 0; i < m_ptPhysicsEngines->size(); ++i) { + if((*m_ptPhysicsEngines)[i]->IsEntityTransferNeeded()) { + (*m_ptPhysicsEngines)[i]->TransferEntities(); + } + } + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/simulator/space/space_no_threads.h b/src/core/simulator/space/space_no_threads.h new file mode 100644 index 00000000..6db5c04f --- /dev/null +++ b/src/core/simulator/space/space_no_threads.h @@ -0,0 +1,36 @@ +/** + * @file argos3/simulator/space/space_no_threads.h + * + * @brief This file provides the definition of the Swarmanoid 3D Space. + * + * This file provides the definition of the Swarmanoid 3D Space. + * + * @author Carlo Pinciroli - + */ + +#ifndef SPACE_NO_THREADS_H +#define SPACE_NO_THREADS_H + +namespace argos { + class CSpace; +} + +#include + +namespace argos { + + class CSpaceNoThreads : public CSpace { + + public: + + CSpaceNoThreads() {} + virtual ~CSpaceNoThreads() {} + + virtual void UpdateControllableEntities(); + virtual void UpdatePhysics(); + + }; + +} + +#endif diff --git a/src/core/simulator/visualization/visualization.cpp b/src/core/simulator/visualization/visualization.cpp new file mode 100644 index 00000000..0bfbd3be --- /dev/null +++ b/src/core/simulator/visualization/visualization.cpp @@ -0,0 +1,33 @@ +/** + * @file argos3/core/simulator/visualization/visualization.cpp + * + * @author Carlo Pinciroli - + */ + +#include +#include + +namespace argos { + + /****************************************/ + /****************************************/ + + void CVisualization::Init(TConfigurationNode& t_tree) { + /* Get visualization id from the XML */ + GetNodeAttribute(t_tree, "id", m_strId); + } + + /****************************************/ + /****************************************/ + + void CVisualization::Execute() { + /* Main cycle */ + while(!m_cSimulator.IsExperimentFinished()) { + m_cSimulator.UpdateSpace(); + } + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/simulator/visualization/visualization.h b/src/core/simulator/visualization/visualization.h new file mode 100644 index 00000000..19dd0937 --- /dev/null +++ b/src/core/simulator/visualization/visualization.h @@ -0,0 +1,84 @@ +/** + * @file argos3/core/simulator/visualization/visualization.h + * + * @brief This file contains the definition of the CVisualization interface. + * + * This file contains the definition of the CVisualization interface. Such interface + * is the base for all the visualisations in ARGoS. + * + * @author Carlo Pinciroli - + */ + +#ifndef VISUALIZATION_H +#define VISUALIZATION_H + +namespace argos { + class CVisualization; +} + +#include +#include + +#include +#include + +namespace argos { + + class CVisualization : public CBaseConfigurableResource { + + public: + + CVisualization() : + m_cSimulator(CSimulator::GetInstance()), + m_cSpace(m_cSimulator.GetSpace()) {} + + virtual ~CVisualization() {} + + virtual void Init(TConfigurationNode& t_tree); + + virtual void Reset() {} + + virtual void Destroy() {} + + virtual void Execute(); + + inline const std::string& GetId() const { + return m_strId; + } + + inline void SetId(const std::string& str_id) { + m_strId = str_id; + } + + protected: + + /** A reference to the simulator */ + CSimulator& m_cSimulator; + + /** A reference to the space */ + CSpace& m_cSpace; + + /** The id of the visualizationer */ + std::string m_strId; + + }; + +#define REGISTER_VISUALIZATION(CLASSNAME, \ + LABEL, \ + AUTHOR, \ + VERSION, \ + BRIEF_DESCRIPTION, \ + LONG_DESCRIPTION, \ + STATUS) \ + REGISTER_SYMBOL(CVisualization, \ + CLASSNAME, \ + LABEL, \ + AUTHOR, \ + VERSION, \ + BRIEF_DESCRIPTION, \ + LONG_DESCRIPTION, \ + STATUS) + +} + +#endif diff --git a/src/core/utility/configuration/argos_configuration.h b/src/core/utility/configuration/argos_configuration.h new file mode 100644 index 00000000..66bae48b --- /dev/null +++ b/src/core/utility/configuration/argos_configuration.h @@ -0,0 +1,499 @@ +/** + * @file argos3/core/utility/configuration/argos_configuration.h + * + * @brief This file provides some functions to deal with XML data. + * + * ARGoS is configured though an XML file. All the functions provided here are used to deal with that file. + * Internally, these functions exploit the + * tinyxml library. + * + * @author Carlo Pinciroli - + */ + +#ifndef ARGOS_CONFIGURATION_H +#define ARGOS_CONFIGURATION_H + +#include +#include +#include +#include + +namespace argos { + + /****************************************/ + /****************************************/ + + /** The ARGoS configuration XML node */ + typedef ticpp::Element TConfigurationNode; + /** The iterator for the ARGoS configuration XML node */ + typedef ticpp::Iterator TConfigurationNodeIterator; + + /****************************************/ + /****************************************/ + + /** + * Given a tree root node, returns true if one of its child nodes has the wanted name. + * Given an XML tree, this function checks for the existence of a first-level node in this tree (i.e., a direct child of the + * root node) with the given name. + * @param t_node the root node of the XML tree + * @param str_tag the name of the wanted child node + * @return true when the node exists, false otherwise + */ + inline bool NodeExists(TConfigurationNode& t_node, + const std::string& str_tag) throw() { + TConfigurationNodeIterator it(str_tag); + it = it.begin(&t_node); + return it != NULL; + } + + /****************************************/ + /****************************************/ + + /** + * Given a tree root node, returns the first of its child nodes with the wanted name. + * Given an XML tree, this function checks for the existence of a first-level node in this tree (i.e., a direct child of the + * root node) with the given name. + * @param t_node the root node of the XML tree + * @param str_tag the name of the wanted child node + * @return the first child node with the given name + * @throw CARGoSException if no child node with the wanted name exists + */ + inline TConfigurationNode& GetNode(TConfigurationNode& t_node, + const std::string& str_tag) { + try { + TConfigurationNodeIterator it(str_tag); + it = it.begin(&t_node); + if(it == NULL) { + THROW_ARGOSEXCEPTION("Node '" << str_tag << "' not found"); + } + return *it; + } + catch(ticpp::Exception& ex) { + THROW_ARGOSEXCEPTION_NESTED("Error searching for '" << str_tag << "' ", ex); + } + } + + /****************************************/ + /****************************************/ + + /** + * Adds an XML node as child of another XML node. + * The node is added at the end of the children of the parent node. + * @param t_parent_node the parent node + * @param t_child_node the child node to add to the parent + * @throw CARGoSException if an error occurred + */ + inline void AddChildNode(TConfigurationNode& t_parent_node, + TConfigurationNode& t_child_node) { + try { + t_parent_node.InsertEndChild(t_child_node); + } + catch(ticpp::Exception& ex) { + THROW_ARGOSEXCEPTION_NESTED("Error inserting node '" << t_child_node << "' into node '" << t_parent_node << "'", ex); + } + } + + /****************************************/ + /****************************************/ + + /** + * Returns the text of the passed XML node + * A node text is as follows: + *
+    * this is the text
+    * 25
+    * 3.14, 5.87
+    * 
+ * This function is templetized. This means that you can pass any variable type to this function and parsing will occur + * automatically. For instance: + *
+    * std::string strText;
+    * GetNodeText(tMyNode1, strText); // tMyNode1 points to 
+    *                                 // strText now is "this is the text"
+    * UInt32 unText;
+    * GetNodeText(tMyNode2, unText);  // tMyNode2 points to 
+    *                                 // unText now is 25
+    * CVector2 cText;
+    * GetNodeText(tMyNode3, cText);   // tMyNode3 points to 
+    *                                 // cText now is CVector2(3.14, 5.87)
+    * 
+ * @param t_node the node + * @param t_buffer a buffer where the text must be stored + * @throw CARGoSException if an error occurred (i.e., parse error) or the node has no text value + * @see GetNodeTextOrDefault() + */ + template + void GetNodeText(TConfigurationNode& t_node, + T& t_buffer) { + try { + t_node.GetText(&t_buffer); + } + catch(std::exception& ex) { + THROW_ARGOSEXCEPTION_NESTED("Parse error", ex); + } + } + + /****************************************/ + /****************************************/ + + /** + * Returns the text of the passed XML node, or the passed default value. + * This function works like GetNodeText(). The only difference is that if no text value is found, the passed default is + * stored in the buffer variable instead. + * @param t_node the node + * @param t_buffer a buffer where the text must be stored + * @param t_default a default value to be used if the node has no text + * @throw CARGoSException if an error occurred (i.e., parse error) + * @see GetNodeText() + */ + template + void GetNodeTextOrDefault(TConfigurationNode& t_node, + T& t_buffer, + const T& t_default) { + try { + t_node.GetTextOrDefault(&t_buffer, t_default); + } + catch(std::exception& ex) { + THROW_ARGOSEXCEPTION_NESTED("Parse error", ex); + } + } + + /****************************************/ + /****************************************/ + + /** + * Returns true if the specified attribute of a node exists. + * @param t_node the node whose attribute we want to search for + * @param str_attribute the name of the attribute to search for + * @return true if the attribute exists, false otherwise + */ + inline bool NodeAttributeExists(TConfigurationNode& t_node, + const std::string& str_attribute) { + return t_node.HasAttribute(str_attribute); + } + + /****************************************/ + /****************************************/ + + /** + * Returns the value of a node's attribute. + * XML nodes can have attributes: + *
+    * 
+    * 
+ * This function is templetized. This means that you can pass any variable type to this function and parsing will occur + * automatically. For instance: + *
+    * std::string strValue;
+    * GetNodeAttribute(tMyNode, "attribute1", strValue); // tMyNode points to 
+    *                                                    // strValue now is "this is a string attribute"
+    * SInt32 nValue;
+    * GetNodeAttribute(tMyNode, "attribute2", nValue);   // tMyNode points to 
+    *                                                    // nValue now is -87
+    * CVector3 cValue;
+    * GetNodeAttribute(tMyNode, "attribute3", cValue);   // tMyNode points to 
+    *                                                    // cValue now is CVector3(-0.5, 12.3, 4)
+    * 
+ * @param t_node the node + * @param str_attribute the name of the wanted attribute + * @param t_buffer a buffer where the value must be stored + * @throw CARGoSException if an error occurred (i.e., parse error) or the attribute does not exist + * @see GetNodeAttributeOrDefault() + */ + template + void GetNodeAttribute(TConfigurationNode& t_node, + const std::string& str_attribute, + T& t_buffer) { + try { + t_node.GetAttribute(str_attribute, &t_buffer, true); + } + catch(ticpp::Exception& ex) { + THROW_ARGOSEXCEPTION_NESTED("Error parsing attribute \"" << str_attribute << "\"", ex); + } + } + + /****************************************/ + /****************************************/ + + /** + * Returns the value of a node's attribute. + * This function is an overloaded version of the templetized GetNodeAttribute() in which the buffer is a boolean. + * @param t_node the node + * @param str_attribute the name of the wanted attribute + * @param b_buffer a buffer where the value must be stored + * @throw CARGoSException if an error occurred (i.e., parse error) or the attribute does not exist + * @see GetNodeAttribute() + */ + inline void GetNodeAttribute(TConfigurationNode& t_node, + const std::string& str_attribute, + bool& b_buffer) { + std::string strBuffer; + try { + t_node.GetAttribute(str_attribute, &strBuffer, true); + if(strBuffer == "true") { + b_buffer = true; + } + else if(strBuffer == "false") { + b_buffer = false; + } + else { + THROW_ARGOSEXCEPTION("Cannot convert '" << strBuffer << "' into a bool. Accepted values: 'true', 'false'."); + } + } + catch(ticpp::Exception& ex) { + THROW_ARGOSEXCEPTION_NESTED("Error parsing attribute \"" << str_attribute << "\"", ex); + } + } + + /****************************************/ + /****************************************/ + + /** + * Returns the value of a node's attribute. + * This function is an overloaded version of the templetized GetNodeAttribute() in which the buffer is a UInt8. + * @param t_node the node + * @param str_attribute the name of the wanted attribute + * @param un_buffer a buffer where the value must be stored + * @throw CARGoSException if an error occurred (i.e., parse error) or the attribute does not exist + * @see GetNodeAttribute() + */ + inline void GetNodeAttribute(TConfigurationNode& t_node, + const std::string& str_attribute, + UInt8& un_buffer) { + try { + UInt32 unTmpBuffer; + t_node.GetAttribute(str_attribute, &unTmpBuffer, true); + un_buffer = unTmpBuffer; + } + catch(ticpp::Exception& ex) { + THROW_ARGOSEXCEPTION_NESTED("Error parsing attribute \"" << str_attribute << "\"", ex); + } + } + + /****************************************/ + /****************************************/ + + /** + * Returns the value of a node's attribute. + * This function is an overloaded version of the templetized GetNodeAttribute() in which the buffer is a SInt8. + * @param t_node the node + * @param str_attribute the name of the wanted attribute + * @param n_buffer a buffer where the value must be stored + * @throw CARGoSException if an error occurred (i.e., parse error) or the attribute does not exist + * @see GetNodeAttribute() + */ + inline void GetNodeAttribute(TConfigurationNode& t_node, + const std::string& str_attribute, + SInt8& n_buffer) { + try { + SInt32 nTmpBuffer; + t_node.GetAttribute(str_attribute, &nTmpBuffer, true); + n_buffer = nTmpBuffer; + } + catch(ticpp::Exception& ex) { + THROW_ARGOSEXCEPTION_NESTED("Error parsing attribute \"" << str_attribute << "\"", ex); + } + } + + /****************************************/ + /****************************************/ + + /** + * Returns the value of a node's attribute, or the passed default value. + * This function works like GetNodeAttribute(). The only difference is that if the wanted attribute does not exist, the + * passed default is stored in the buffer instead. + * stored in the buffer variable instead. + * @param t_node the node + * @param str_attribute the name of the wanted attribute + * @param t_buffer a buffer where the value must be stored + * @param t_default a default value to be used if the attribute does not exist + * @throw CARGoSException if an error occurred (i.e., parse error) + * @see GetNodeAttribute() + */ + template + void GetNodeAttributeOrDefault(TConfigurationNode& t_node, + const std::string& str_attribute, + T& t_buffer, + const T& t_default) { + try { + t_node.GetAttributeOrDefault(str_attribute, &t_buffer, t_default); + } + catch(ticpp::Exception& ex) { + THROW_ARGOSEXCEPTION_NESTED("Error parsing attribute \"" << str_attribute << "\"", ex); + } + } + + /****************************************/ + /****************************************/ + + /** + * Returns the value of a node's attribute, or the passed default value. + * This function is an overloaded version of the templetized GetNodeAttributeOrDefault() in which the buffer is a boolean. + * stored in the buffer variable instead. + * @param t_node the node + * @param str_attribute the name of the wanted attribute + * @param b_buffer a buffer where the value must be stored + * @param b_default a default value to be used if the attribute does not exist + * @throw CARGoSException if an error occurred (i.e., parse error) + * @see GetNodeAttributeOrDefault() + */ + inline void GetNodeAttributeOrDefault(TConfigurationNode& t_node, + const std::string& str_attribute, + bool& b_buffer, + const bool b_default) { + std::string strBuffer; + const std::string strDefault = (b_default ? "true" : "false"); + try { + t_node.GetAttributeOrDefault(str_attribute, &strBuffer, strDefault); + if(strBuffer == "true") { + b_buffer = true; + } + else if(strBuffer == "false") { + b_buffer = false; + } + else { + THROW_ARGOSEXCEPTION("Cannot convert '" << strBuffer << "' into a bool. Accepted values: 'true', 'false'."); + } + } + catch(ticpp::Exception& ex) { + THROW_ARGOSEXCEPTION_NESTED("Error parsing attribute \"" << str_attribute << "\"", ex); + } + } + + /****************************************/ + /****************************************/ + + /** + * Returns the value of a node's attribute, or the passed default value. + * This function is an overloaded version of the templetized GetNodeAttributeOrDefault() in which the buffer is a UInt8. + * stored in the buffer variable instead. + * @param t_node the node + * @param str_attribute the name of the wanted attribute + * @param un_buffer a buffer where the value must be stored + * @param un_default a default value to be used if the attribute does not exist + * @throw CARGoSException if an error occurred (i.e., parse error) + * @see GetNodeAttributeOrDefault() + */ + inline void GetNodeAttributeOrDefault(TConfigurationNode& t_node, + const std::string& str_attribute, + UInt8& un_buffer, + const UInt8 un_default) { + try { + UInt32 unTmpBuffer; + t_node.GetAttributeOrDefault(str_attribute, &unTmpBuffer, static_cast(un_default)); + un_buffer = unTmpBuffer; + } + catch(ticpp::Exception& ex) { + THROW_ARGOSEXCEPTION_NESTED("Error parsing attribute \"" << str_attribute << "\"", ex); + } + } + + /****************************************/ + /****************************************/ + + /** + * Returns the value of a node's attribute, or the passed default value. + * This function is an overloaded version of the templetized GetNodeAttributeOrDefault() in which the buffer is a SInt8. + * stored in the buffer variable instead. + * @param t_node the node + * @param str_attribute the name of the wanted attribute + * @param n_buffer a buffer where the value must be stored + * @param n_default a default value to be used if the attribute does not exist + * @throw CARGoSException if an error occurred (i.e., parse error) + * @see GetNodeAttributeOrDefault() + */ + inline void GetNodeAttributeOrDefault(TConfigurationNode& t_node, + const std::string& str_attribute, + SInt8& n_buffer, + const SInt8 n_default) { + try { + SInt32 nTmpBuffer; + t_node.GetAttributeOrDefault(str_attribute, &nTmpBuffer, static_cast(n_default)); + n_buffer = nTmpBuffer; + } + catch(ticpp::Exception& ex) { + THROW_ARGOSEXCEPTION_NESTED("Error parsing attribute \"" << str_attribute << "\"", ex); + } + } + + /****************************************/ + /****************************************/ + + /** + * Sets the value of the wanted node's attribute. + * If the attribute does not exist, it is created. + * @param t_node the node + * @param str_attribute the name of the wanted attribute + * @param t_value the value to set + */ + template + void SetNodeAttribute(TConfigurationNode& t_node, + const std::string& str_attribute, + const T& t_value) { + t_node.SetAttribute(str_attribute, t_value); + } + + /****************************************/ + /****************************************/ + + /** + * Sets the value of the wanted node's attribute. + * This function is an overloaded version of the templetized SetNodeAttribute() for boolean values. + * @param t_node the node + * @param str_attribute the name of the wanted attribute + * @param b_value the value to set + * @see SetNodeAttribute() + */ + inline void SetNodeAttribute(TConfigurationNode& t_node, + const std::string& str_attribute, + const bool b_value) { + if(b_value) { + t_node.SetAttribute(str_attribute, "true"); + } + else { + t_node.SetAttribute(str_attribute, "false"); + } + } + + /****************************************/ + /****************************************/ + + /** + * Sets the value of the wanted node's attribute. + * This function is an overloaded version of the templetized SetNodeAttribute() for SInt8 values. + * @param t_node the node + * @param str_attribute the name of the wanted attribute + * @param n_value the value to set + * @see SetNodeAttribute() + */ + inline void SetNodeAttribute(TConfigurationNode& t_node, + const std::string& str_attribute, + const SInt8 n_value) { + t_node.SetAttribute(str_attribute, static_cast(n_value)); + } + + /****************************************/ + /****************************************/ + + /** + * Sets the value of the wanted node's attribute. + * This function is an overloaded version of the templetized SetNodeAttribute() for UInt8 values. + * @param t_node the node + * @param str_attribute the name of the wanted attribute + * @param un_value the value to set + * @see SetNodeAttribute() + */ + inline void SetNodeAttribute(TConfigurationNode& t_node, + const std::string& str_attribute, + const UInt8 un_value) { + t_node.SetAttribute(str_attribute, static_cast(un_value)); + } + + /****************************************/ + /****************************************/ + +} + +#endif diff --git a/src/core/utility/configuration/argos_exception.h b/src/core/utility/configuration/argos_exception.h new file mode 100644 index 00000000..60c7b764 --- /dev/null +++ b/src/core/utility/configuration/argos_exception.h @@ -0,0 +1,125 @@ +/** + * @file argos3/core/utility/configuration/argos_exception.h + * + * @brief This file provides the definition of the main ARGoS exception. + * + * Apart from the exception class, this file also provides several utility macros that should always be used to throw + * ARGoS exceptions. The simplest way to throw an ARGoS exception is to use the following: + *
+ * THROW_ARGOSEXCEPTION("The value you provided is wrong.")
+ * 
+ * you can also used variable values in the message, as follows: + *
+ * THROW_ARGOSEXCEPTION("The value \"" << myVar << "\" is wrong.")
+ * 
+ * It is also possible to nest exceptions, to provide more details to the user when an error occurs. See the following example: + *
+ * try {
+ *    DoStuff(); // throws an ARGoS exception
+ *    DoMoreStuff(); // this is not executed
+ * }
+ * catch(CARGoSException& ex) {
+ *    THROW_ARGOSEXCEPTION_NESTED("There was an error while doing stuff", ex);
+ * }
+ * 
+ * + * In addition, it is possible to define assertions for debug purposes. Assertions are conditions on variable values. They are + * typically nice to have while developing to be sure that the code is behaving correctly (i.e., array bound checks), but in + * later phases, when the code must be released, the cost of the check is considered an unnecessary burden. To support + * debug assertions, ARGoS provides the ARGOS_ASSERT macro. Here is an example: + *
+ * void MyFunction(UInt32 un_index) {
+ *    ARGOS_ASSERT(un_index < 10, "Index out of bounds.");
+ *    ...
+ * }
+ * 
+ * When the code is compiled in debug, this macro checks the condition. If the conditions is false, an ARGoS exception is + * thrown with the passed message. When the code is compiled in release, the ARGOS_ASSERT macro does not produce any code at all, + * thus the resulting binary is much faster. + * + * @author Carlo Pinciroli - + */ + +#ifndef ARGOS_EXCEPTION_H +#define ARGOS_EXCEPTION_H + +namespace argos { + class CARGoSException; +} + +#include +#include + +namespace argos { + + /** + * The exception that wraps all errors in ARGoS. + * + * This class is used to wrap all errors occurring in ARGoS. You should never explicitly throw this exception + * directly, but you should instead use the provided macros THROW_ARGOSEXCEPTION() and THROW_ARGOSEXCEPTION_NESTED(). + */ + class CARGoSException : public std::exception { + + public: + + /** + * Class constructor. + * @param str_what the error message that explains what happened and why the exception was thrown + * @param pc_nested a pointer to the nested exception + */ + CARGoSException(const std::string& str_what, std::exception* pc_nested = NULL) throw() : + m_strWhat("CARGoSException thrown: " + str_what), m_pcNested(pc_nested) { + if (m_pcNested != NULL) { + std::ostringstream w; + w << m_strWhat << " [nested: \"" << m_pcNested->what() << "\"]"; + m_strWhat = w.str(); + } + } + /** + * Class destructor. + */ + virtual ~CARGoSException() throw() { + } + + /** + * Returns the error message that explains what happened and why the exception was thrown + * The returned message also contains messages of nested exceptions, if any. + */ + virtual const char* what() const throw() { + return m_strWhat.c_str(); + } + + private: + + /** + * The error message that explains what happened and why the exception was thrown + * The message also contains messages of nested exceptions, if any. + */ + std::string m_strWhat; + /** A pointer to the nested exception, or NULL of there is none */ + std::exception* m_pcNested; + + }; + +} + +/** + * This macro throws an ARGoS exception with the passed message. + */ +#define THROW_ARGOSEXCEPTION(message) { std::ostringstream what; what << __FILE__ << ":" << __LINE__ << std::endl << " " << message; throw CARGoSException(what.str()); } +/** + * This macro throws an ARGoS exception with the passed message and nesting the passed exception. + */ +#define THROW_ARGOSEXCEPTION_NESTED(message, nested) { std::ostringstream what; what << __FILE__ << ":" << __LINE__ << std::endl << " " << message; throw CARGoSException(what.str(), &nested); } + +#ifndef NDEBUG +/** + * When code is compiled in debug, this macro throws an ARGoS exception with the passed message if the specified condition is not met. + * When code is compiled in release, this macro does nothing. + */ +#define ARGOS_ASSERT(condition, message) { if ( !(condition) ) THROW_ARGOSEXCEPTION(message); } +#else +#define ARGOS_ASSERT(condition, message) +#endif + +#endif diff --git a/src/core/utility/configuration/base_configurable_resource.h b/src/core/utility/configuration/base_configurable_resource.h new file mode 100644 index 00000000..b33388d7 --- /dev/null +++ b/src/core/utility/configuration/base_configurable_resource.h @@ -0,0 +1,53 @@ +/** + * @file argos3/core/utility/configuration/base_configurable_resource.h + * + * @brief This file provides the definition of the base configurable resource interface. + * + * @author Carlo Pinciroli - + */ + +#ifndef BASE_CONFIGURABLE_RESOURCE_H +#define BASE_CONFIGURABLE_RESOURCE_H + +namespace argos { + class CBaseConfigurableResource; +} + +#include + +namespace argos { + + /** + * This class is the base of all XML-configurable ARGoS interface. + */ + class CBaseConfigurableResource { + + public: + + /** + * Class destructor. + */ + virtual ~CBaseConfigurableResource() = 0; + + /** + * Initializes the resource. + * @param t_tree the base of the XML configuration tree to parse + * @throw CARGoSException if an error occurs + */ + virtual void Init(TConfigurationNode& t_tree) = 0; + + /** + * Resets the resource. + */ + virtual void Reset() = 0; + + /** + * Undoes whatever was done by Init(). + */ + virtual void Destroy() = 0; + + }; + +} + +#endif diff --git a/src/core/utility/configuration/command_line_arg_parser.cpp b/src/core/utility/configuration/command_line_arg_parser.cpp new file mode 100644 index 00000000..6a3b7714 --- /dev/null +++ b/src/core/utility/configuration/command_line_arg_parser.cpp @@ -0,0 +1,227 @@ +/** + * @file argos3/core/utility/configuration/command_line_arg_parser.cpp + * + * @author Carlo Pinciroli - + */ + +#include "command_line_arg_parser.h" +#include + +namespace argos { + + /****************************************/ + /****************************************/ + + CCommandLineArgParser::CCommandLineArgParser() : + m_nCurrentArgument(0) { + } + + /****************************************/ + /****************************************/ + + CCommandLineArgParser::~CCommandLineArgParser() { + while(! m_vecArguments.empty()) { + delete m_vecArguments.back(); + m_vecArguments.pop_back(); + } + } + + /****************************************/ + /****************************************/ + + void CCommandLineArgParser::PrintUsage(CARGoSLog& c_log) { + for(size_t i = 0; i < m_vecArguments.size(); ++i) { + c_log << "-" << m_vecArguments[i]->ShortOption << "|" + << "--" << m_vecArguments[i]->LongOption; + if(!m_vecArguments[i]->IsFlag) { + c_log << " "; + } + c_log << "\t" + << m_vecArguments[i]->Description + << std::endl; + } + } + + /****************************************/ + /****************************************/ + + void CCommandLineArgParser::Parse(SInt32 n_argc, + char** ppch_argv) { + /* Used to store information about each argument to parse */ + size_t unArgLength; + char* pchCurrentArg; + /* Start parsing from the beginning */ + m_nCurrentArgument = 1; + /* Go through all the passed arguments */ + while(m_nCurrentArgument < n_argc) { + /* + * Parsing does as follows: + * If the first characters is a - followed by other charactares, then it's an option + * If the second character is a - too, it's a long one + * Otherwise it's a (set of) short option(s) + * If the first character is not a -, or the string is just "-", error + */ + pchCurrentArg = ppch_argv[m_nCurrentArgument]; + unArgLength = ::strlen(pchCurrentArg); + if(ppch_argv[m_nCurrentArgument][0] != '-' || + unArgLength == 1) { + THROW_ARGOSEXCEPTION("Unrecognized option \"" << pchCurrentArg << "\"."); + } + else { + /* The first character is a - and more characters are to come */ + if(pchCurrentArg[1] == '-') { + /* Also the second char is a - */ + if(unArgLength > 2) { + /* We have a long option */ + ParseLongOption(n_argc, ppch_argv); + } + else { + /* It's just a --, which we don't support */ + THROW_ARGOSEXCEPTION("Unrecognized option \"" << pchCurrentArg << "\"."); + } + } + else { + /* We have a short option like -x, or a set of them like -xyz */ + if(unArgLength == 2) { + /* Single short option: -x */ + ParseShortOption(n_argc, ppch_argv); + } + else { + /* Set of short options: -xyz */ + ParseShortOptions(n_argc, ppch_argv); + } + } + } + /* Parse next option */ + ++m_nCurrentArgument; + } + } + + /****************************************/ + /****************************************/ + + void CCommandLineArgParser::ParseLongOption(SInt32 n_argc, + char** ppch_argv) { + /* Strip the initial -- from the option */ + std::string strArg(ppch_argv[m_nCurrentArgument]+2); + try { + /* Search for the option in the vector */ + for(size_t i = 0; i < m_vecArguments.size(); ++i) { + if(m_vecArguments[i]->LongOption == strArg) { + /* The option has been found */ + if(m_vecArguments[i]->IsFlag) { + /* It's a flag, pass "true" to set the boolean to true */ + m_vecArguments[i]->Parse("true"); + } + else { + /* It's an argument, use what follows as parameter */ + ++m_nCurrentArgument; + if(m_nCurrentArgument == n_argc) { + THROW_ARGOSEXCEPTION("Missing argument for option \"--" << strArg << "\"."); + } + m_vecArguments[i]->Parse(ppch_argv[m_nCurrentArgument]); + } + /* No need to continue searching */ + return; + } + } + } + catch(CARGoSException& ex) { + /* Something went wrong with the parsing */ + THROW_ARGOSEXCEPTION_NESTED("Error parsing option \"--" << strArg << "\".", ex); + } + /* If you get here, it's because the option hasn't been found */ + THROW_ARGOSEXCEPTION("Unrecognized option \"--" << strArg << "\"."); + } + + /****************************************/ + /****************************************/ + + void CCommandLineArgParser::ParseShortOption(SInt32 n_argc, + char** ppch_argv) { + /* Strip the initial - from the option */ + char strArg(ppch_argv[m_nCurrentArgument][1]); + try { + /* Search for the option in the vector */ + for(size_t i = 0; i < m_vecArguments.size(); ++i) { + if(m_vecArguments[i]->ShortOption == strArg) { + /* The option has been found */ + if(m_vecArguments[i]->IsFlag) { + /* It's a flag, pass "true" to set the boolean to true */ + m_vecArguments[i]->Parse("true"); + } + else { + /* It's an argument, use what follows as parameter */ + ++m_nCurrentArgument; + if(m_nCurrentArgument == n_argc) { + THROW_ARGOSEXCEPTION("Missing argument for option \"-" << strArg << "\"."); + } + m_vecArguments[i]->Parse(ppch_argv[m_nCurrentArgument]); + } + /* No need to continue searching */ + return; + } + } + } + catch(CARGoSException& ex) { + /* Something went wrong with the parsing */ + THROW_ARGOSEXCEPTION_NESTED("Error parsing option \"-" << strArg << "\".", ex); + } + /* If you get here, it's because the option hasn't been found */ + THROW_ARGOSEXCEPTION("Unrecognized option \"-" << strArg << "\"."); + } + + /****************************************/ + /****************************************/ + + void CCommandLineArgParser::ParseShortOptions(SInt32 n_argc, + char** ppch_argv) { + /* Strip the initial - from the option */ + std::string strArg(ppch_argv[m_nCurrentArgument]+1); + /* Go through all options */ + for(size_t o = 0; o < strArg.length(); ++o) { + /* Search for the option in the vector */ + size_t i = 0; + bool bFound = false; + try { + while(i < m_vecArguments.size() && !bFound) { + if(m_vecArguments[i]->ShortOption == strArg[o]) { + /* The option has been found */ + if(m_vecArguments[i]->IsFlag) { + /* It's a flag, pass "true" to set the boolean to true */ + m_vecArguments[i]->Parse("true"); + } + else { + /* It's an argument, only the last short option is allowed to have one */ + if(o < strArg.length()-1) { + THROW_ARGOSEXCEPTION("Option \"-" << strArg[o] << "\" requires an argument."); + } + else { + ++m_nCurrentArgument; + m_vecArguments[i]->Parse(ppch_argv[m_nCurrentArgument]); + } + } + /* No need to continue searching */ + bFound = true; + } + else { + /* Try next option */ + ++i; + } + } + } + catch(CARGoSException& ex) { + /* Something went wrong with the parsing */ + THROW_ARGOSEXCEPTION_NESTED("Error parsing option \"-" << strArg << "\".", ex); + } + if(!bFound) { + /* The option hasn't been found */ + THROW_ARGOSEXCEPTION("Unrecognized option \"-" << strArg[o] << "\"."); + } + } + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/utility/configuration/command_line_arg_parser.h b/src/core/utility/configuration/command_line_arg_parser.h new file mode 100644 index 00000000..7c553a23 --- /dev/null +++ b/src/core/utility/configuration/command_line_arg_parser.h @@ -0,0 +1,216 @@ +/** + * @file argos3/core/utility/configuration/command_line_arg_parser.h + * + * This file provides the definition of the class CCommandLineArgParser. + * It is an easy-to-use command line parser. + * + * It supports a subset of the GNU-style syntax: + *
+ *   $ program --gnu-style-flag         # OK
+ *   $ program --gnu-style-option value # OK
+ *   $ program --gnu-style-option=value # ERROR
+ *   $ program -x                       # OK
+ *   $ program -xyz value               # OK
+ * 
+ * + * However, positional arguments are not supported: + *
+ *   $ program value       # ERROR
+ *   $ program --arg value # OK
+ *   $ program -a value    # OK
+ * 
+ * + * @author Carlo Pinciroli - + */ + +#ifndef COMMAND_LINE_ARG_PARSER_H +#define COMMAND_LINE_ARG_PARSER_H + +namespace argos { + class CCommandLineArgParser; +} + +#include +#include +#include +#include +#include +#include + +namespace argos { + + /** + * Easy-to-use command line argument parser. + * + * Example: you want to parse + *
+    *   $ program --flag --arg1 intvalue --arg2 stringvalue # GNU-style
+    *   $ program -f -1 intvalue -2 stringvalue             # short style
+    * 
+ * + * The code to do it: + * + *
+    *   int main(int argc, char** argv) {
+    *     bool bIsFlag;         // will be true if the flag was specified, false otherwise
+    *     UInt32 unIntValue;    // stores th parsed intvalue
+    *     std::string strValue; // stores the parsed stringvalue
+    *
+    *     // Create the parser
+    *     CCommandLineArgParser cCLAP;
+    *     // Add the flag
+    *     cCLAP.AddFlag(
+    *       'f',
+    *       "--flag",
+    *       "This is a flag",
+    *       bIsFlag);
+    *     // Add the int argument
+    *     cCLAP.AddArgument(
+    *       '1',
+    *       "--arg1",
+    *       "This is an int value",
+    *       unIntValue);
+    *     // Add the string argument
+    *     cCLAP.AddArgument(
+    *       '2',
+    *       "--arg2",
+    *       "This is a string value",
+    *       strValue);
+    *
+    *     // Parse the command line!
+    *     // In case of errors, a CARGoSException is thrown
+    *     try {
+    *       cCLAP.Parse(argc, argv);
+    *     }
+    *     catch(CARGoSException& ex) {
+    *       LOGERR << "Error: " << ex.what() << std::endl;
+    *     }
+    * 
+ */ + class CCommandLineArgParser { + + public: + + /** + * Class constructor. + */ + CCommandLineArgParser(); + + /** + * Class destructor. + */ + virtual ~CCommandLineArgParser(); + + /** + * Adds a flag to the parser. + * @param ch_short_option A single character for the short option + * @param str_long_option The long option + * @param str_description The description shown by PrintUsage() + * @param b_flag The boolean variable to associate to this entry + */ + void AddFlag(char ch_short_option, + const std::string& str_long_option, + const std::string& str_description, + bool& b_flag) { + m_vecArguments.push_back( + new CArgument( + ch_short_option, + str_long_option, + str_description, + true, + b_flag)); + b_flag = false; + } + + /** + * Adds an argument to the parser. + * @param ch_short_option A single character for the short option + * @param str_long_option The long option + * @param str_description The description shown by PrintUsage() + * @param t_buffer The buffer variable to associate to this entry + */ + template void AddArgument(char ch_short_option, + const std::string& str_long_option, + const std::string& str_description, + T& t_buffer) { + m_vecArguments.push_back( + new CArgument( + ch_short_option, + str_long_option, + str_description, + false, + t_buffer)); + } + + /** + * Prints the arguments on the log. + * If you want a better layout, you need to extend this method. + * @param c_log The destination log: LOG or LOGERR + * @see CARGoSLog + */ + virtual void PrintUsage(CARGoSLog& c_log); + + /** + * Parses the arguments on the command line. + * @param n_argc The number of arguments to parse + * @param ppch_argv The string array containing the command line + */ + virtual void Parse(SInt32 n_argc, + char** ppch_argv); + + private: + + void ParseLongOption (SInt32 n_argc, char** ppch_argv); + void ParseShortOption (SInt32 n_argc, char** ppch_argv); + void ParseShortOptions(SInt32 n_argc, char** ppch_argv); + + private: + + class CAbstractArgument { + + public: + + virtual void Parse(const std::string& str_value) = 0; + + public: + + char ShortOption; + std::string LongOption; + std::string Description; + bool IsFlag; + + }; + + template class CArgument : public CAbstractArgument { + + public: + + CArgument(char ch_short_option, + const std::string& str_long_option, + const std::string& str_description, + bool b_is_flag, + T& t_buffer) : + m_tBuffer(t_buffer) { + ShortOption = ch_short_option; + LongOption = str_long_option; + Description = str_description; + IsFlag = b_is_flag; + } + + virtual void Parse(const std::string& str_value) { + m_tBuffer = FromString(str_value); + } + + T& m_tBuffer; + }; + + private: + + std::vector m_vecArguments; + SInt32 m_nCurrentArgument; + + }; + +} + +#endif diff --git a/src/core/utility/configuration/memento.h b/src/core/utility/configuration/memento.h new file mode 100644 index 00000000..2e9f537e --- /dev/null +++ b/src/core/utility/configuration/memento.h @@ -0,0 +1,54 @@ +/** + * @file argos3/core/utility/configuration/memento.h + * + * @brief This file provides the definition of the memento interface. + * + * The Memento pattern + * is a software design pattern that provides the ability to restore an object to + * its previous state. + * + * @author Carlo Pinciroli - + */ + +#ifndef MEMENTO_H +#define MEMENTO_H + +namespace argos { + class CMemento; +} + +#include + +namespace argos { + + /** + * The interface inherited by all classes whose state must be saved and restored. + */ + class CMemento { + + public: + + /** + * Class destructor. + */ + virtual ~CMemento() = 0; + + /** + * Saves the state of the object to the given buffer. + * Data is added to the buffer. + * @param c_buffer the target buffer + */ + virtual void SaveState(CByteArray& c_buffer) = 0; + + /** + * Restores the state of the object from the given buffer. + * The read data is removed from the buffer. + * @param c_buffer the source buffer + */ + virtual void LoadState(CByteArray& c_buffer) = 0; + + }; + +} + +#endif diff --git a/src/core/utility/configuration/tinyxml/dox b/src/core/utility/configuration/tinyxml/dox new file mode 100644 index 00000000..f5cfe5f3 --- /dev/null +++ b/src/core/utility/configuration/tinyxml/dox @@ -0,0 +1,1229 @@ +# Doxyfile 1.4.3 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = TinyXml + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 2.5.3 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = ./docs + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = YES + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. + +SHOW_DIRECTORIES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the progam writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . \ + readme.txt \ + tutorial_gettingStarted.txt \ + tutorial_ticpp.txt + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm + +FILE_PATTERNS = *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = . + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = YES + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = TinyXML Help_v2.5.3.chm + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = "C:\Program Files\DocRunner\hhc.exe" + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = NO + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO \ No newline at end of file diff --git a/src/core/utility/configuration/tinyxml/readme.txt b/src/core/utility/configuration/tinyxml/readme.txt new file mode 100644 index 00000000..aad3648b --- /dev/null +++ b/src/core/utility/configuration/tinyxml/readme.txt @@ -0,0 +1,531 @@ +/** @mainpage + +

TinyXML

+ +TinyXML is a simple, small, C++ XML parser that can be easily +integrated into other programs. + +

What it does.

+ +In brief, TinyXML parses an XML document, and builds from that a +Document Object Model (DOM) that can be read, modified, and saved. + +XML stands for "eXtensible Markup Language." It allows you to create +your own document markups. Where HTML does a very good job of marking +documents for browsers, XML allows you to define any kind of document +markup, for example a document that describes a "to do" list for an +organizer application. XML is a very structured and convenient format. +All those random file formats created to store application data can +all be replaced with XML. One parser for everything. + +The best place for the complete, correct, and quite frankly hard to +read spec is at +http://www.w3.org/TR/2004/REC-xml-20040204/. An intro to XML +(that I really like) can be found at +http://skew.org/xml/tutorial. + +There are different ways to access and interact with XML data. +TinyXML uses a Document Object Model (DOM), meaning the XML data is parsed +into a C++ objects that can be browsed and manipulated, and then +written to disk or another output stream. You can also construct an XML document +from scratch with C++ objects and write this to disk or another output +stream. + +TinyXML is designed to be easy and fast to learn. It is two headers +and four cpp files. Simply add these to your project and off you go. +There is an example file - xmltest.cpp - to get you started. + +TinyXML is released under the ZLib license, +so you can use it in open source or commercial code. The details +of the license are at the top of every source file. + +TinyXML attempts to be a flexible parser, but with truly correct and +compliant XML output. TinyXML should compile on any reasonably C++ +compliant system. It does not rely on exceptions or RTTI. It can be +compiled with or without STL support. TinyXML fully supports +the UTF-8 encoding, and the first 64k character entities. + + +

What it doesn't do.

+ +TinyXML doesn't parse or use DTDs (Document Type Definitions) or XSLs +(eXtensible Stylesheet Language.) There are other parsers out there +(check out www.sourceforge.org, search for XML) that are much more fully +featured. But they are also much bigger, take longer to set up in +your project, have a higher learning curve, and often have a more +restrictive license. If you are working with browsers or have more +complete XML needs, TinyXML is not the parser for you. + +The following DTD syntax will not parse at this time in TinyXML: + +@verbatim + + ]> +@endverbatim + +because TinyXML sees this as a !DOCTYPE node with an illegally +embedded !ELEMENT node. This may be addressed in the future. + +

Tutorials.

+ +For the impatient, here are some tutorials to get you going. A great way to get started, +but it is worth your time to read this (very short) manual completely. + +- @subpage ticppTutorial +- @subpage tutorial0 + +

Code Status.

+ +TinyXML is mature, tested code. It is very stable. If you find +bugs, please file a bug report on the sourceforge web site +(www.sourceforge.net/projects/tinyxml). We'll get them straightened +out as soon as possible. + +There are some areas of improvement; please check sourceforge if you are +interested in working on TinyXML. + +

Related Projects

+ +TinyXML projects you may find useful! (Descriptions provided by the projects.) + +
    +
  • TinyXPath (http://tinyxpath.sourceforge.net). TinyXPath is a small footprint + XPath syntax decoder, written in C++.
  • +
  • @subpage ticpp (http://code.google.com/p/ticpp/). TinyXML++ is a completely new + interface to TinyXML that uses MANY of the C++ strengths. Templates, + exceptions, and much better error handling.
  • +
+ +

Features

+ +

Using STL

+ +TinyXML can be compiled to use or not use STL. When using STL, TinyXML +uses the std::string class, and fully supports std::istream, std::ostream, +operator<<, and operator>>. Many API methods have both 'const char*' and +'const std::string&' forms. + +When STL support is compiled out, no STL files are included whatsoever. All +the string classes are implemented by TinyXML itself. API methods +all use the 'const char*' form for input. + +Use the compile time #define: + + TIXML_USE_STL + +to compile one version or the other. This can be passed by the compiler, +or set as the first line of "tinyxml.h". + +Note: If compiling the test code in Linux, setting the environment +variable TINYXML_USE_STL=YES/NO will control STL compilation. In the +Windows project file, STL and non STL targets are provided. In your project, +It's probably easiest to add the line "#define TIXML_USE_STL" as the first +line of tinyxml.h. + +

UTF-8

+ +TinyXML supports UTF-8 allowing to manipulate XML files in any language. TinyXML +also supports "legacy mode" - the encoding used before UTF-8 support and +probably best described as "extended ascii". + +Normally, TinyXML will try to detect the correct encoding and use it. However, +by setting the value of TIXML_DEFAULT_ENCODING in the header file, TinyXML +can be forced to always use one encoding. + +TinyXML will assume Legacy Mode until one of the following occurs: +
    +
  1. If the non-standard but common "UTF-8 lead bytes" (0xef 0xbb 0xbf) + begin the file or data stream, TinyXML will read it as UTF-8.
  2. +
  3. If the declaration tag is read, and it has an encoding="UTF-8", then + TinyXML will read it as UTF-8.
  4. +
  5. If the declaration tag is read, and it has no encoding specified, then TinyXML will + read it as UTF-8.
  6. +
  7. If the declaration tag is read, and it has an encoding="something else", then TinyXML + will read it as Legacy Mode. In legacy mode, TinyXML will work as it did before. It's + not clear what that mode does exactly, but old content should keep working.
  8. +
  9. Until one of the above criteria is met, TinyXML runs in Legacy Mode.
  10. +
+ +What happens if the encoding is incorrectly set or detected? TinyXML will try +to read and pass through text seen as improperly encoded. You may get some strange results or +mangled characters. You may want to force TinyXML to the correct mode. + +You may force TinyXML to Legacy Mode by using LoadFile( TIXML_ENCODING_LEGACY ) or +LoadFile( filename, TIXML_ENCODING_LEGACY ). You may force it to use legacy mode all +the time by setting TIXML_DEFAULT_ENCODING = TIXML_ENCODING_LEGACY. Likewise, you may +force it to TIXML_ENCODING_UTF8 with the same technique. + +For English users, using English XML, UTF-8 is the same as low-ASCII. You +don't need to be aware of UTF-8 or change your code in any way. You can think +of UTF-8 as a "superset" of ASCII. + +UTF-8 is not a double byte format - but it is a standard encoding of Unicode! +TinyXML does not use or directly support wchar, TCHAR, or Microsoft's _UNICODE at this time. +It is common to see the term "Unicode" improperly refer to UTF-16, a wide byte encoding +of unicode. This is a source of confusion. + +For "high-ascii" languages - everything not English, pretty much - TinyXML can +handle all languages, at the same time, as long as the XML is encoded +in UTF-8. That can be a little tricky, older programs and operating systems +tend to use the "default" or "traditional" code page. Many apps (and almost all +modern ones) can output UTF-8, but older or stubborn (or just broken) ones +still output text in the default code page. + +For example, Japanese systems traditionally use SHIFT-JIS encoding. +Text encoded as SHIFT-JIS can not be read by TinyXML. +A good text editor can import SHIFT-JIS and then save as UTF-8. + +The Skew.org link does a great +job covering the encoding issue. + +The test file "utf8test.xml" is an XML containing English, Spanish, Russian, +and Simplified Chinese. (Hopefully they are translated correctly). The file +"utf8test.gif" is a screen capture of the XML file, rendered in IE. Note that +if you don't have the correct fonts (Simplified Chinese or Russian) on your +system, you won't see output that matches the GIF file even if you can parse +it correctly. Also note that (at least on my Windows machine) console output +is in a Western code page, so that Print() or printf() cannot correctly display +the file. This is not a bug in TinyXML - just an OS issue. No data is lost or +destroyed by TinyXML. The console just doesn't render UTF-8. + + +

Entities

+TinyXML recognizes the pre-defined "character entities", meaning special +characters. Namely: + +@verbatim + & & + < < + > > + " " + ' ' +@endverbatim + +These are recognized when the XML document is read, and translated to there +UTF-8 equivalents. For instance, text with the XML of: + +@verbatim + Far & Away +@endverbatim + +will have the Value() of "Far & Away" when queried from the TiXmlText object, +and will be written back to the XML stream/file as an ampersand. Older versions +of TinyXML "preserved" character entities, but the newer versions will translate +them into characters. + +Additionally, any character can be specified by its Unicode code point: +The syntax " " or " " are both to the non-breaking space characher. + +

Printing

+TinyXML can print output in several different ways that all have strengths and limitations. + +- Print( FILE* ). Output to a std-C stream, which includes all C files as well as stdout. + - "Pretty prints", but you don't have control over printing options. + - The output is streamed directly to the FILE object, so there is no memory overhead + in the TinyXML code. + - used by Print() and SaveFile() + +- operator<<. Output to a c++ stream. + - Integrates with standart C++ iostreams. + - Outputs in "network printing" mode without line breaks. Good for network transmission + and moving XML between C++ objects, but hard for a human to read. + +- TiXmlPrinter. Output to a std::string or memory buffer. + - API is less concise + - Future printing options will be put here. + - Printing may change slightly in future versions as it is refined and expanded. + +

Streams

+With TIXML_USE_STL on TinyXML supports C++ streams (operator <<,>>) streams as well +as C (FILE*) streams. There are some differences that you may need to be aware of. + +C style output: + - based on FILE* + - the Print() and SaveFile() methods + + Generates formatted output, with plenty of white space, intended to be as + human-readable as possible. They are very fast, and tolerant of ill formed + XML documents. For example, an XML document that contains 2 root elements + and 2 declarations, will still print. + +C style input: + - based on FILE* + - the Parse() and LoadFile() methods + + A fast, tolerant read. Use whenever you don't need the C++ streams. + +C++ style output: + - based on std::ostream + - operator<< + + Generates condensed output, intended for network transmission rather than + readability. Depending on your system's implementation of the ostream class, + these may be somewhat slower. (Or may not.) Not tolerant of ill formed XML: + a document should contain the correct one root element. Additional root level + elements will not be streamed out. + +C++ style input: + - based on std::istream + - operator>> + + Reads XML from a stream, making it useful for network transmission. The tricky + part is knowing when the XML document is complete, since there will almost + certainly be other data in the stream. TinyXML will assume the XML data is + complete after it reads the root element. Put another way, documents that + are ill-constructed with more than one root element will not read correctly. + Also note that operator>> is somewhat slower than Parse, due to both + implementation of the STL and limitations of TinyXML. + +

White space

+The world simply does not agree on whether white space should be kept, or condensed. +For example, pretend the '_' is a space, and look at "Hello____world". HTML, and +at least some XML parsers, will interpret this as "Hello_world". They condense white +space. Some XML parsers do not, and will leave it as "Hello____world". (Remember +to keep pretending the _ is a space.) Others suggest that __Hello___world__ should become +Hello___world. + +It's an issue that hasn't been resolved to my satisfaction. TinyXML supports the +first 2 approaches. Call TiXmlBase::SetCondenseWhiteSpace( bool ) to set the desired behavior. +The default is to condense white space. + +If you change the default, you should call TiXmlBase::SetCondenseWhiteSpace( bool ) +before making any calls to Parse XML data, and I don't recommend changing it after +it has been set. + + +

Handles

+ +Where browsing an XML document in a robust way, it is important to check +for null returns from method calls. An error safe implementation can +generate a lot of code like: + +@verbatim +TiXmlElement* root = document.FirstChildElement( "Document" ); +if ( root ) +{ + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. +@endverbatim + +Handles have been introduced to clean this up. Using the TiXmlHandle class, +the previous code reduces to: + +@verbatim +TiXmlHandle docHandle( &document ); +TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); +if ( child2 ) +{ + // do something useful +@endverbatim + +Which is much easier to deal with. See TiXmlHandle for more information. + + +

Row and Column tracking

+Being able to track nodes and attributes back to their origin location +in source files can be very important for some applications. Additionally, +knowing where parsing errors occured in the original source can be very +time saving. + +TinyXML can tracks the row and column origin of all nodes and attributes +in a text file. The TiXmlBase::Row() and TiXmlBase::Column() methods return +the origin of the node in the source text. The correct tabs can be +configured in TiXmlDocument::SetTabSize(). + + +

Using and Installing

+ +To Compile and Run xmltest: + +A Linux Makefile and a Windows Visual C++ .dsw file is provided. +Simply compile and run. It will write the file demotest.xml to your +disk and generate output on the screen. It also tests walking the +DOM by printing out the number of nodes found using different +techniques. + +The Linux makefile is very generic and runs on many systems - it +is currently tested on mingw and +MacOSX. You do not need to run 'make depend'. The dependecies have been +hard coded. + +

Windows project file for VC6

+
    +
  • tinyxml: tinyxml library, non-STL
  • +
  • tinyxmlSTL: tinyxml library, STL
  • +
  • tinyXmlTest: test app, non-STL
  • +
  • tinyXmlTestSTL: test app, STL
  • +
+ +

Makefile

+At the top of the makefile you can set: + +PROFILE, DEBUG, and TINYXML_USE_STL. Details (such that they are) are in +the makefile. + +In the tinyxml directory, type "make clean" then "make". The executable +file 'xmltest' will be created. + + + +

To Use in an Application:

+ +Add tinyxml.cpp, tinyxml.h, tinyxmlerror.cpp, tinyxmlparser.cpp, tinystr.cpp, and tinystr.h to your +project or make file. That's it! It should compile on any reasonably +compliant C++ system. You do not need to enable exceptions or +RTTI for TinyXML. + + +

How TinyXML works.

+ +An example is probably the best way to go. Take: +@verbatim + + + + Go to the Toy store! + Do bills + +@endverbatim + +Its not much of a To Do list, but it will do. To read this file +(say "demo.xml") you would create a document, and parse it in: +@verbatim + TiXmlDocument doc( "demo.xml" ); + doc.LoadFile(); +@endverbatim + +And its ready to go. Now lets look at some lines and how they +relate to the DOM. + +@verbatim + +@endverbatim + + The first line is a declaration, and gets turned into the + TiXmlDeclaration class. It will be the first child of the + document node. + + This is the only directive/special tag parsed by by TinyXML. + Generally directive tags are stored in TiXmlUnknown so the + commands wont be lost when it is saved back to disk. + +@verbatim + +@endverbatim + + A comment. Will become a TiXmlComment object. + +@verbatim + +@endverbatim + + The "ToDo" tag defines a TiXmlElement object. This one does not have + any attributes, but does contain 2 other elements. + +@verbatim + +@endverbatim + + Creates another TiXmlElement which is a child of the "ToDo" element. + This element has 1 attribute, with the name "priority" and the value + "1". + +@verbatim +Go to the +@endverbatim + + A TiXmlText. This is a leaf node and cannot contain other nodes. + It is a child of the "Item" TiXmlElement. + +@verbatim + +@endverbatim + + + Another TiXmlElement, this one a child of the "Item" element. + +Etc. + +Looking at the entire object tree, you end up with: +@verbatim +TiXmlDocument "demo.xml" + TiXmlDeclaration "version='1.0'" "standalone=no" + TiXmlComment " Our to do list data" + TiXmlElement "ToDo" + TiXmlElement "Item" Attribtutes: priority = 1 + TiXmlText "Go to the " + TiXmlElement "bold" + TiXmlText "Toy store!" + TiXmlElement "Item" Attributes: priority=2 + TiXmlText "Do bills" +@endverbatim + +

Documentation

+ +The documentation is build with Doxygen, using the 'dox' +configuration file. + +

License

+ +TinyXML is released under the zlib license: + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +

References

+ +The World Wide Web Consortium is the definitive standard body for +XML, and there web pages contain huge amounts of information. + +The definitive spec: +http://www.w3.org/TR/2004/REC-xml-20040204/ + +I also recommend "XML Pocket Reference" by Robert Eckstein and published by +OReilly...the book that got the whole thing started. + +

Contributors, Contacts, and a Brief History

+ +Thanks very much to everyone who sends suggestions, bugs, ideas, and +encouragement. It all helps, and makes this project fun. A special thanks +to the contributors on the web pages that keep it lively. + +So many people have sent in bugs and ideas, that rather than list here +we try to give credit due in the "changes.txt" file. + +TinyXML was originally written by Lee Thomason. (Often the "I" still +in the documentation.) Lee reviews changes and releases new versions, +with the help of Yves Berquin, Andrew Ellerton, and the tinyXml community. + +We appreciate your suggestions, and would love to know if you +use TinyXML. Hopefully you will enjoy it and find it useful. +Please post questions, comments, file bugs, or contact us at: + +www.sourceforge.net/projects/tinyxml + +Lee Thomason, Yves Berquin, Andrew Ellerton +*/ diff --git a/src/core/utility/configuration/tinyxml/ticpp.cpp b/src/core/utility/configuration/tinyxml/ticpp.cpp new file mode 100644 index 00000000..5965a51c --- /dev/null +++ b/src/core/utility/configuration/tinyxml/ticpp.cpp @@ -0,0 +1,1150 @@ +/* +http://code.google.com/p/ticpp/ +Copyright (c) 2006 Ryan Pusztai, Ryan Mulder + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifdef TIXML_USE_TICPP + +#include "ticpp.h" +#include "ticpprc.h" +#include "tinyxml.h" +#include + +using namespace ticpp; + +// In the following Visitor functions, casting away const should be safe, as the object can only be referred to by a const & +bool Visitor::VisitEnter( const TiXmlDocument& doc ) +{ + return VisitEnter( Document( const_cast< TiXmlDocument* >( &doc ) ) ); +} + +bool Visitor::VisitExit( const TiXmlDocument& doc ) +{ + return VisitEnter( Document( const_cast< TiXmlDocument* >( &doc ) ) ); +} + +bool Visitor::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + if ( 0 != firstAttribute ) + { + Attribute attribute( const_cast< TiXmlAttribute* >( firstAttribute ) ); + return VisitEnter( Element( const_cast< TiXmlElement* >( &element ) ), &attribute ); + } + else + { + return VisitEnter( Element( const_cast< TiXmlElement* >( &element ) ), 0 ); + } +} + +bool Visitor::VisitExit( const TiXmlElement& element ) +{ + return VisitExit( Element( const_cast< TiXmlElement* >( &element ) ) ); +} + +bool Visitor::Visit( const TiXmlDeclaration& declaration ) +{ + return Visit( Declaration( const_cast< TiXmlDeclaration* >( &declaration ) ) ); +} + +bool Visitor::Visit( const TiXmlStylesheetReference& stylesheet ) +{ + return Visit( StylesheetReference( const_cast< TiXmlStylesheetReference* >( &stylesheet ) ) ); +} + +bool Visitor::Visit( const TiXmlText& text ) +{ + return Visit( Text( const_cast< TiXmlText* >( &text ) ) ); +} + +bool Visitor::Visit( const TiXmlComment& comment ) +{ + return Visit( Comment( const_cast< TiXmlComment* >( &comment ) ) ); +} + +Attribute::Attribute() +{ + SetTiXmlPointer( new TiXmlAttribute() ); + m_impRC->InitRef(); +} + +Attribute::Attribute( TiXmlAttribute* attribute ) +{ + SetTiXmlPointer( attribute ); + m_impRC->IncRef(); +} + +Attribute::Attribute( const std::string& name, const std::string& value ) +{ + SetTiXmlPointer( new TiXmlAttribute( name, value ) ); + m_impRC->InitRef(); +} + +void Attribute::operator=( const Attribute& copy ) +{ + // Dropping the reference to the old object + this->m_impRC->DecRef(); + + // Pointing to the new Object + SetTiXmlPointer( copy.m_tiXmlPointer ); + + // The internal tixml pointer changed in the above line + this->m_impRC->IncRef(); +} + +Attribute::Attribute( const Attribute& copy ) : Base() +{ + // Dropping the reference to the old object + this->m_impRC->DecRef(); + + // Pointing to the new Object + SetTiXmlPointer( copy.m_tiXmlPointer ); + + // The internal tixml pointer changed in the above line + this->m_impRC->IncRef(); +} + +Attribute::~Attribute() +{ + m_impRC->DecRef(); +} + +std::string Attribute::Value() const +{ + ValidatePointer(); + return m_tiXmlPointer->ValueStr(); +} + +std::string Attribute::Name() const +{ + ValidatePointer(); + return m_tiXmlPointer->Name(); +} + +Attribute* Attribute::Next( bool throwIfNoAttribute ) const +{ + ValidatePointer(); + TiXmlAttribute* attribute = m_tiXmlPointer->Next(); + if ( 0 == attribute ) + { + if ( throwIfNoAttribute ) + { + TICPPTHROW( "No more attributes found" ) + } + else + { + return 0; + } + } + + Attribute* temp = new Attribute( attribute ); + attribute->m_spawnedWrappers.push_back( temp ); + + return temp; +} + +Attribute* Attribute::Previous( bool throwIfNoAttribute ) const +{ + ValidatePointer(); + TiXmlAttribute* attribute = m_tiXmlPointer->Previous(); + if ( 0 == attribute ) + { + if ( throwIfNoAttribute ) + { + TICPPTHROW( "No more attributes found" ) + } + else + { + return 0; + } + } + + Attribute* temp = new Attribute( attribute ); + attribute->m_spawnedWrappers.push_back( temp ); + + return temp; +} + +void Attribute::IterateNext( const std::string&, Attribute** next ) const +{ + *next = Next( false ); +} + +void Attribute::IteratePrevious( const std::string&, Attribute** previous ) const +{ + *previous = Previous( false ); +} + +void Attribute::Print( FILE* file, int depth ) const +{ + ValidatePointer(); + m_tiXmlPointer->Print( file, depth ); +} + +void Attribute::SetTiXmlPointer( TiXmlAttribute* newPointer ) +{ + m_tiXmlPointer = newPointer; + SetImpRC( newPointer ); +} + +//***************************************************************************** + +Node* Node::NodeFactory( TiXmlNode* tiXmlNode, bool throwIfNull, bool rememberSpawnedWrapper ) const +{ + if ( 0 == tiXmlNode ) + { + if ( throwIfNull ) + { + TICPPTHROW( "tiXmlNode is NULL" ) + } + else + { + return 0; + } + } + + Node* temp; + switch ( tiXmlNode->Type() ) + { + case TiXmlNode::DOCUMENT: + temp = new Document( tiXmlNode->ToDocument() ); + break; + + case TiXmlNode::ELEMENT: + temp = new Element( tiXmlNode->ToElement() ); + break; + + case TiXmlNode::COMMENT: + temp = new Comment( tiXmlNode->ToComment() ); + break; + + case TiXmlNode::TEXT: + temp = new Text( tiXmlNode->ToText() ); + break; + + case TiXmlNode::DECLARATION: + temp = new Declaration( tiXmlNode->ToDeclaration() ); + break; + + case TiXmlNode::STYLESHEETREFERENCE: + temp = new StylesheetReference( tiXmlNode->ToStylesheetReference() ); + break; + + default: + TICPPTHROW( "Type is unsupported" ) + } + + if ( rememberSpawnedWrapper ) + { + tiXmlNode->m_spawnedWrappers.push_back( temp ); + } + return temp; +} + + +std::string Node::Value() const +{ + return GetTiXmlPointer()->ValueStr(); +} + +void Node::Clear() +{ + GetTiXmlPointer()->Clear(); +} + +Node* Node::Parent( bool throwIfNoParent ) const +{ + TiXmlNode* parent = GetTiXmlPointer()->Parent(); + if ( ( 0 == parent ) && throwIfNoParent ) + { + TICPPTHROW( "No parent exists" ); + } + + return NodeFactory( parent, false ); +} + +Node* Node::FirstChild( bool throwIfNoChildren ) const +{ + return FirstChild( "", throwIfNoChildren ); +} + +Node* Node::FirstChild( const std::string& value, bool throwIfNoChildren ) const +{ + return FirstChild( value.c_str(), throwIfNoChildren ); +} + +Node* Node::FirstChild( const char* value, bool throwIfNoChildren ) const +{ + TiXmlNode* childNode; + if ( 0 == strlen( value ) ) + { + childNode = GetTiXmlPointer()->FirstChild(); + } + else + { + childNode = GetTiXmlPointer()->FirstChild( value ); + } + + if ( ( 0 == childNode ) && throwIfNoChildren ) + { + TICPPTHROW( "Child with the value of \"" << value << "\" not found" ); + } + + return NodeFactory( childNode, false ); +} + +Node* Node::LastChild( bool throwIfNoChildren ) const +{ + return LastChild( "", throwIfNoChildren ); +} + +Node* Node::LastChild( const std::string& value, bool throwIfNoChildren ) const +{ + return LastChild( value.c_str(), throwIfNoChildren ); +} + +Node* Node::LastChild( const char* value, bool throwIfNoChildren ) const +{ + TiXmlNode* childNode; + if ( 0 == strlen( value ) ) + { + childNode = GetTiXmlPointer()->LastChild(); + } + else + { + childNode = GetTiXmlPointer()->LastChild( value ); + } + + if ( ( 0 == childNode ) && throwIfNoChildren ) + { + TICPPTHROW( "Child with the value of \"" << value << "\" not found" ); + } + + return NodeFactory( childNode, false ); +} + +Node* Node::IterateChildren ( Node* previous ) const +{ + TiXmlNode* pointer; + if ( 0 == previous ) + { + pointer = GetTiXmlPointer()->IterateChildren( 0 ); + } + else + { + pointer = GetTiXmlPointer()->IterateChildren( previous->GetTiXmlPointer() ); + } + + return NodeFactory( pointer, false ); +} + +Node* Node::IterateChildren( const std::string& value, Node* previous ) const +{ + TiXmlNode* pointer; + if ( 0 == previous ) + { + pointer = GetTiXmlPointer()->IterateChildren( value, 0 ); + } + else + { + pointer = GetTiXmlPointer()->IterateChildren( value, previous->GetTiXmlPointer() ); + } + + return NodeFactory( pointer, false ); +} + +Node* Node::InsertEndChild( Node& addThis ) +{ + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + TICPPTHROW( "Node is a Document and can't be inserted" ); + } + + TiXmlNode* pointer = GetTiXmlPointer()->InsertEndChild( *addThis.GetTiXmlPointer() ); + if ( 0 == pointer ) + { + TICPPTHROW( "Node can't be inserted" ); + } + + return NodeFactory( pointer ); +} + +Node* Node::LinkEndChild( Node* childNode ) +{ + if ( childNode->Type() == TiXmlNode::DOCUMENT ) + { + TICPPTHROW( "Node is a Document and can't be linked" ); + } + + // Increment reference count when adding to the tree + childNode->m_impRC->IncRef(); + + if ( 0 == GetTiXmlPointer()->LinkEndChild( childNode->GetTiXmlPointer() ) ) + { + TICPPTHROW( "Node can't be linked" ); + } + + return childNode; +} + +Node* Node::InsertBeforeChild( Node* beforeThis, Node& addThis ) +{ + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + TICPPTHROW( "Node is a Document and can't be inserted" ); + } + + // Increment reference count when adding to the tree + addThis.m_impRC->IncRef(); + + TiXmlNode* pointer = GetTiXmlPointer()->InsertBeforeChild( beforeThis->GetTiXmlPointer(), *addThis.GetTiXmlPointer() ); + if ( 0 == pointer ) + { + TICPPTHROW( "Node can't be inserted" ); + } + + return NodeFactory( pointer ); +} + +Node* Node::InsertAfterChild( Node* afterThis, Node& addThis ) +{ + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + TICPPTHROW( "Node is a Document and can't be inserted" ); + } + + // Increment reference count when adding to the tree + addThis.m_impRC->IncRef(); + + TiXmlNode* pointer = GetTiXmlPointer()->InsertAfterChild( afterThis->GetTiXmlPointer(), *addThis.GetTiXmlPointer() ); + if ( 0 == pointer ) + { + TICPPTHROW( "Node can't be inserted" ); + } + + return NodeFactory( pointer ); +} + +Node* Node::ReplaceChild( Node* replaceThis, Node& withThis ) +{ + if ( withThis.Type() == TiXmlNode::DOCUMENT ) + { + TICPPTHROW( "Node is a Document and can't be inserted" ); + } + + // Increment reference count when adding to the tree + withThis.m_impRC->IncRef(); + + TiXmlNode* pointer = GetTiXmlPointer()->ReplaceChild( replaceThis->GetTiXmlPointer(), *withThis.GetTiXmlPointer() ); + if ( 0 == pointer ) + { + TICPPTHROW( "Node can't be inserted" ); + } + + return NodeFactory( pointer ); +} + +void Node::RemoveChild( Node* removeThis ) +{ + if ( !GetTiXmlPointer()->RemoveChild( removeThis->GetTiXmlPointer() ) ) + { + TICPPTHROW( "Node to remove (" << removeThis->Value() << ") is not a child of this Node (" << Value() << ")" ) + } +} + +Node* Node::PreviousSibling( bool throwIfNoSiblings ) const +{ + return PreviousSibling( "", throwIfNoSiblings ); +} + +Node* Node::PreviousSibling( const std::string& value, bool throwIfNoSiblings ) const +{ + return PreviousSibling( value.c_str(), throwIfNoSiblings ); +} + +Node* Node::PreviousSibling( const char* value, bool throwIfNoSiblings ) const +{ + TiXmlNode* sibling; + if ( 0 == strlen( value ) ) + { + sibling = GetTiXmlPointer()->PreviousSibling(); + } + else + { + sibling = GetTiXmlPointer()->PreviousSibling( value ); + } + + if ( ( 0 == sibling ) && throwIfNoSiblings ) + { + TICPPTHROW( "No Siblings found with value, '" << value << "', Prior to this Node (" << Value() << ")" ) + } + + return NodeFactory( sibling, false ); +} + +Node* Node::NextSibling( bool throwIfNoSiblings ) const +{ + return NextSibling( "", throwIfNoSiblings ); +} + +Node* Node::NextSibling( const std::string& value, bool throwIfNoSiblings ) const +{ + return NextSibling( value.c_str(), throwIfNoSiblings ); +} + +Node* Node::NextSibling( const char* value, bool throwIfNoSiblings ) const +{ + TiXmlNode* sibling; + if ( 0 == strlen( value ) ) + { + sibling = GetTiXmlPointer()->NextSibling(); + } + else + { + sibling = GetTiXmlPointer()->NextSibling( value ); + } + + if ( ( 0 == sibling ) && throwIfNoSiblings ) + { + TICPPTHROW( "No Siblings found with value, '" << value << "', After this Node (" << Value() << ")" ) + } + + return NodeFactory( sibling, false ); +} + +Element* Node::NextSiblingElement( bool throwIfNoSiblings ) const +{ + return NextSiblingElement( "", throwIfNoSiblings ); +} + +Element* Node::NextSiblingElement( const std::string& value, bool throwIfNoSiblings ) const +{ + return NextSiblingElement( value.c_str(), throwIfNoSiblings ); +} + +Element* Node::NextSiblingElement( const char* value, bool throwIfNoSiblings ) const +{ + TiXmlElement* sibling; + if ( 0 == strlen( value ) ) + { + sibling = GetTiXmlPointer()->NextSiblingElement(); + } + else + { + sibling = GetTiXmlPointer()->NextSiblingElement( value ); + } + + if ( 0 == sibling ) + { + if ( throwIfNoSiblings ) + { + TICPPTHROW( "No Element Siblings found with value, '" << value << "', After this Node (" << Value() << ")" ) + } + else + { + return 0; + } + } + + Element* temp = new Element( sibling ); + sibling->m_spawnedWrappers.push_back( temp ); + + return temp; +} + +Element* Node::FirstChildElement( bool throwIfNoChildren ) const +{ + return FirstChildElement( "", throwIfNoChildren ); +} + +Element* Node::FirstChildElement( const std::string& value, bool throwIfNoChildren ) const +{ + return FirstChildElement( value.c_str(), throwIfNoChildren ); +} + +Element* Node::FirstChildElement( const char* value, bool throwIfNoChildren ) const +{ + TiXmlElement* element; + if ( 0 == strlen( value ) ) + { + element = GetTiXmlPointer()->FirstChildElement(); + } + else + { + element = GetTiXmlPointer()->FirstChildElement( value ); + } + + if ( 0 == element ) + { + if( throwIfNoChildren ) + { + TICPPTHROW( "Element (" << Value() << ") does NOT contain a child with the value of '" << value << "'" ) + } + else + { + return 0; + } + } + + Element* temp = new Element( element ); + element->m_spawnedWrappers.push_back( temp ); + + return temp; +} + +int Node::Type() const +{ + return GetTiXmlPointer()->Type(); +} + +Document* Node::GetDocument( bool throwIfNoDocument ) const +{ + TiXmlDocument* doc = GetTiXmlPointer()->GetDocument(); + if ( 0 == doc ) + { + if( throwIfNoDocument ) + { + TICPPTHROW( "This node (" << Value() << ") is not linked under a document" ) + } + else + { + return 0; + } + } + Document* temp = new Document( doc ); + doc->m_spawnedWrappers.push_back( temp ); + + return temp; +} + +bool Node::NoChildren() const +{ + return GetTiXmlPointer()->NoChildren(); +} + +Document* Node::ToDocument() const +{ + TiXmlDocument* doc = GetTiXmlPointer()->ToDocument(); + if ( 0 == doc ) + { + TICPPTHROW( "This node (" << Value() << ") is not a Document" ) + } + Document* temp = new Document( doc ); + doc->m_spawnedWrappers.push_back( temp ); + + return temp; +} + +Element* Node::ToElement() const +{ + TiXmlElement* doc = GetTiXmlPointer()->ToElement(); + if ( 0 == doc ) + { + TICPPTHROW( "This node (" << Value() << ") is not a Element" ) + } + Element* temp = new Element( doc ); + doc->m_spawnedWrappers.push_back( temp ); + + return temp; +} + +Comment* Node::ToComment() const +{ + TiXmlComment* doc = GetTiXmlPointer()->ToComment(); + if ( 0 == doc ) + { + TICPPTHROW( "This node (" << Value() << ") is not a Comment" ) + } + Comment* temp = new Comment( doc ); + doc->m_spawnedWrappers.push_back( temp ); + + return temp; +} + +Text* Node::ToText() const +{ + TiXmlText* doc = GetTiXmlPointer()->ToText(); + if ( 0 == doc ) + { + TICPPTHROW( "This node (" << Value() << ") is not a Text" ) + } + Text* temp = new Text( doc ); + doc->m_spawnedWrappers.push_back( temp ); + + return temp; +} + +Declaration* Node::ToDeclaration() const +{ + TiXmlDeclaration* doc = GetTiXmlPointer()->ToDeclaration(); + if ( 0 == doc ) + { + TICPPTHROW( "This node (" << Value() << ") is not a Declaration" ) + } + Declaration* temp = new Declaration( doc ); + doc->m_spawnedWrappers.push_back( temp ); + + return temp; +} + +StylesheetReference* Node::ToStylesheetReference() const +{ + TiXmlStylesheetReference* doc = GetTiXmlPointer()->ToStylesheetReference(); + if ( 0 == doc ) + { + TICPPTHROW( "This node (" << Value() << ") is not a StylesheetReference" ) + } + StylesheetReference* temp = new StylesheetReference( doc ); + doc->m_spawnedWrappers.push_back( temp ); + + return temp; +} + +std::auto_ptr< Node > Node::Clone() const +{ + TiXmlNode* node = GetTiXmlPointer()->Clone(); + if ( 0 == node ) + { + TICPPTHROW( "Node could not be cloned" ); + } + std::auto_ptr< Node > temp( NodeFactory( node, false, false ) ); + + // Take ownership of the memory from TiXml + temp->m_impRC->InitRef(); + + return temp; +} + +bool Node::Accept( TiXmlVisitor* visitor ) const +{ + return GetTiXmlPointer()->Accept( visitor ); +} + +//***************************************************************************** + +Comment::Comment() +: NodeImp< TiXmlComment >( new TiXmlComment() ) +{ + m_impRC->InitRef(); +} + +Comment::Comment( TiXmlComment* comment ) +: NodeImp< TiXmlComment >( comment ) +{ +} + +Comment::Comment( const std::string& comment ) +: NodeImp< TiXmlComment >( new TiXmlComment() ) +{ + m_impRC->InitRef(); + m_tiXmlPointer->SetValue( comment ); +} + +//***************************************************************************** + +Text::Text() +: NodeImp< TiXmlText >( new TiXmlText("") ) +{ + m_impRC->InitRef(); +} + + +Text::Text( const std::string& value ) +: NodeImp< TiXmlText >( new TiXmlText( value ) ) +{ + m_impRC->InitRef(); +} + +Text::Text( TiXmlText* text ) +: NodeImp< TiXmlText >( text ) +{ +} + + +//***************************************************************************** + +Document::Document() +: NodeImp< TiXmlDocument >( new TiXmlDocument() ) +{ + m_impRC->InitRef(); +} + +Document::Document( TiXmlDocument* document ) +: NodeImp< TiXmlDocument >( document ) +{ +} + +Document::Document( const char* documentName ) +: NodeImp< TiXmlDocument >( new TiXmlDocument( documentName ) ) +{ + m_impRC->InitRef(); +} + +Document::Document( const std::string& documentName ) +: NodeImp< TiXmlDocument >( new TiXmlDocument( documentName ) ) +{ + m_impRC->InitRef(); +} + +void Document::LoadFile( TiXmlEncoding encoding ) +{ + if ( !m_tiXmlPointer->LoadFile( encoding ) ) + { + TICPPTHROW( "Couldn't load " << m_tiXmlPointer->Value() ); + } +} + +void Document::SaveFile( void ) const +{ + if ( !m_tiXmlPointer->SaveFile() ) + { + TICPPTHROW( "Couldn't save " << m_tiXmlPointer->Value() ); + } +} + +void Document::LoadFile( const std::string& filename, TiXmlEncoding encoding ) +{ + if ( !m_tiXmlPointer->LoadFile( filename.c_str(), encoding ) ) + { + TICPPTHROW( "Couldn't load " << filename ); + } +} + +void Document::LoadFile( const char* filename, TiXmlEncoding encoding ) +{ + if ( !m_tiXmlPointer->LoadFile( filename, encoding ) ) + { + TICPPTHROW( "Couldn't load " << filename ); + } +} + +void Document::SaveFile( const std::string& filename ) const +{ + if ( !m_tiXmlPointer->SaveFile( filename.c_str() ) ) + { + TICPPTHROW( "Couldn't save " << filename ); + } +} + +void Document::Parse( const std::string& xml, bool throwIfParseError, TiXmlEncoding encoding ) +{ + m_tiXmlPointer->Parse( xml.c_str(), 0, encoding ); + if( throwIfParseError && m_tiXmlPointer->Error() ) + { + TICPPTHROW( "Error parsing xml." ); + } +} + +//***************************************************************************** + +Element::Element() +: NodeImp< TiXmlElement >( new TiXmlElement( "DefaultValueCausedByCreatingAnElementWithNoParameters" ) ) +{ + m_impRC->InitRef(); +} + +Element::Element( const std::string& value ) +: NodeImp< TiXmlElement >( new TiXmlElement( value ) ) +{ + m_impRC->InitRef(); +} + +Element::Element( const char* value ) +: NodeImp< TiXmlElement >( new TiXmlElement( value ) ) +{ + m_impRC->InitRef(); +} + +Element::Element( TiXmlElement* element ) +: NodeImp< TiXmlElement >( element ) +{ +} + +Attribute* Element::FirstAttribute( bool throwIfNoAttributes ) const +{ + ValidatePointer(); + TiXmlAttribute* attribute = m_tiXmlPointer->FirstAttribute(); + if ( ( 0 == attribute ) && throwIfNoAttributes ) + { + TICPPTHROW( "This Element (" << Value() << ") has no attributes" ) + } + + if ( 0 == attribute ) + { + if( throwIfNoAttributes ) + { + TICPPTHROW( "Element (" << Value() << ") has no attributes" ) + } + else + { + return 0; + } + } + + Attribute* temp = new Attribute( attribute ); + attribute->m_spawnedWrappers.push_back( temp ); + + return temp; +} + +Attribute* Element::LastAttribute( bool throwIfNoAttributes ) const +{ + ValidatePointer(); + TiXmlAttribute* attribute = m_tiXmlPointer->LastAttribute(); + if ( ( 0 == attribute ) && throwIfNoAttributes ) + { + TICPPTHROW( "This Element (" << Value() << ") has no attributes" ) + } + + if ( 0 == attribute ) + { + if( throwIfNoAttributes ) + { + TICPPTHROW( "Element (" << Value() << ") has no attributes" ) + } + else + { + return 0; + } + } + + Attribute* temp = new Attribute( attribute ); + attribute->m_spawnedWrappers.push_back( temp ); + + return temp; +} + +std::string Element::GetAttributeOrDefault( const std::string& name, const std::string& defaultValue ) const +{ + std::string value; + if ( !GetAttributeImp( name, &value ) ) + { + return defaultValue; + } + return value; +} + +std::string Element::GetAttribute( const std::string& name ) const +{ + return GetAttributeOrDefault( name, std::string() ); +} + +bool Element::HasAttribute( const std::string& name ) const +{ + ValidatePointer(); + return ( 0 != m_tiXmlPointer->Attribute( name.c_str() ) ); +} + +void Element::RemoveAttribute( const std::string& name ) +{ + ValidatePointer(); + m_tiXmlPointer->RemoveAttribute( name.c_str() ); +} + +bool Element::GetAttributeImp( const std::string& name, std::string* value ) const +{ + ValidatePointer(); + + // Get value from TinyXML, if the attribute exists + const char* retVal = m_tiXmlPointer->Attribute( name.c_str() ); + + // TinyXML returns NULL if the attribute doesn't exist + if ( 0 == retVal ) + { + return false; + } + else + { + *value = retVal; + return true; + } +} + +bool Element::GetTextImp( std::string* value ) const +{ + ValidatePointer(); + + // Get value from TinyXML, if the attribute exists + const char* retVal = m_tiXmlPointer->GetText(); + + // TinyXML returns NULL if the attribute doesn't exist + if ( 0 == retVal ) + { + return false; + } + else + { + *value = retVal; + return true; + } +} + +//***************************************************************************** + +Declaration::Declaration() +: NodeImp< TiXmlDeclaration >( new TiXmlDeclaration() ) +{ + m_impRC->InitRef(); +} + +Declaration::Declaration( TiXmlDeclaration* declaration ) +: NodeImp< TiXmlDeclaration >( declaration ) +{ +} + +Declaration::Declaration( const std::string& version, const std::string& encoding, const std::string& standalone ) +: NodeImp< TiXmlDeclaration >( new TiXmlDeclaration( version, encoding, standalone ) ) +{ + m_impRC->InitRef(); +} + +std::string Declaration::Version() const +{ + return m_tiXmlPointer->Version(); +} + +std::string Declaration::Encoding() const +{ + return m_tiXmlPointer->Encoding(); +} + +std::string Declaration::Standalone() const +{ + return m_tiXmlPointer->Standalone(); +} + +//***************************************************************************** + +StylesheetReference::StylesheetReference() +: NodeImp< TiXmlStylesheetReference >( new TiXmlStylesheetReference() ) +{ + m_impRC->InitRef(); +} + +StylesheetReference::StylesheetReference( TiXmlStylesheetReference* stylesheetReference ) +: NodeImp< TiXmlStylesheetReference >( stylesheetReference ) +{ +} + +StylesheetReference::StylesheetReference( const std::string& type, const std::string& href ) +: NodeImp< TiXmlStylesheetReference >( new TiXmlStylesheetReference( type, href ) ) +{ + m_impRC->InitRef(); +} + +std::string StylesheetReference::Type() const +{ + return m_tiXmlPointer->Type(); +} + +std::string StylesheetReference::Href() const +{ + return m_tiXmlPointer->Href(); +} + +//***************************************************************************** + +Exception::Exception(const std::string &details) +: +m_details( details ) +{ + +} + +Exception::~Exception() throw() +{ +} + +const char* Exception::what() const throw() +{ + return m_details.c_str(); +} + +//***************************************************************************** + +TiCppRC::TiCppRC() +{ + // Spawn reference counter for this object + m_tiRC = new TiCppRCImp( this ); +} + +void TiCppRC::DeleteSpawnedWrappers() +{ + std::vector< Base* >::reverse_iterator wrapper; + for ( wrapper = m_spawnedWrappers.rbegin(); wrapper != m_spawnedWrappers.rend(); ++wrapper ) + { + delete *wrapper; + } + m_spawnedWrappers.clear(); +} + +TiCppRC::~TiCppRC() +{ + DeleteSpawnedWrappers(); + + // Set pointer held by reference counter to NULL + this->m_tiRC->Nullify(); + + // Decrement reference - so reference counter will delete itself if necessary + this->m_tiRC->DecRef(); +} + +//***************************************************************************** + +TiCppRCImp::TiCppRCImp( TiCppRC* tiCppRC ) + : m_count( 1 ), m_tiCppRC ( tiCppRC ) +{ +} + +void TiCppRCImp::IncRef() +{ + m_count++; +} + +void TiCppRCImp::DecRef() +{ + m_count--; + if ( 0 == m_count ) + { + delete m_tiCppRC; + delete this; + } +} + +void TiCppRCImp::InitRef() +{ + m_count = 1; +} + +void TiCppRCImp::Nullify() +{ + m_tiCppRC = 0; +} + +TiCppRC* TiCppRCImp::Get() +{ + return m_tiCppRC; +} + +bool TiCppRCImp::IsNull() +{ + return 0 == m_tiCppRC; +} + +#endif // TIXML_USE_TICPP diff --git a/src/core/utility/configuration/tinyxml/ticpp.h b/src/core/utility/configuration/tinyxml/ticpp.h new file mode 100644 index 00000000..44dc106c --- /dev/null +++ b/src/core/utility/configuration/tinyxml/ticpp.h @@ -0,0 +1,1902 @@ +/* +http://code.google.com/p/ticpp/ +Copyright (c) 2006 Ryan Pusztai, Ryan Mulder + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/** +@copydoc ticpp +@file +@author Ryan Pusztai +@author Ryan Mulder +@date 04/11/2006 + +@version 0.04a by edam@waxworlds.org: based Exception based on std::exception; added stream + << and >> support; added Document::Parse(); bug fix; improved THROW() macro. +@version 0.04 Added NodeImp class. Also made all the classes inherit from NodeImp. +@version 0.03 Added Declaration class +@version 0.02 Added Element class +@version 0.01 Added Exception class, Document class + +@todo add UNKNOWN support. See ticpp::NodeFactory. +@todo add TYPECOUNT support. See ticpp::NodeFactory. +@todo Add a quick reference +*/ +#ifdef TIXML_USE_TICPP + +#ifndef TICPP_INCLUDED +#define TICPP_INCLUDED + +#include "tinyxml.h" +#include +#include +#include +#include +#include + +/** +@subpage ticpp is a TinyXML wrapper that uses a lot more C++ ideals. +It throws exceptions, uses templates, is in its own name space, and +requires STL (Standard Template Library). This is done to ease the use +of getting values in and out of the xml. + +If you don't perfer to use some of the concepts just don't use it. +It is just a wrapper that extends TinyXML. It doesn't actually change +any of TinyXML. +*/ +namespace ticpp +{ + /** + This is a ticpp exception class + */ + class Exception : public std::exception + { + public: + /** + Construct an exception with a message + */ + Exception( const std::string& details ); + ~Exception() throw(); + + /// Override std::exception::what() to return m_details + const char* what() const throw(); + + std::string m_details; /**< Exception Details */ + }; + + /** + This allows you to stream your exceptions in. + It will take care of the conversion and throwing the exception. + */ + #define TICPPTHROW( message ) \ + { \ + std::ostringstream full_message; \ + std::string file( __FILE__ ); \ + file = file.substr( file.find_last_of( "\\/" ) + 1 ); \ + full_message << message << " <" << file << "@" << __LINE__ << ">"; \ + full_message << BuildDetailedErrorString(); \ + throw Exception( full_message.str() ); \ + } + + // Forward Declarations for Visitor, and others. + class Document; + class Element; + class Declaration; + class StylesheetReference; + class Text; + class Comment; + class Attribute; + + /** Wrapper around TiXmlVisitor */ + class Visitor : public TiXmlVisitor + { + public: + // Overload the TiXmlVisitor functions, wrap objects, call ticpp::Visitor functions + /// @internal + virtual bool VisitEnter( const TiXmlDocument& doc ); + /// @internal + virtual bool VisitExit( const TiXmlDocument& doc ); + /// @internal + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + /// @internal + virtual bool VisitExit( const TiXmlElement& element ); + /// @internal + virtual bool Visit( const TiXmlDeclaration& declaration ); + /// @internal + virtual bool Visit( const TiXmlStylesheetReference& stylesheet ); + /// @internal + virtual bool Visit( const TiXmlText& text ); + /// @internal + virtual bool Visit( const TiXmlComment& comment ); + + public: + /// Visit a document. + virtual bool VisitEnter( const Document& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const Document& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const Element& /*element*/, const Attribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const Element& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const Declaration& /*declaration*/ ) { return true; } + /// Visit a stylesheet reference + virtual bool Visit( const StylesheetReference& /*stylesheet*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const Text& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const Comment& /*comment*/ ) { return true; } + }; + + /** Wrapper around TiXmlBase */ + class Base + { + public: + + /** + Converts any class with a proper overload of the << opertor to a std::string + @param value The value to be converted + @throws Exception When value cannot be converted to a std::string + */ + template < class T > + std::string ToString( const T& value ) const + { + std::stringstream convert; + convert << value; + if ( convert.fail() ) + { + TICPPTHROW( "Could not convert value to text" ); + } + return convert.str(); + } + + std::string ToString( const std::string& value ) const + { + return value; + } + + /** + Converts a std::string to any class with a proper overload of the >> opertor + @param temp The string to be converted + @param out [OUT] The container for the returned value + @throws Exception When temp cannot be converted to the target type + */ + template < class T > + void FromString( const std::string& temp, T* out ) const + { + std::istringstream val( temp ); + val >> *out; + + if ( val.fail() ) + { + TICPPTHROW( "Could not convert \"" << temp << "\" to target type" ); + } + } + + /** + Specialization for std::string + */ + void FromString( const std::string& temp, std::string* out ) const + { + *out = temp; + } + + /** + Return the position, in the original source file, of this node or attribute. + Wrapper around TiXmlBase::Row() + */ + int Row() const + { + return GetBasePointer()->Row(); + } + + /** + Return the position, in the original source file, of this node or attribute. + Wrapper around TiXmlBase::Row() + */ + int Column() const + { + return GetBasePointer()->Column(); + } + + /** + Compare internal TiXml pointers to determine is both are wrappers around the same node + */ + bool operator == ( const Base& rhs ) const + { + return ( GetBasePointer() == rhs.GetBasePointer() ); + } + + /** + Compare internal TiXml pointers to determine is both are wrappers around the same node + */ + bool operator != ( const Base& rhs ) const + { + return ( GetBasePointer() != rhs.GetBasePointer() ); + } + + /** + Builds detailed error string using TiXmlDocument::Error() and others + */ + std::string BuildDetailedErrorString() const + { + std::ostringstream full_message; + #ifndef TICPP_NO_RTTI + TiXmlNode* node = dynamic_cast< TiXmlNode* >( GetBasePointer() ); + if ( node != 0 ) + { + TiXmlDocument* doc = node->GetDocument(); + if ( doc != 0 ) + { + if ( doc->Error() ) + { + full_message << "\nDescription: " << doc->ErrorDesc() + << "\nFile: " << (strlen( doc->Value() ) > 0 ? doc->Value() : "") + << "\nLine: " << doc->ErrorRow() + << "\nColumn: " << doc->ErrorCol(); + } + } + } + #endif + return full_message.str(); + } + + /** + Destructor + */ + virtual ~Base() + { + } + + protected: + mutable TiCppRCImp* m_impRC; /**< Holds status of internal TiXmlPointer - use this to determine if object has been deleted already */ + + /** + @internal + Updates the pointer to the reference counter to point at the counter in the new node. + + @param node TiXmlBase containing the new reference counter + */ + void SetImpRC( TiXmlBase* node ) + { + m_impRC = node->m_tiRC; + } + + void ValidatePointer() const + { + if ( m_impRC->IsNull() ) + { + TICPPTHROW( "Internal TiXml Pointer is NULL" ); + } + } + + /** + @internal + Get internal TiXmlBase* + */ + virtual TiXmlBase* GetBasePointer() const = 0; + }; + + /** + Wrapper around TiXmlAttribute + */ + class Attribute : public Base + { + private: + TiXmlAttribute* m_tiXmlPointer; + TiXmlBase* GetBasePointer() const + { + ValidatePointer(); + return m_tiXmlPointer; + } + + public: + /** + Construct an empty attribute. + */ + Attribute(); + + /** + Construct an attribute with @a name and @a value + + @param name The name of the attribute + @param value The value of the attribute + */ + Attribute( const std::string& name, const std::string& value ); + + /** + @internal + Construct an attribute with the internal pointer + + @param attribute The internal pointer + */ + Attribute( TiXmlAttribute* attribute ); + + /** + Get the value of this attribute + Uses Base::FromString to convert TiXmlAttribute::ValueStr from a std::string, + and puts it in the passed pointer. + + @param value [OUT] A pointer to fill with the value + */ + template < class T > + void GetValue( T* value ) const + { + ValidatePointer(); + FromString( m_tiXmlPointer->ValueStr(), value ); + } + + /** + Get the value of this attribute. + Simple wrapper for TiXmlAttribute::ValueStr. + + @see GetValue + */ + std::string Value() const; + + /** + Set the value of this node. + Uses Base::ToString to convert value to a std::string, then calls TiXmlAttribute::SetValue. + + @param value The value to set + */ + template < class T > + void SetValue( const T& value ) + { + ValidatePointer(); + m_tiXmlPointer->SetValue( ToString( value ) ); + } + + /** + Get the value of this attribute + Uses Base::FromString to convert TiXmlAttribute::Name from a std::string, + and puts it in the passed pointer. + + @param name [OUT] A pointer to fill with the name + */ + template < class T > + void GetName( T* name ) const + { + ValidatePointer(); + FromString( m_tiXmlPointer->Name(), name ); + } + + /** + Get the value of this attribute. + Simple wrapper for TiXmlAttribute::Name. + + @see GetName + */ + std::string Name() const; + + /** + Set the value of this attribute. + Uses Base::ToString to convert @a name to a std::string, then calls TiXmlAttribute::SetName. + + @param name The name to set + */ + template < class T > + void SetName( const T& name ) + { + ValidatePointer(); + m_tiXmlPointer->SetName( ToString( name ) ); + } + + /** + @internal + Updates the reference count for the old and new pointers. + */ + void operator=( const Attribute& copy ); + + /** + @internal + Updates the reference count for the old and new pointers. + */ + Attribute( const Attribute& copy ); + + /* + Decrements reference count. + */ + ~Attribute(); + + /** + Get the next sibling attribute in the DOM. + */ + Attribute* Next( bool throwIfNoAttribute = true ) const; + + /** + Get the previous sibling attribute in the DOM. + */ + Attribute* Previous( bool throwIfNoAttribute = true ) const; + + /** + @internal + Just for Iterator<> + + @param next [OUT] The pointer to the next valid attribute + @return true if there is a next attribute, false if not + */ + void IterateNext( const std::string&, Attribute** next ) const; + + /** + @internal + Just for Iterator<> + + @param previous [OUT] The pointer to the previous valid attribute + @return true if there is a previous attribute, false if not + */ + void IteratePrevious( const std::string&, Attribute** previous ) const; + + /** + All TinyXml classes can print themselves to a filestream. + */ + virtual void Print( FILE* file, int depth ) const; + + private: + + /** + @internal + Sets the internal pointer. + Saves a copy of the pointer to the RC object. + + @param newPointer TiXmlAttribute* to set. + */ + void SetTiXmlPointer( TiXmlAttribute* newPointer ); + }; + + /** + Wrapper around TiXmlNode + */ + class Node : public Base + { + public: + + /** + Get the value of this node + Uses Base::FromString to convert TiXmlNode::ValueStr from a std::string, + and puts it in the passed pointer. + + @param value [OUT] A pointer to fill with the value + */ + template < class T > + void GetValue( T* value) const + { + FromString( GetTiXmlPointer()->ValueStr(), value ); + } + + /** + Get the value of this node. + Simple wrapper for TiXmlNode::ValueStr. + + @see GetValue + */ + std::string Value() const; + + /** + Set the value of this node. + Uses Base::ToString to convert value to a std::string, then calls TiXmlNode::SetValue. + + @param value The value to set + */ + template < class T > + void SetValue( const T& value ) + { + GetTiXmlPointer()->SetValue( ToString( value ) ); + } + + /** + Clear all Nodes below this. + Simple wrapper for TiXmlNode::Clear. + */ + void Clear(); + + /** + The Parent of this Node. + Simple wrapper for TiXmlNode::Parent. + + @param throwIfNoParent [DEF] If true, throws when Parent = NULL. + @return The parent of this node, NULL if there is no Parent. + @throws Exception When throwIfNoParent is true, and TiXmlNode::Parent returns Null. + */ + Node* Parent( bool throwIfNoParent = true ) const; + + /** + The first child of this node. + + @param throwIfNoChildren [DEF] If true, will throw an exception if there are no children. + @return Pointer to child, Null if no children and 'throwIfNoChildren' is false. + @throws Exception When throwIfNoChildren is true, and TiXmlNode::FirstChild returns Null. + + @see TiXmlNode::FirstChild + */ + Node* FirstChild( bool throwIfNoChildren = true ) const; + + /** + @internal + The first child of this node with the matching @a value. + + @overload + @param value Value to match. + @param throwIfNoChildren [DEF] If true, will throw an exception if there are no children. + + @see FirstChild( bool throwIfNoChildren = true ) + */ + Node* FirstChild( const char* value, bool throwIfNoChildren = true ) const; + + /** + The first child of this node with the matching @a value. + + @overload + @param value Value to match. + @param throwIfNoChildren [DEF] If true, will throw an exception if there are no children. + + @see FirstChild( const char* value, bool throwIfNoChildren = true ) + */ + Node* FirstChild( const std::string& value, bool throwIfNoChildren = true ) const; + + /** + The last child of this node. + + @param throwIfNoChildren [DEF] If true, will throw an exception if there are no children. + @return Pointer to child, Null if no children and 'throwIfNoChildren' is false. + @throws Exception When throwIfNoChildren is true, and TiXmlNode::LastChild returns Null. + + @see TiXmlNode::LastChild + */ + Node* LastChild( bool throwIfNoChildren = true ) const; + + /** + @internal + The last child of this node with the matching @a value. + + @overload + @param value Value to match. + @param throwIfNoChildren [DEF] If true, will throw an exception if there are no children. + + @see LastChild( bool throwIfNoChildren = true ) + */ + Node* LastChild( const char* value, bool throwIfNoChildren = true ) const; + + /** + The last child of this node with the matching @a value. + + @overload + @param value Value to match. + @param throwIfNoChildren [DEF] If true, will throw an exception if there are no children. + + @see LastChild( const char* value, bool throwIfNoChildren = true ) + */ + Node* LastChild( const std::string& value, bool throwIfNoChildren = true ) const; + + /** + An alternate way to walk the children of a node. + Simple wrapper for TiXmlNode::IterateChildren. + + @param previous The previous Node* that was returned from IterateChildren. + @return NULL When there are no more children. + */ + Node* IterateChildren( Node* previous ) const; + + /** + This flavor of IterateChildren searches for children with a particular @a value. + Simple wrapper for TiXmlNode::IterateChildren. + + @param value The value you want to search for. + @param previous The previous Node* that was returned from IterateChildren. + @return NULL When there are no more children. + */ + Node* IterateChildren( const std::string& value, Node* previous ) const; + + /** + Adds a child past the LastChild. + Throws if you try to insert a document. + + @note This takes a copy of @a addThis so it is not as efficiant as LinkEndChild. + @param addThis Node to insert. + @throws Exception When TiXmlNode::InsertEndChild returns Null + + @see LinkEndChild + @see TiXmlNode::InsertEndChild + */ + Node* InsertEndChild( Node& addThis ); + + /** + Adds a child past the LastChild. + Throws if you try to link a document. + + @param childNode Node to link. + @throws Exception When TiXmlNode::LinkEndChild returns Null. + + @see InsertEndChild + @see TiXmlNode::LinkEndChild + */ + Node* LinkEndChild( Node* childNode ); + + /** + Adds a child before the specified child. + Throws if you try to insert a document. + + @param beforeThis Node that will have @a addThis linked before. + @param addThis Node to insert before. + @throws Exception When TiXmlNode::InsertBeforeChild returns Null. + + @see InsertAfterChild + @see TiXmlNode::InsertBeforeChild + */ + Node* InsertBeforeChild( Node* beforeThis, Node& addThis ); + + /** + Adds a child after the specified child. + Throws if you try to insert a document. + + @param afterThis Node that will have @a addThis linked after. + @param addThis Node to insert after. + @throws Exception When TiXmlNode::InsertAfterChild returns Null. + + @see InsertBeforeChild + @see TiXmlNode::InsertAfterChild + */ + Node* InsertAfterChild( Node* afterThis, Node& addThis ); + + /** + Replace a child of this node. + Throws if you try to replace with a document. + + @param replaceThis Node to replace. + @param withThis Node that is replacing @a replaceThis. + @throws Exception When TiXmlNode::ReplaceChild returns Null. + + @see TiXmlNode::ReplaceChild + */ + Node* ReplaceChild( Node* replaceThis, Node& withThis ); + + /** + Delete a child of this node. + + @param removeThis Node to delete. + @throws Exception When removeThis is not a child of this Node. + + @see TiXmlNode::RemoveChild + */ + void RemoveChild( Node* removeThis ); + + /** + Navigate to a sibling node. + Wrapper around TiXmlNode::PreviousSibling. + + @param throwIfNoSiblings [DEF] If true, will throw an exception if there are no siblings. + @return Pointer to sibling, Null if no siblings and 'throwIfNoSiblings' is false. + @throws Exception When TiXmlNode::PreviousSibling returns Null and 'throwIfNoSiblings' is true. + */ + Node* PreviousSibling( bool throwIfNoSiblings = true ) const; + + /** + Navigate to a sibling node with the given @a value. + + @overload + @param value The value of the node to look for. + @param throwIfNoSiblings [DEF] If true, will throw an exception if there are no siblings. + + @see PreviousSibling( bool throwIfNoSiblings ) + */ + Node* PreviousSibling( const std::string& value, bool throwIfNoSiblings = true ) const; + + /** + @internal + Navigate to a sibling node with the given @a value. + + @overload + @param value The value of the node to look for. + @param throwIfNoSiblings [DEF] If true, will throw an exception if there are no siblings. + + @see PreviousSibling( const std::string& value, bool throwIfNoSiblings ) + */ + Node* PreviousSibling( const char* value, bool throwIfNoSiblings = true ) const; + + /** + Navigate to a sibling node. + Wrapper around TiXmlNode::NextSibling. + + @param throwIfNoSiblings [DEF] If true, will throw an exception if there are no siblings. + @return Pointer to sibling, Null if no siblings and 'throwIfNoSiblings' is false. + @throws Exception When TiXmlNode::NextSibling returns Null and 'throwIfNoSiblings' is true. + */ + Node* NextSibling( bool throwIfNoSiblings = true ) const; + + /** + Navigate to a sibling node with the given @a value. + + @overload + @param value The value of the node to look for. + @param throwIfNoSiblings [DEF] If true, will throw an exception if there are no siblings. + + @see NextSibling( bool throwIfNoSiblings ) + */ + Node* NextSibling( const std::string& value, bool throwIfNoSiblings = true ) const; + + /** + @internal + Navigate to a sibling node with the given @a value. + + @overload + @param value The value of the node to look for. + @param throwIfNoSiblings [DEF] If true, will throw an exception if there are no siblings. + + @see NextSibling( const std::string& value, bool throwIfNoSiblings ) + */ + Node* NextSibling( const char* value, bool throwIfNoSiblings = true ) const; + + /** + @internal + Just for Iterator<> + + @param value The value of nodes to iterate through + @param next [OUT] The pointer to the first valid node + */ + template < class T > + void IterateFirst( const std::string& value, T** first ) const + { + *first = 0; + for( Node* child = FirstChild( value, false ); child; child = child->NextSibling( value, false ) ) + { + *first = dynamic_cast< T* >( child ); + if ( 0 != *first ) + { + return; + } + } + } + + virtual void IterateFirst( const std::string&, Attribute** ) const + { + TICPPTHROW( "Attributes can only be iterated with Elements." ) + } + + /** + @internal + Just for Iterator<> + + @param value The value of nodes to iterate through + @param next [OUT] The pointer to the next valid node + */ + template < class T > + void IterateNext( const std::string& value, T** next ) const + { + Node* sibling = NextSibling( value, false ); + *next = dynamic_cast< T* >( sibling ); + + while ( ( 0 != sibling ) && ( 0 == *next ) ) + { + sibling = sibling->NextSibling( value, false ); + *next = dynamic_cast< T* >( sibling ); + } + } + + /** + @internal + Just for Iterator<> + + @param value The value of nodes to iterate through + @param previous [OUT] The pointer to the previous valid node + */ + template < class T > + void IteratePrevious( const std::string& value, T** previous ) const + { + Node* sibling = PreviousSibling( value, false ); + *previous = dynamic_cast< T* >( sibling ); + + while ( ( 0 != sibling ) && ( 0 == *previous ) ) + { + sibling = sibling->PreviousSibling( value, false ); + *previous = dynamic_cast< T* >( sibling ); + } + } + + /** + Navigate to a sibling element. + Wrapper around TiXmlNode::NextSibling. + + @param throwIfNoSiblings [DEF] If true, will throw an exception if there are no sibling element. + @return Pointer to sibling, Null if no siblings and 'throwIfNoSiblings' is false. + @throws Exception When TiXmlNode::NextSibling returns Null and 'throwIfNoSiblings' is true. + */ + Element* NextSiblingElement( bool throwIfNoSiblings = true ) const; + + /** + Navigate to a sibling element with the given @a value. + + @overload + @param value The value of the element to look for. + @param throwIfNoSiblings [DEF] If true, will throw an exception if there are no sibling elements. + @see NextSiblingElement( bool throwIfNoSiblings ) + */ + Element* NextSiblingElement( const std::string& value, bool throwIfNoSiblings = true ) const; + + /** + @internal + Navigate to a sibling element with the given @a value. + + @overload + @param value The value of the element to look for. + @param throwIfNoSiblings [DEF] If true, will throw an exception if there are no sibling elements. + + @see NextSiblingElement( const std::string& value, bool throwIfNoSiblings ) + */ + Element* NextSiblingElement( const char* value, bool throwIfNoSiblings = true ) const; + + /** + The first child element of this node. + + @param throwIfNoChildren [DEF] If true, will throw an exception if there are no element children. + @return Pointer to child, Null if no element children and 'throwIfNoChildren' is false. + @throws Exception When throwIfNoChildren is true, and TiXmlNode::FirstChildElement returns Null. + + @see TiXmlNode::FirstChildElement + */ + Element* FirstChildElement( bool throwIfNoChildren = true ) const; + + /** + @internal + The first child element of this node with the matching @a value. + + @overload + @param value Value to match. + @param throwIfNoChildren [DEF] If true, will throw an exception if there are no element children. + + @see FirstChildElement( bool throwIfNoChildren = true ) + */ + Element* FirstChildElement( const char* value, bool throwIfNoChildren = true ) const; + + /** + The first child element of this node with the matching @a value. + + @overload + @param value Value to match. + @param throwIfNoChildren [DEF] If true, will throw an exception if there are no element children. + + @see FirstChildElement( const char* value, bool throwIfNoChildren = true ) + */ + Element* FirstChildElement( const std::string& value, bool throwIfNoChildren = true ) const; + + /** + Query the type (as TiXmlNode::NodeType ) of this node. + */ + int Type() const; + + /** + Return a pointer to the Document this node lives in. + + @param throwIfNoDocument [DEF] If true, will throw an exception if this node is not linked under a Document. + @return A pointer to the Document this node lives in, NULL if not linked under a Document, and 'throwIfNoDocument' is false. + @throws Exception When this node is not linked under a Document and 'throwIfNoDocument' is true. + */ + Document* GetDocument( bool throwIfNoDocument = true ) const; + + /** + Check if this node has no children. + + @return true if this node has no children. + */ + bool NoChildren() const; + + #ifndef TICPP_NO_RTTI + /** + Pointer conversion ( NOT OBJECT CONVERSION ) - replaces TiXmlNode::ToElement, TiXmlNode::ToDocument, TiXmlNode::ToComment, etc. + + @throws Exception When the target is not an object of class T + @warning Some ancient compilers do not support explicit specification of member template arguments, which this depends on ( e.g. VC6 ). + */ + template < class T > + T* To() const + { + T* pointer = dynamic_cast< T* >( this ); + if ( 0 == pointer ) + { + std::string thisType = typeid( this ).name(); + std::string targetType = typeid( T ).name(); + std::string thatType = typeid( *this ).name(); + TICPPTHROW( "The " << thisType.substr( 6 ) << " could not be casted to a " << targetType.substr( 6 ) + << " *, because the target object is not a " << targetType.substr( 6 ) << ". (It is a " << thatType.substr( 6 ) << ")" ); + } + return pointer; + } + #endif + + /** + Pointer conversion - replaces TiXmlNode::ToDocument. + + @throws Exception When this node is not a Document. + */ + Document* ToDocument() const; + + /** + Pointer conversion - replaces TiXmlNode::ToElement. + + @throws Exception When this node is not a Element. + */ + Element* ToElement() const; + + /** + Pointer conversion - replaces TiXmlNode::ToComment. + + @throws Exception When this node is not a Comment. + */ + Comment* ToComment() const; + + /** + Pointer conversion - replaces TiXmlNode::ToText. + + @throws Exception When this node is not a Text. + */ + Text* ToText() const; + + /** + Pointer conversion - replaces TiXmlNode::ToDeclaration. + + @throws Exception When this node is not a Declaration. + */ + Declaration* ToDeclaration() const; + + /** + Pointer conversion - replaces TiXmlNode::ToStylesheetReference. + + @throws Exception When this node is not a StylesheetReference. + */ + StylesheetReference* ToStylesheetReference() const; + + /** + Create an exact duplicate of this node and return it. + + @note Using auto_ptr to manage the memory declared on the heap by TiXmlNode::Clone. + @code + // Now using clone + ticpp::Document doc( "C:\\Test.xml" ); + ticpp::Node* sectionToClone; + sectionToClone = doc.FirstChild( "settings" ); + std::auto_ptr< ticpp::Node > clonedNode = sectionToClone->Clone(); + // Now you can use the clone. + ticpp::Node* node2 = clonedNode->FirstChildElement()->FirstChild(); + ... + // After the variable clonedNode goes out of scope it will automatically be cleaned up. + @endcode + @return Pointer the duplicate node. + */ + std::auto_ptr< Node > Clone() const; + + /** + Accept a hierchical visit the nodes in the TinyXML DOM. + @return The boolean returned by the visitor. + */ + bool Accept( TiXmlVisitor* visitor ) const; + + /** + Stream input operator. + */ + friend std::istream& operator >>( std::istream& in, Node& base ) + { + in >> *base.GetTiXmlPointer(); + return in; + } + + /** + Stream output operator. + */ + friend std::ostream& operator <<( std::ostream& out, const Node& base ) + { + out << *base.GetTiXmlPointer(); + return out; + } + + protected: + /** + @internal + Allows NodeImp to use Node*'s. + */ + virtual TiXmlNode* GetTiXmlPointer() const = 0; + + TiXmlBase* GetBasePointer() const + { + return GetTiXmlPointer(); + } + + /** + @internal + Constructs the correct child of Node, based on the Type of the TiXmlNode*. + */ + Node* NodeFactory( TiXmlNode* tiXmlNode, bool throwIfNull = true, bool rememberSpawnedWrapper = true ) const; + + }; + + /** Iterator for conveniently stepping through Nodes and Attributes. + TinyXML++ introduces iterators: + @code + ticpp::Iterator< ticpp::Node > child; + for ( child = child.begin( parent ); child != child.end(); child++ ) + @endcode + + Iterators have the added advantage of filtering by type: + @code + // Only iterates through Comment nodes + ticpp::Iterator< ticpp::Comment > child; + for ( child = child.begin( parent ); child != child.end(); child++ ) + @endcode + + @code + // Only iterates through Element nodes with value "ElementValue" + ticpp::Iterator< ticpp::Element > child( "ElementValue" ); + for ( child = child.begin( parent ); child != child.end(); child++ ) + @endcode + + Finally, Iterators also work with Attributes + @code + ticpp::Iterator< ticpp::Attribute > attribute; + for ( attribute = attribute.begin( element ); attribute != attribute.end(); attribute++ ) + @endcode + */ + template < class T = Node > + class Iterator + { + private: + T* m_p; /**< Internal Pointer */ + std::string m_value; /**< Value for NextSibling calls */ + + public: + + /** + For for loop comparisons. + @param parent The parent of the nodes to iterate. + @return The first child of type T. + @code + ticpp::Iterator< ticpp::Node > child; + for ( child = child.begin( parent ); child != child.end(); child++ ) + @endcode + */ + T* begin( const Node* parent ) const + { + T* pointer; + parent->IterateFirst( m_value, &pointer ); + return pointer; + } + + /** + For for loop comparisons. + @return NULL + @code + ticpp::Iterator< ticpp::Node > child; + for ( child = child.begin( parent ); child != child.end(); child++ ) + @endcode + */ + T* end() const + { + return 0; + } + + /** Constructor. + @param value If not empty, this iterator will only visit nodes with matching value. + @code + // Only iterates through Element nodes with value "ElementValue" + ticpp::Iterator< ticpp::Element > child( "ElementValue" ); + for ( child = child.begin( parent ); child != child.end(); child++ ) + @endcode + */ + Iterator( const std::string& value = "" ) + : m_p( 0 ), m_value( value ) + { + } + + /// Constructor + Iterator( T* node, const std::string& value = "" ) + : m_p( node ), m_value( value ) + { + } + + /// Constructor + Iterator( const Iterator& it ) + : m_p( it.m_p ), m_value( it.m_value ) + { + } + + /** + Gets internal pointer. + @return The internal pointer. + */ + T* Get() const + { + return m_p; + } + + /** Sets internal pointer */ + Iterator& operator=( const Iterator& it ) + { + m_p = it.m_p; + m_value = it.m_value; + return *this; + } + + /** Sets internal pointer */ + Iterator& operator=( T* p ) + { + m_p = p; + return *this; + } + + /** Sets internal pointer to the Next Sibling, or Iterator::END, if there are no more siblings */ + Iterator& operator++() + { + m_p->IterateNext( m_value, &m_p ); + return *this; + } + + /** Sets internal pointer to the Next Sibling, or Iterator::END, if there are no more siblings */ + Iterator operator++(int) + { + Iterator tmp(*this); + ++(*this); + return tmp; + } + + /** Sets internal pointer to the Previous Sibling, or Iterator::END, if there are no prior siblings */ + Iterator& operator--() + { + m_p->IteratePrevious( m_value, &m_p ); + return *this; + } + + /** Sets internal pointer to the Previous Sibling, or Iterator::END, if there are no prior siblings */ + Iterator operator--(int) + { + Iterator tmp(*this); + --(*this); + return tmp; + } + + /** Compares internal pointer */ + bool operator!=( const T* p ) const + { + if ( m_p == p ) + { + return false; + } + if ( 0 == m_p || 0 == p ) + { + return true; + } + return *m_p != *p; + } + + /** Compares internal pointer */ + bool operator!=( const Iterator& it ) const + { + return operator!=( it.m_p ); + } + + /** Compares internal pointer* */ + bool operator==( T* p ) const + { + if ( m_p == p ) + { + return true; + } + if ( 0 == m_p || 0 == p ) + { + return false; + } + return *m_p == *p; + } + + /** Compares internal pointer */ + bool operator==( const Iterator& it ) const + { + return operator==( it.m_p ); + } + + /** So Iterator behaves like a STL iterator */ + T* operator->() const + { + return m_p; + } + + /** So Iterator behaves like a STL iterator */ + T& operator*() const + { + return *m_p; + } + }; + + /** Implementation of Node wrapper */ + template < class T > + class NodeImp : public Node + { + protected: + + T* m_tiXmlPointer; /**< Internal pointer to the TiXml Class which is being wrapped */ + + /** + @internal + Gets the internal TinyXML pointer. + + @returns The internal TiXmlNode*. + */ + TiXmlNode* GetTiXmlPointer() const + { + ValidatePointer(); + return m_tiXmlPointer; + } + + /** + @internal + Sets the internal pointer. + Saves a copy of the pointer to the RC object. + + @param newPointer TiXmlNode* to set. + */ + void SetTiXmlPointer( T* newPointer ) + { + m_tiXmlPointer = newPointer; + SetImpRC( newPointer ); + } + + /** + @internal + Constructor used by child classes. + */ + NodeImp( T* tiXmlPointer ) + { + // Check for NULL pointers + if ( 0 == tiXmlPointer ) + { + #ifdef TICPP_NO_RTTI + TICPPTHROW( "Can not create TinyXML objext" ); + #else + TICPPTHROW( "Can not create a " << typeid( T ).name() ); + #endif + } + SetTiXmlPointer( tiXmlPointer ); + m_impRC->IncRef(); + } + + /** + @internal + Updates the reference count for the old and new pointers. + In addition, the spawnedWrappers must be cleared out before a new TiXml object is loaded in. + */ + virtual void operator=( const NodeImp& copy ) + { + // Dropping the reference to the old object + this->m_impRC->DecRef(); + + // Pointing to the new Object + SetTiXmlPointer( copy.m_tiXmlPointer ); + + // The internal tixml pointer changed in the above line + this->m_impRC->IncRef(); + } + + /** + @internal + Updates the reference count for the old and new pointers. + In addition, the spawnedWrappers must be cleared out before a new TiXml object is loaded in + */ + NodeImp( const NodeImp& copy ) : Node( copy ) + { + // Pointing to the new Object + SetTiXmlPointer( copy.m_tiXmlPointer ); + + // The internal tixml pointer changed in the above line + this->m_impRC->IncRef(); + } + + public: + + /* + Deletes the spawned wrapper objects. + Decrements reference count. + */ + virtual ~NodeImp() + { + m_impRC->DecRef(); + } + }; + + /** Wrapper around TiXmlComment */ + class Comment : public NodeImp< TiXmlComment > + { + public: + + /** + Constructor. + */ + Comment(); + + /** + Constructor. + */ + Comment( TiXmlComment* comment ); + + /** + Constructor. + */ + Comment( const std::string& comment ); + }; + + /** Wrapper around TiXmlText */ + class Text : public NodeImp< TiXmlText > + { + public: + + /** + Constructor. + */ + Text(); + + /** + Constructor. + @overload + */ + Text( TiXmlText* text ); + + /** + Constructor. + @overload + */ + Text( const std::string& value ); + + /** + Streams value into a string and creates a Text with it. + Uses ToString to covert the parameter to a string. + + @param value The value of the Text node. + @throws Exception + + @see TiXmlText + */ + template < class T > + Text( const T& value ) + : NodeImp< TiXmlText >( new TiXmlText( ToString( value ) ) ) + { + m_impRC->InitRef(); + } + }; + + /** Wrapper around TiXmlDocument */ + class Document : public NodeImp< TiXmlDocument > + { + public: + /** + Default Constructor. + Create an empty document, that has no name. + */ + Document(); + + /** + Constructor. + */ + Document( TiXmlDocument* document ); + + /** + Constructor. + */ + Document( const char* documentName ); + + /** + Constructor. + Create a document with a name. The name of the document is also the filename of the xml. + + @param documentName Name to set in the Document. + */ + Document( const std::string& documentName ); + + /** + Load a file using the current document value. Throws if load is unsuccessful. + + @param encoding Sets the documents encoding. + @see TiXmlEncoding + @throws Exception + */ + void LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** + Save a file using the current document value. Throws if it can't save the file. + + @throws Exception + */ + void SaveFile() const; + + /** + Load a file using the given filename. Throws if load is unsuccessful. + + @param filename File to load. + @param encoding Sets the documents encoding. + @see TiXmlEncoding + @throws Exception + */ + void LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** + @copydoc Document::LoadFile( const std::string&, TiXmlEncoding ) + */ + void LoadFile( const char* filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** + Save a file using the given filename. Throws if it can't save the file. + + @param filename File to save. + @throws Exception + */ + void SaveFile( const std::string& filename ) const; + + /** + Parse the given xml data. + + @param xml Xml to parse. + @param throwIfParseError [DEF] If true, throws when there is a parse error. + @param encoding Sets the documents encoding. + @throws Exception + */ + void Parse( const std::string& xml, bool throwIfParseError = true, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + }; + + /** Wrapper around TiXmlElement */ + class Element : public NodeImp< TiXmlElement > + { + public: + /** + Default Constructor. + */ + Element(); + + /** + Default Constructor. Initializes all the variables. + @param value The value of the element. + */ + Element( const std::string& value ); + + /** + Default Constructor. Initializes all the variables. + @param value The value of the element. + */ + Element( const char* value ); + + /** + Constructor. + */ + Element( TiXmlElement* element ); + + /** + Constructor that allows you to set the element text + @param value The value of the element. + @param text The text to set. + */ + template < class T > + Element( const std::string& value, const T& text ) + : NodeImp< TiXmlElement >( new TiXmlElement( value ) ) + { + m_impRC->InitRef(); + SetText( text ); + } + + /** + Access the first attribute in this element. + + @param throwIfNoAttributes [DEF] If true, throws when there are no attributes + @return The first attribute, NULL if there are none and @a throwIfNoAttributes is true + */ + Attribute* FirstAttribute( bool throwIfNoAttributes = true ) const; + + /** + Access the last attribute in this element. + + @param throwIfNoAttributes [DEF] If true, throws when there are no attributes + @return The last attribute, NULL if there are none and @a throwIfNoAttributes is true + */ + Attribute* LastAttribute( bool throwIfNoAttributes = true ) const; + + /** + @internal + Just for Iterator<> + + @param value The value of nodes to iterate through + @param next [OUT] The pointer to the first valid node + */ + void IterateFirst( const std::string&, Attribute** first ) const + { + *first = 0; + for( Attribute* child = FirstAttribute( false ); child; child = child->Next( false ) ) + { + *first = dynamic_cast< Attribute* >( child ); + if ( 0 != *first ) + { + return; + } + } + } + + /** + Sets an attribute of name to a given value. + The attribute will be created if it does not exist, or changed if it does. + Uses ToString to convert the @a value to a string, so there is no need to use any other SetAttribute methods. + + @see GetAttribute + */ + template < class T > + void SetAttribute ( const std::string& name, const T& value ) + { + ValidatePointer(); + m_tiXmlPointer->SetAttribute( name, ToString( value ) ); + } + + /** + Gets the text of an Element. + + @param throwIfNotFound [DEF] If true, will throw an exception if there is no text in this element + @note This only works if the Text is the FirstChild node + @throws Exception When there is no text and throwIfNotFound is true + + @see GetText( T* value, bool throwIfNotFound = false ) + @see GetTextOrDefault + @see GetTextOrDefault( T* value, const DefaultT& defaultValue ) + @see TiXmlElement::GetText + */ + std::string GetText( bool throwIfNotFound = true ) const + { + // Get the element's text value as a std::string + std::string temp; + if ( !GetTextImp( &temp ) ) + { + if ( throwIfNotFound ) + { + TICPPTHROW( "Text does not exists in the current element" ); + } + } + + return temp; + } + + /** + Gets the text of an Element, if it doesn't exist it will return the defaultValue. + + @param defaultValue What to put in 'value' if there is no text in this element + @note This only works if the Text is the FirstChild node + + @see GetText + @see GetText( T* value, bool throwIfNotFound = false ) + @see GetTextOrDefault( T* value, const DefaultT& defaultValue ) + @see TiXmlElement::GetText + */ + std::string GetTextOrDefault( const std::string& defaultValue ) const + { + // Get the element's text value as a std::string + std::string temp; + if ( !GetTextImp( &temp ) ) + { + return defaultValue; + } + + return temp; + } + + /** + Gets the text value of an Element, if it doesn't exist it will return the defaultValue. + Uses FromString to convert the string to the type of choice + + @param value [OUT] The container for the returned value + @param defaultValue What to put in 'value' if there is no text in this element + @note This is different than GetText() in that it will covert the text to what ever type you want. + @note This only works if the Text is the FirstChild node + + @see GetText + @see GetText( T* value, bool throwIfNotFound = false ) + @see GetTextOrDefault( const std::string& defaultValue ) + @see TiXmlElement::GetText + */ + template < class T, class DefaultT > + void GetTextOrDefault( T* value, const DefaultT& defaultValue ) const + { + // Get the element's text value as a std::string + std::string temp; + if ( !GetTextImp( &temp ) ) + { + // The text value does not exist - set value to the default + *value = defaultValue; + return; + } + + // Stream the value from the string to T + FromString( temp, value ); + } + + /** + Gets the text of an Element. + Uses FromString to convert the string to the type of choice. + + @param value [OUT] The container for the returned value + @param throwIfNotFound [DEF] If true, will throw an exception if there is no text in this element + @note This is different than GetText() in that it will covert the text to what ever type you want + @note This only works if the Text is the FirstChild node + @throws Exception When there is no text and throwIfNotFound is true + + @see GetText + @see GetTextOrDefault + @see GetTextOrDefault( T* value, const DefaultT& defaultValue ) + @see TiXmlElement::GetText + */ + template< class T > + void GetText( T* value, bool throwIfNotFound = true ) const + { + // Get the element's text value as a std::string + std::string temp; + if ( !GetTextImp( &temp ) ) + { + if ( throwIfNotFound ) + { + TICPPTHROW( "Text does not exists in the current element" ); + } + else + { + return; + } + } + + // Stream the value from the string to T + FromString( temp, value ); + } + + /** + Convenience function to set the text of an element. + Creates a Text node and inserts it as the first child. + Uses ToString to convert the parameter to a string. + + @param value The text to set. + */ + template < class T > + void SetText( const T& value ) + { + ValidatePointer(); + std::string temp = ToString( value ); + + if ( m_tiXmlPointer->NoChildren() ) + { + m_tiXmlPointer->LinkEndChild( new TiXmlText( temp ) ); + } + else + { + if ( 0 == m_tiXmlPointer->GetText() ) + { + m_tiXmlPointer->InsertBeforeChild( m_tiXmlPointer->FirstChild(), TiXmlText( temp ) ); + } + else + { + // There already is text, so change it + m_tiXmlPointer->FirstChild()->SetValue( temp ); + } + } + } + + /** + Gets an attribute of @a name from an element, if it doesn't exist it will return the defaultValue. + Uses FromString to convert the string to the type of choice. + + @param name The name of the attribute you are querying. + @param value [OUT] The container for the returned value. + @param defaultValue What to put in @a value if there is no attribute in this element. + @throws Exception + + @see GetAttribute + */ + template < class T, class DefaulT > + void GetAttributeOrDefault( const std::string& name, T* value, const DefaulT& defaultValue ) const + { + // Get the attribute's value as a std::string + std::string temp; + if ( !GetAttributeImp( name, &temp ) ) + { + // The attribute does not exist - set value to the default + *value = defaultValue; + return; + } + + // Stream the value from the string to T + FromString( temp, value ); + } + + /** + Gets an attribute of @a name from an element, if it doesn't exist it will return the defaultValue. + + @param name The name of the attribute you are querying. + @param defaultValue What to put in @a value if there is no attribute in this element. + + @see GetAttribute + */ + std::string GetAttributeOrDefault( const std::string& name, const std::string& defaultValue ) const; + + /** + Returns an attribute of @a name from an element. + Uses FromString to convert the string to the type of choice. + + @param name The name of the attribute you are querying. + @param throwIfNotFound [DEF] If true, will throw an exception if the attribute doesn't exist + @throws Exception When the attribute doesn't exist and throwIfNotFound is true + @see GetAttributeOrDefault + */ + template < class T > + T GetAttribute( const std::string& name, bool throwIfNotFound = true ) const + { + // Get the attribute's value as a std::string + std::string temp; + T value; + if ( !GetAttributeImp( name, &temp ) ) + { + if ( throwIfNotFound ) + { + TICPPTHROW( "Attribute does not exist" ); + } + } + else + { + // Stream the value from the string to T + FromString( temp, &value ); + } + + return value; + } + + /** + Gets an attribute of @a name from an element. + Uses FromString to convert the string to the type of choice. + + @param name The name of the attribute you are querying. + @param value [OUT] The container for the returned value + @param throwIfNotFound [DEF] If true, will throw an exception if the attribute doesn't exist + @throws Exception When the attribute doesn't exist and throwIfNotFound is true + + @see GetAttributeOrDefault + */ + template< class T > + void GetAttribute( const std::string& name, T* value, bool throwIfNotFound = true ) const + { + // Get the attribute's value as a std::string + std::string temp; + if ( !GetAttributeImp( name, &temp ) ) + { + if ( throwIfNotFound ) + { + TICPPTHROW( "Attribute does not exist" ); + } + else + { + return; + } + } + + // Stream the value from the string to T + FromString( temp, value ); + } + + /** + Gets an attribute of @a name from an element. + Returns an empty string if the attribute does not exist. + + @param name The name of the attribute you are querying. + @return The value of the attribute, or an empty string if it does not exist. + + @see GetAttributeOrDefault + */ + std::string GetAttribute( const std::string& name ) const; + + /** + Returns true, if attribute exists + + @param name The name of the attribute you are checking. + @return Existence of attribute + */ + bool HasAttribute( const std::string& name ) const; + + /** + Removes attribute from element. + + @param name The name of the attribute to remove. + */ + void RemoveAttribute( const std::string& name ); + + private: + + /** + @internal + Implimentation of the GetAttribute and GetAttributeOrDefault template methods. + */ + bool GetAttributeImp( const std::string& name, std::string* value ) const; + + /** + @internal + Implimentation of the GetText, GetTextOrDefault, GetTextValue, and GetTextValueOrDefault template methods. + */ + bool GetTextImp( std::string* value ) const; + }; + + /** Wrapper around TiXmlDeclaration */ + class Declaration : public NodeImp< TiXmlDeclaration > + { + public: + /** + Default Constructor. Construct an empty declaration. + */ + Declaration(); + + /** + Constructor. + */ + Declaration( TiXmlDeclaration* declaration ); + + /** + Constructor. + */ + Declaration( const std::string& version, const std::string& encoding, const std::string& standalone ); + + /** + Version. Will return an empty string if none was found. + */ + std::string Version() const; + + /** + Encoding. Will return an empty string if none was found. + */ + std::string Encoding() const; + + /** + StandAlone. Is this a standalone document? + */ + std::string Standalone() const; + }; + + /** Wrapper around TiXmlStylesheetReference */ + class StylesheetReference : public NodeImp< TiXmlStylesheetReference > + { + public: + /** + Default Constructor. Construct an empty declaration. + */ + StylesheetReference(); + + /** + Constructor. + */ + StylesheetReference( TiXmlStylesheetReference* stylesheetReference ); + + /** + Constructor. + */ + StylesheetReference( const std::string& type, const std::string& href ); + + /** + Type. Will return an empty string if none was found. + */ + std::string Type() const; + + /** + Href. Will return an empty string if none was found. + */ + std::string Href() const; + }; +} + +#endif // TICPP_INCLUDED + +#endif // TIXML_USE_TICPP diff --git a/src/core/utility/configuration/tinyxml/ticpprc.h b/src/core/utility/configuration/tinyxml/ticpprc.h new file mode 100644 index 00000000..9758fe35 --- /dev/null +++ b/src/core/utility/configuration/tinyxml/ticpprc.h @@ -0,0 +1,122 @@ +/* +http://code.google.com/p/ticpp/ +Copyright (c) 2006 Ryan Pusztai, Ryan Mulder + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifdef TIXML_USE_TICPP + +#ifndef TICPPRC_INCLUDED +#define TICPPRC_INCLUDED + +#include + +// Forward declare ticpp::Node, so it can be made a friend of TiCppRC +namespace ticpp +{ + class Base; +} + +// Forward declare TiCppRCImp so TiCppRC can hold a pointer to it +class TiCppRCImp; + +/** +Base class for reference counting functionality +*/ +class TiCppRC +{ + // Allow ticpp::Node to directly modify reference count + friend class ticpp::Base; + +private: + + TiCppRCImp* m_tiRC; /**< Pointer to reference counter */ + +public: + + /** + Constructor + Spawns new reference counter with a pointer to this + */ + TiCppRC(); + + /** + Destructor + Nullifies the pointer to this held by the reference counter + Decrements reference count + */ + virtual ~TiCppRC(); + + std::vector< ticpp::Base* > m_spawnedWrappers; /**< Remember all wrappers that we've created with 'new' - ( e.g. NodeFactory, FirstChildElement, etc. )*/ + + /** + Delete all container objects we've spawned with 'new'. + */ + void DeleteSpawnedWrappers(); +}; + +class TiCppRCImp +{ +private: + + int m_count; /**< Holds reference count to me, and to the node I point to */ + + TiCppRC* m_tiCppRC; /**< Holds pointer to an object inheriting TiCppRC */ + +public: + + /** + Initializes m_tiCppRC pointer, and set reference count to 1 + */ + TiCppRCImp( TiCppRC* tiCppRC ); + + /** + Allows the TiCppRC object to set the pointer to itself ( m_tiCppRc ) to NULL when the TiCppRC object is deleted + */ + void Nullify(); + + /** + Increment Reference Count + */ + void IncRef(); + + /** + Decrement Reference Count + */ + void DecRef(); + + /** + Set Reference Count to 1 - dangerous! - Use only if you are sure of the consequences + */ + void InitRef(); + + /** + Get internal pointer to the TiCppRC object - not reference counted, use at your own risk + */ + TiCppRC* Get(); + + /** + Returns state of internal pointer - will be null if the object was deleted + */ + bool IsNull(); +}; + +#endif // TICPP_INCLUDED + +#endif // TIXML_USE_TICPP diff --git a/src/core/utility/configuration/tinyxml/tinystr.cpp b/src/core/utility/configuration/tinyxml/tinystr.cpp new file mode 100644 index 00000000..68125071 --- /dev/null +++ b/src/core/utility/configuration/tinyxml/tinystr.cpp @@ -0,0 +1,116 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Løvset, 7. April 2005. + */ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/src/core/utility/configuration/tinyxml/tinystr.h b/src/core/utility/configuration/tinyxml/tinystr.h new file mode 100644 index 00000000..3c2aa9d5 --- /dev/null +++ b/src/core/utility/configuration/tinyxml/tinystr.h @@ -0,0 +1,319 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005. + * + * - completely rewritten. compact, clean, and fast implementation. + * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems) + * - fixed reserve() to work as per specification. + * - fixed buggy compares operator==(), operator<(), and operator>() + * - fixed operator+=() to take a const ref argument, following spec. + * - added "copy" constructor with length, and most compare operators. + * - added swap(), clear(), size(), capacity(), operator+(). + */ + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include +#include + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + // = operator + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + // = operator + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/src/core/utility/configuration/tinyxml/tinyxml.cpp b/src/core/utility/configuration/tinyxml/tinyxml.cpp new file mode 100644 index 00000000..c6663446 --- /dev/null +++ b/src/core/utility/configuration/tinyxml/tinyxml.cpp @@ -0,0 +1,1969 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#include "tinyxml.h" + +#include + +#ifdef TIXML_USE_STL +#include +#include +#endif + + +bool TiXmlBase::condenseWhiteSpace = true; + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; + #else + return fopen( filename, mode ); + #endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::DOCUMENT ) + { + delete node; + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( replaceThis->parent != this ) + return 0; + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +void TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return &node->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const char* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s ); + } + else { + *i = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const std::string* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s->c_str() ); + } + else { + *i = 0; + } + } + return s; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const char* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s ); + } + else { + *d = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const std::string* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s->c_str() ); + } + else { + *d = 0; + } + } + return s; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + char buf[64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%d", val ); + #else + sprintf( buf, "%d", val ); + #endif + SetAttribute( name, buf ); +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + std::ostringstream oss; + oss << val; + SetAttribute( name, oss.str() ); +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + char buf[256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); + #else + sprintf( buf, "%f", val ); + #endif + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING _name( cname ); + TIXML_STRING _value( cvalue ); + #else + const char* _name = cname; + const char* _value = cvalue; + #endif + + TiXmlAttribute* node = attributeSet.Find( _name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( cname, cvalue ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, const std::string& _value ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; iNext() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a node + // 2) An element with only a text child is printed as text + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + // See STL_STRING_BUG below. + //StringToBuffer buf( value ); + + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + // See STL_STRING_BUG below. +// StringToBuffer buf( value ); +// +// if ( buf.buffer && SaveFile( buf.buffer ) ) +// return true; +// +// return false; + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + // There was a really terrifying little bug here. The code: + // value = filename + // in the STL case, cause the assignment method of the std::string to + // be called. What is strange, is that the std::string had the same + // address as it's c_str() method, and so bad things happen. Looks + // like a bug in the Microsoft STL implementation. + // Add an extra string to avoid the crash. + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // If we have a file, assume it is all one big XML file, and read it in. + // The document parser may decide the document ends sooner than the entire file, however. + TIXML_STRING data; + data.reserve( length ); + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // + // + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + const char* lastPos = buf; + const char* p = buf; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + if ( *p == 0xa ) { + // Newline character. No special rules for this. Append all the characters + // since the last string, and include the newline. + data.append( lastPos, (p-lastPos+1) ); // append, include the newline + ++p; // move past the newline + lastPos = p; // and point to the new buffer (may be 0) + assert( p <= (buf+length) ); + } + else if ( *p == 0xd ) { + // Carriage return. Append what we have so far, then + // handle moving forward in the buffer. + if ( (p-lastPos) > 0 ) { + data.append( lastPos, p-lastPos ); // do not add the CR + } + data += (char)0xa; // a proper newline + + if ( *(p+1) == 0xa ) { + // Carriage return - new line sequence + p += 2; + lastPos = p; + assert( p <= (buf+length) ); + } + else { + // it was followed by something else...that is presumably characters again. + ++p; + lastPos = p; + assert( p <= (buf+length) ); + } + } + else { + ++p; + } + } + // Handle any left over characters. + if ( p-lastPos ) { + data.append( lastPos, p-lastPos ); + } + delete [] buf; + buf = 0; + + Parse( data.c_str(), 0, encoding ); + + if ( Error() ) + return false; + else + return true; +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); + #else + sprintf (buf, "%lf", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + +TiXmlStylesheetReference::TiXmlStylesheetReference( const char * _type, + const char * _href ) + : TiXmlNode( TiXmlNode::STYLESHEETREFERENCE ) +{ + type = _type; + href = _href; +} + + +#ifdef TIXML_USE_STL +TiXmlStylesheetReference::TiXmlStylesheetReference( const std::string& _type, + const std::string& _href ) + : TiXmlNode( TiXmlNode::STYLESHEETREFERENCE ) +{ + type = _type; + href = _href; +} +#endif + + +TiXmlStylesheetReference::TiXmlStylesheetReference( const TiXmlStylesheetReference& copy ) + : TiXmlNode( TiXmlNode::STYLESHEETREFERENCE ) +{ + copy.CopyTo( this ); +} + + +void TiXmlStylesheetReference::operator=( const TiXmlStylesheetReference& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +void TiXmlStylesheetReference::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, ""); + if ( str ) (*str) += "?>"; +} + +void TiXmlStylesheetReference::CopyTo( TiXmlStylesheetReference* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->type = type; + target->href = href; +} + +bool TiXmlStylesheetReference::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + +TiXmlNode* TiXmlStylesheetReference::Clone() const +{ + TiXmlStylesheetReference* clone = new TiXmlStylesheetReference(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} +*/ +#endif + + +const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} +*/ + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && iNextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && iNextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && iNextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && iNextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += ""; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += ""; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += ""; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + +bool TiXmlPrinter::Visit( const TiXmlStylesheetReference& stylesheet ) +{ + DoIndent(); + stylesheet.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} diff --git a/src/core/utility/configuration/tinyxml/tinyxml.h b/src/core/utility/configuration/tinyxml/tinyxml.h new file mode 100644 index 00000000..4720dd7c --- /dev/null +++ b/src/core/utility/configuration/tinyxml/tinyxml.h @@ -0,0 +1,1884 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_TICPP + #ifndef TIXML_USE_STL + #define TIXML_USE_STL + #endif +#endif + +#ifdef TIXML_USE_STL + #include + #include + #include + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SNSCANF _snscanf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SNSCANF _snscanf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SNSCANF snscanf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlStylesheetReference; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 5; +const int TIXML_PATCH_VERSION = 3; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simple called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its sibilings will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a stylesheet reference + virtual bool Visit( const TiXmlStylesheetReference& /*stylesheet*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +#ifdef TIXML_USE_TICPP +#include "ticpprc.h" +class TiXmlBase : public TiCppRC +#else +class TiXmlBase +#endif +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_OUT_OF_MEMORY, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + DOCUMENT, + ELEMENT, + COMMENT, + UNKNOWN, + TEXT, + DECLARATION, + STYLESHEETREFERENCE, + TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: DOCUMENT, ELEMENT, COMMENT, + UNKNOWN, TEXT, and DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlStylesheetReference* ToStylesheetReference() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlStylesheetReference* ToStylesheetReference() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + const TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* Find( const char* _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + #ifdef TIXML_USE_STL + const TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* Find( const std::string& _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + + #endif + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + void operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + /* + This is - in theory - a bug fix for "QueryValueAtribute returns truncated std::string" + but template specialization is hard to get working cross-compiler. Leaving the bug for now. + + // The above will fail for std::string because the space character is used as a seperator. + // Specialize for strings. Bug [ 1695429 ] QueryValueAtribute returns truncated std::string + template<> int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + */ + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + void operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); } + void operator=( const TiXmlText& base ) { base.CopyTo( this ); } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + void operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + +/** A stylesheet reference looks like this: + @verbatim + + @endverbatim + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 2 and they are always the same. +*/ +class TiXmlStylesheetReference : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlStylesheetReference() : TiXmlNode( TiXmlNode::STYLESHEETREFERENCE ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlStylesheetReference( const std::string& _type, + const std::string& _href ); +#endif + + /// Construct. + TiXmlStylesheetReference( const char* _type, + const char* _href ); + + TiXmlStylesheetReference( const TiXmlStylesheetReference& copy ); + void operator=( const TiXmlStylesheetReference& copy ); + + virtual ~TiXmlStylesheetReference() {} + + /// Type. Will return an empty string if none was found. + const char *Type() const { return type.c_str (); } + /// Href. Will return an empty string if none was found. + const char *Href() const { return href.c_str (); } + + /// Creates a copy of this StylesheetReference and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlStylesheetReference* ToStylesheetReference() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlStylesheetReference* ToStylesheetReference() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlStylesheetReference* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING type; + TIXML_STRING href; +}; + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); } + void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + void operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && LoadFile( f.buffer, encoding )); + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && SaveFile( f.buffer )); + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + virtual bool Visit( const TiXmlStylesheetReference& stylesheet ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i +#include + +#include "tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( ( *p && IsWhiteSpace( *p ) ) || *p == '\n' || *p =='\r' ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; iappend( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p ) + p += strlen( endTag ); + return p; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + TiXmlDocument* doc = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: "; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + +// int tabsize = 4; +// if ( document ) +// tabsize = document->TabSize(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = ""; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; igood() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlStylesheetReference::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + type = ""; + href = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "type", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + type = attrib.Value(); + } + else if ( StringEqual( p, "href", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + href = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} diff --git a/src/core/utility/configuration/tinyxml/tutorial_gettingStarted.txt b/src/core/utility/configuration/tinyxml/tutorial_gettingStarted.txt new file mode 100644 index 00000000..34b3e184 --- /dev/null +++ b/src/core/utility/configuration/tinyxml/tutorial_gettingStarted.txt @@ -0,0 +1,835 @@ +/** @page tutorial0 TinyXML Tutorial + +

What is this?

+ +This tutorial has a few tips and suggestions on how to use TinyXML +effectively. + +I've also tried to include some C++ tips like how to convert strings to +integers and vice versa. This isn't anything to do with TinyXML itself, but +it may helpful for your project so I've put it in anyway. + +If you don't know basic C++ concepts this tutorial won't be useful. +Likewise if you don't know what a DOM is, look elsewhere first. + +

Before we start

+ +Some example XML datasets/files will be used. + +example1.xml: + +@verbatim + +World +@endverbatim + +example2.xml: + +@verbatim + + + + Alas + Great World + Alas (again) + + +@endverbatim + +example3.xml: + +@verbatim + + + + + +@endverbatim + +example4.xml + +@verbatim + + + + + Welcome to MyApp + Thank you for using MyApp + + + + + + +@endverbatim + +

Getting Started

+ +

Load XML from a file

+ +The simplest way to load a file into a TinyXML DOM is: + +@verbatim +TiXmlDocument doc( "demo.xml" ); +doc.LoadFile(); +@endverbatim + +A more real-world usage is shown below. This will load the file and display +the contents to STDOUT: + +@verbatim +// load the named file and dump its structure to STDOUT +void dump_to_stdout(const char* pFilename) +{ + TiXmlDocument doc(pFilename); + bool loadOkay = doc.LoadFile(); + if (loadOkay) + { + printf("\n%s:\n", pFilename); + dump_to_stdout( &doc ); // defined later in the tutorial + } + else + { + printf("Failed to load file \"%s\"\n", pFilename); + } +} +@endverbatim + +A simple demonstration of this function is to use a main like this: + +@verbatim +int main(void) +{ + dump_to_stdout("example1.xml"); + return 0; +} +@endverbatim + +Recall that Example 1 XML is: + +@verbatim + +World +@endverbatim + +Running the program with this XML will display this in the console/DOS window: + +@verbatim +DOCUMENT ++ DECLARATION ++ ELEMENT Hello + + TEXT[World] +@endverbatim + + +The ``dump_to_stdout`` function is defined later in this tutorial and is +useful if you want to understand recursive traversal of a DOM. + +

Building Documents Programatically

+ + +This is how to build Example 1 pragmatically: + +@verbatim +void build_simple_doc( ) +{ + // Make xml: World + TiXmlDocument doc; + TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" ); + TiXmlElement * element = new TiXmlElement( "Hello" ); + TiXmlText * text = new TiXmlText( "World" ); + element->LinkEndChild( text ); + doc.LinkEndChild( decl ); + doc.LinkEndChild( element ); + doc.SaveFile( "madeByHand.xml" ); +} +@endverbatim + +This can be loaded and displayed on the console with: + +@verbatim +dump_to_stdout("madeByHand.xml"); // this func defined later in the tutorial +@endverbatim + +and you'll see it is identical to Example 1: + +@verbatim +madeByHand.xml: +Document ++ Declaration ++ Element [Hello] + + Text: [World] +@endverbatim + +This code produces exactly the same XML DOM but it shows a different ordering +to node creation and linking: + +@verbatim +void write_simple_doc2( ) +{ + // same as write_simple_doc1 but add each node + // as early as possible into the tree. + + TiXmlDocument doc; + TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" ); + doc.LinkEndChild( decl ); + + TiXmlElement * element = new TiXmlElement( "Hello" ); + doc.LinkEndChild( element ); + + TiXmlText * text = new TiXmlText( "World" ); + element->LinkEndChild( text ); + + doc.SaveFile( "madeByHand2.xml" ); +} +@endverbatim + +Both of these produce the same XML, namely: + +@verbatim + +World +@endverbatim + +Or in structure form: + +@verbatim +DOCUMENT ++ DECLARATION ++ ELEMENT Hello + + TEXT[World] +@endverbatim + + +

Attributes

+ +Given an existing node, settings attributes is easy: + +@verbatim +window = new TiXmlElement( "Demo" ); +window->SetAttribute("name", "Circle"); +window->SetAttribute("x", 5); +window->SetAttribute("y", 15); +window->SetDoubleAttribute("radius", 3.14159); +@endverbatim + +You can it also work with the TiXmlAttribute objects if you want. + +The following code shows one way (not the only way) to get all attributes of an +element, print the name and string value, and if the value can be converted +to an integer or double, print that value too: + +@verbatim +// print all attributes of pElement. +// returns the number of attributes printed +int dump_attribs_to_stdout(TiXmlElement* pElement, unsigned int indent) +{ + if ( !pElement ) return 0; + + TiXmlAttribute* pAttrib=pElement->FirstAttribute(); + int i=0; + int ival; + double dval; + const char* pIndent=getIndent(indent); + printf("\n"); + while (pAttrib) + { + printf( "%s%s: value=[%s]", pIndent, pAttrib->Name(), pAttrib->Value()); + + if (pAttrib->QueryIntValue(&ival)==TIXML_SUCCESS) printf( " int=%d", ival); + if (pAttrib->QueryDoubleValue(&dval)==TIXML_SUCCESS) printf( " d=%1.1f", dval); + printf( "\n" ); + i++; + pAttrib=pAttrib->Next(); + } + return i; +} +@endverbatim + +

Writing a document to a file

+ +Writing a pre-built DOM to a file is trivial: + +@verbatim +doc.SaveFile( saveFilename ); +@endverbatim + +Recall, for example, example 4: + +@verbatim + + + + + Welcome to MyApp + Thank you for using MyApp + + + + + + +@endverbatim + +The following function builds this DOM and writes the file "appsettings.xml": + +@verbatim +void write_app_settings_doc( ) +{ + TiXmlDocument doc; + TiXmlElement* msg; + TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" ); + doc.LinkEndChild( decl ); + + TiXmlElement * root = new TiXmlElement( "MyApp" ); + doc.LinkEndChild( root ); + + TiXmlComment * comment = new TiXmlComment(); + comment->SetValue(" Settings for MyApp " ); + root->LinkEndChild( comment ); + + TiXmlElement * msgs = new TiXmlElement( "Messages" ); + root->LinkEndChild( msgs ); + + msg = new TiXmlElement( "Welcome" ); + msg->LinkEndChild( new TiXmlText( "Welcome to MyApp" )); + msgs->LinkEndChild( msg ); + + msg = new TiXmlElement( "Farewell" ); + msg->LinkEndChild( new TiXmlText( "Thank you for using MyApp" )); + msgs->LinkEndChild( msg ); + + TiXmlElement * windows = new TiXmlElement( "Windows" ); + root->LinkEndChild( windows ); + + TiXmlElement * window; + window = new TiXmlElement( "Window" ); + windows->LinkEndChild( window ); + window->SetAttribute("name", "MainFrame"); + window->SetAttribute("x", 5); + window->SetAttribute("y", 15); + window->SetAttribute("w", 400); + window->SetAttribute("h", 250); + + TiXmlElement * cxn = new TiXmlElement( "Connection" ); + root->LinkEndChild( cxn ); + cxn->SetAttribute("ip", "192.168.0.1"); + cxn->SetDoubleAttribute("timeout", 123.456); // floating point attrib + + dump_to_stdout( &doc ); + doc.SaveFile( "appsettings.xml" ); +} +@endverbatim + +The dump_to_stdout function will show this structure: + +@verbatim +Document ++ Declaration ++ Element [MyApp] + (No attributes) + + Comment: [ Settings for MyApp ] + + Element [Messages] + (No attributes) + + Element [Welcome] + (No attributes) + + Text: [Welcome to MyApp] + + Element [Farewell] + (No attributes) + + Text: [Thank you for using MyApp] + + Element [Windows] + (No attributes) + + Element [Window] + + name: value=[MainFrame] + + x: value=[5] int=5 d=5.0 + + y: value=[15] int=15 d=15.0 + + w: value=[400] int=400 d=400.0 + + h: value=[250] int=250 d=250.0 + 5 attributes + + Element [Connection] + + ip: value=[192.168.0.1] int=192 d=192.2 + + timeout: value=[123.456000] int=123 d=123.5 + 2 attributes +@endverbatim + + +I was surprised that TinyXml, by default, writes the XML in what other +APIs call a "pretty" format - it modifies the whitespace of text of elements +that contain other nodes so that writing the tree includes an indication of +nesting level. + +I haven't looked yet to see if there is a way to turn off indenting when +writing a file - its bound to be easy. + +[Lee: It's easy in STL mode, just use cout << myDoc. Non-STL mode is +always in "pretty" format. Adding a switch would be a nice feature and +has been requested.] + + +

XML to/from C++ objects

+ +

Intro

+ +This example assumes you're loading and saving your app settings in an +XML file, e.g. something like example4.xml. + +There are a number of ways to do this. For example, look into the TinyBind +project at http://sourceforge.net/projects/tinybind + +This section shows a plain-old approach to loading and saving a basic object +structure using XML. + +

Set up your object classes

+ +Start off with some basic classes like these: + +@verbatim +#include +#include +using namespace std; + +typedef std::map MessageMap; + +// a basic window abstraction - demo purposes only +class WindowSettings +{ +public: + int x,y,w,h; + string name; + + WindowSettings() + : x(0), y(0), w(100), h(100), name("Untitled") + { + } + + WindowSettings(int x, int y, int w, int h, const string& name) + { + this->x=x; + this->y=y; + this->w=w; + this->h=h; + this->name=name; + } +}; + +class ConnectionSettings +{ +public: + string ip; + double timeout; +}; + +class AppSettings +{ +public: + string m_name; + MessageMap m_messages; + list m_windows; + ConnectionSettings m_connection; + + AppSettings() {} + + void save(const char* pFilename); + void load(const char* pFilename); + + // just to show how to do it + void setDemoValues() + { + m_name="MyApp"; + m_messages.clear(); + m_messages["Welcome"]="Welcome to "+m_name; + m_messages["Farewell"]="Thank you for using "+m_name; + m_windows.clear(); + m_windows.push_back(WindowSettings(15,15,400,250,"Main")); + m_connection.ip="Unknown"; + m_connection.timeout=123.456; + } +}; +@endverbatim + +This is a basic main() that shows how to create a default settings object tree, +save it and load it again: + +@verbatim +int main(void) +{ + AppSettings settings; + + settings.save("appsettings2.xml"); + settings.load("appsettings2.xml"); + return 0; +} +@endverbatim + +The following main() shows creation, modification, saving and then loading of a +settings structure: + +@verbatim +int main(void) +{ + // block: customise and save settings + { + AppSettings settings; + settings.m_name="HitchHikerApp"; + settings.m_messages["Welcome"]="Don't Panic"; + settings.m_messages["Farewell"]="Thanks for all the fish"; + settings.m_windows.push_back(WindowSettings(15,25,300,250,"BookFrame")); + settings.m_connection.ip="192.168.0.77"; + settings.m_connection.timeout=42.0; + + settings.save("appsettings2.xml"); + } + + // block: load settings + { + AppSettings settings; + settings.load("appsettings2.xml"); + printf("%s: %s\n", settings.m_name.c_str(), + settings.m_messages["Welcome"].c_str()); + WindowSettings & w=settings.m_windows.front(); + printf("%s: Show window '%s' at %d,%d (%d x %d)\n", + settings.m_name.c_str(), w.name.c_str(), w.x, w.y, w.w, w.h); + printf("%s: %s\n", settings.m_name.c_str(), settings.m_messages["Farewell"].c_str()); + } + return 0; +} +@endverbatim + +When the save() and load() are completed (see below), running this main() +displays on the console: + +@verbatim +HitchHikerApp: Don't Panic +HitchHikerApp: Show window 'BookFrame' at 15,25 (300 x 100) +HitchHikerApp: Thanks for all the fish +@endverbatim + +

Encode C++ state as XML

+ +There are lots of different ways to approach saving this to a file. +Here's one: + +@verbatim +void AppSettings::save(const char* pFilename) +{ + TiXmlDocument doc; + TiXmlElement* msg; + TiXmlComment * comment; + string s; + TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" ); + doc.LinkEndChild( decl ); + + TiXmlElement * root = new TiXmlElement(m_name.c_str()); + doc.LinkEndChild( root ); + + comment = new TiXmlComment(); + s=" Settings for "+m_name+" "; + comment->SetValue(s.c_str()); + root->LinkEndChild( comment ); + + // block: messages + { + MessageMap::iterator iter; + + TiXmlElement * msgs = new TiXmlElement( "Messages" ); + root->LinkEndChild( msgs ); + + for (iter=m_messages.begin(); iter != m_messages.end(); iter++) + { + const string & key=(*iter).first; + const string & value=(*iter).second; + msg = new TiXmlElement(key.c_str()); + msg->LinkEndChild( new TiXmlText(value.c_str())); + msgs->LinkEndChild( msg ); + } + } + + // block: windows + { + TiXmlElement * windowsNode = new TiXmlElement( "Windows" ); + root->LinkEndChild( windowsNode ); + + list::iterator iter; + + for (iter=m_windows.begin(); iter != m_windows.end(); iter++) + { + const WindowSettings& w=*iter; + + TiXmlElement * window; + window = new TiXmlElement( "Window" ); + windowsNode->LinkEndChild( window ); + window->SetAttribute("name", w.name.c_str()); + window->SetAttribute("x", w.x); + window->SetAttribute("y", w.y); + window->SetAttribute("w", w.w); + window->SetAttribute("h", w.h); + } + } + + // block: connection + { + TiXmlElement * cxn = new TiXmlElement( "Connection" ); + root->LinkEndChild( cxn ); + cxn->SetAttribute("ip", m_connection.ip.c_str()); + cxn->SetDoubleAttribute("timeout", m_connection.timeout); + } + + doc.SaveFile(pFilename); +} +@endverbatim + +Running this with the modified main produces this file: + +@verbatim + + + + + Thanks for all the fish + Don't Panic + + + + + + +@endverbatim + + +

Decoding state from XML

+ +As with encoding objects, there are a number of approaches to decoding XML +into your own C++ object structure. The following approach uses TiXmlHandles. + +@verbatim +void AppSettings::load(const char* pFilename) +{ + TiXmlDocument doc(pFilename); + if (!doc.LoadFile()) return; + + TiXmlHandle hDoc(&doc); + TiXmlElement* pElem; + TiXmlHandle hRoot(0); + + // block: name + { + pElem=hDoc.FirstChildElement().Element(); + // should always have a valid root but handle gracefully if it does + if (!pElem) return; + m_name=pElem->Value(); + + // save this for later + hRoot=TiXmlHandle(pElem); + } + + // block: string table + { + m_messages.clear(); // trash existing table + + pElem=hRoot.FirstChild( "Messages" ).FirstChild().Element(); + for( pElem; pElem; pElem=pElem->NextSiblingElement()) + { + const char *pKey=pElem->Value(); + const char *pText=pElem->GetText(); + if (pKey && pText) + { + m_messages[pKey]=pText; + } + } + } + + // block: windows + { + m_windows.clear(); // trash existing list + + TiXmlElement* pWindowNode=hRoot.FirstChild( "Windows" ).FirstChild().Element(); + for( pWindowNode; pWindowNode; pWindowNode=pWindowNode->NextSiblingElement()) + { + WindowSettings w; + const char *pName=pWindowNode->Attribute("name"); + if (pName) w.name=pName; + + pWindowNode->QueryIntAttribute("x", &w.x); // If this fails, original value is left as-is + pWindowNode->QueryIntAttribute("y", &w.y); + pWindowNode->QueryIntAttribute("w", &w.w); + pWindowNode->QueryIntAttribute("hh", &w.h); + + m_windows.push_back(w); + } + } + + // block: connection + { + pElem=hRoot.FirstChild("Connection").Element(); + if (pElem) + { + m_connection.ip=pElem->Attribute("ip"); + pElem->QueryDoubleAttribute("timeout",&m_connection.timeout); + } + } +} +@endverbatim + +

Full listing for dump_to_stdout

+ +Below is a copy-and-paste demo program for loading arbitrary XML files and +dumping the structure to STDOUT using the recursive traversal listed above. + +@verbatim +// tutorial demo program +#include "stdafx.h" +#include "tinyxml.h" + +// ---------------------------------------------------------------------- +// STDOUT dump and indenting utility functions +// ---------------------------------------------------------------------- +const unsigned int NUM_INDENTS_PER_SPACE=2; + +const char * getIndent( unsigned int numIndents ) +{ + static const char * pINDENT=" + "; + static const unsigned int LENGTH=strlen( pINDENT ); + unsigned int n=numIndents*NUM_INDENTS_PER_SPACE; + if ( n > LENGTH ) n = LENGTH; + + return &pINDENT[ LENGTH-n ]; +} + +// same as getIndent but no "+" at the end +const char * getIndentAlt( unsigned int numIndents ) +{ + static const char * pINDENT=" "; + static const unsigned int LENGTH=strlen( pINDENT ); + unsigned int n=numIndents*NUM_INDENTS_PER_SPACE; + if ( n > LENGTH ) n = LENGTH; + + return &pINDENT[ LENGTH-n ]; +} + +int dump_attribs_to_stdout(TiXmlElement* pElement, unsigned int indent) +{ + if ( !pElement ) return 0; + + TiXmlAttribute* pAttrib=pElement->FirstAttribute(); + int i=0; + int ival; + double dval; + const char* pIndent=getIndent(indent); + printf("\n"); + while (pAttrib) + { + printf( "%s%s: value=[%s]", pIndent, pAttrib->Name(), pAttrib->Value()); + + if (pAttrib->QueryIntValue(&ival)==TIXML_SUCCESS) printf( " int=%d", ival); + if (pAttrib->QueryDoubleValue(&dval)==TIXML_SUCCESS) printf( " d=%1.1f", dval); + printf( "\n" ); + i++; + pAttrib=pAttrib->Next(); + } + return i; +} + +void dump_to_stdout( TiXmlNode* pParent, unsigned int indent = 0 ) +{ + if ( !pParent ) return; + + TiXmlNode* pChild; + TiXmlText* pText; + int t = pParent->Type(); + printf( "%s", getIndent(indent)); + int num; + + switch ( t ) + { + case TiXmlNode::DOCUMENT: + printf( "Document" ); + break; + + case TiXmlNode::ELEMENT: + printf( "Element [%s]", pParent->Value() ); + num=dump_attribs_to_stdout(pParent->ToElement(), indent+1); + switch(num) + { + case 0: printf( " (No attributes)"); break; + case 1: printf( "%s1 attribute", getIndentAlt(indent)); break; + default: printf( "%s%d attributes", getIndentAlt(indent), num); break; + } + break; + + case TiXmlNode::COMMENT: + printf( "Comment: [%s]", pParent->Value()); + break; + + case TiXmlNode::UNKNOWN: + printf( "Unknown" ); + break; + + case TiXmlNode::TEXT: + pText = pParent->ToText(); + printf( "Text: [%s]", pText->Value() ); + break; + + case TiXmlNode::DECLARATION: + printf( "Declaration" ); + break; + default: + break; + } + printf( "\n" ); + for ( pChild = pParent->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) + { + dump_to_stdout( pChild, indent+1 ); + } +} + +// load the named file and dump its structure to STDOUT +void dump_to_stdout(const char* pFilename) +{ + TiXmlDocument doc(pFilename); + bool loadOkay = doc.LoadFile(); + if (loadOkay) + { + printf("\n%s:\n", pFilename); + dump_to_stdout( &doc ); // defined later in the tutorial + } + else + { + printf("Failed to load file \"%s\"\n", pFilename); + } +} + +// ---------------------------------------------------------------------- +// main() for printing files named on the command line +// ---------------------------------------------------------------------- +int main(int argc, char* argv[]) +{ + for (int i=1; i Debug\tinyxml_1.exe example1.xml + +example1.xml: +Document ++ Declaration ++ Element [Hello] + (No attributes) + + Text: [World] +@endverbatim + + Authors and Changes +
    +
  • Written by Ellers, April, May, June 2005
  • +
  • Minor edits and integration into doc system, Lee Thomason September 2005
  • +
  • Updated by Ellers, October 2005
  • +
+
+ +*/ diff --git a/src/core/utility/configuration/tinyxml/tutorial_ticpp.txt b/src/core/utility/configuration/tinyxml/tutorial_ticpp.txt new file mode 100644 index 00000000..6e215385 --- /dev/null +++ b/src/core/utility/configuration/tinyxml/tutorial_ticpp.txt @@ -0,0 +1,220 @@ +/** +@page ticppTutorial TinyXML++ Tutorial +Take a look here @subpage ticpp + +This is a work in progress. + + +@page ticpp TinyXML++ +

General Concepts

+The TinyXML++ classes are all wrappers around the corresponding classes within TinyXML. + +There is no reason to create TinyXML++ objects on the heap, using @p new, because the memory is managed for you. If you choose +to use @p new to create TinyXML++ objects, you will @b always need to use @p delete to clean up. + +Basically, TinyXML++ objects are just wrappers around TinyXML pointers. + +

Goals

+- Simplify the use and interface of TinyXml, using C++ concepts. + - Use exceptions for error handling, so there are no return codes to check + - Use templates for automatic type conversion + - Use STL style iterators to move through nodes and attributes + +

Details

+

Use exceptions for error handling

+When using the original TinyXML, every function returns a value indicating +success or failure. A programmer would have to check that value to ensure +the function succeeded. + +Example: +@code +// Load a document +TiXmlDocument doc( pFilename ); +if ( !doc.LoadFile() ) return; + +// Get a node +TiXmlElement* pElem = doc.FirstChildElement(); +if ( !pElem ) return; + +// Get the node we want +pElem = pElem->NextSibling(); +if ( !pElem ) return; + +// do something useful here +@endcode + +An alternative was to use TiXmlHandle, which allows for function chaining by +checking the intermediate function return values: + +Example: +@code +// Load a document +TiXmlDocument doc(pFilename); +if (!doc.LoadFile()) return; + +// Make a document handle +TiXmlHandle hDoc(&doc); + +// Get an element by using the handle to chain calls +// Note the conversion of the TiXmlHandle to the TiXmlElement* - .Element() +TiXmlElement* pElem = hDoc.FirstChildElement().NextSibling().Element(); +if ( !pElem ) return; + +// do something useful here +@endcode + +With TinyXML++, if there is an error during a function call, it throws an exception. +This means that a programmer can assume that every function is successful, as +long as the functions are enclosed in a try-catch block. + +Example: +@code +try +{ + // Load a document + ticpp::Document doc( pFilename ); + doc.LoadFile(); + + // Get an element by chaining calls - no return values to check, no TiXmlHandle + ticpp::Element* pElem = doc.FirstChildElement()->NextSibling(); + + // do something useful here +} +catch( ticpp::Exception& ex ) +{ + // If any function has an error, execution will enter here. + // Report the error + std::cout << ex.what(); +} +@endcode + + +

Use templates for automatic type conversion

+When using TinyXML, a programmer either needs to convert values to and from +strings, or choose from one of many overloads to get the value in the desired +type. + +Example: +@code +// Load a document +TiXmlDocument doc( pFilename ); +if ( !doc.LoadFile() ) return; + +// Get a node +TiXmlElement* pElem = doc.FirstChildElement(); +if ( !pElem ) return; + +// Get the node we want +pElem = pElem->NextSibling(); +if ( !pElem ) return; + +// Get the attribute as a string, convert to int +const char* pszAttr = pElem->Attribute( "myAttribute" ); +int attr = atoi( pszAttr ); + +// Get the attribute as an int +int attr2; +if ( TIXML_SUCCESS != pElem->QueryIntAttribute( "myAttribute", &attr2 ) ) +{ + return; +} + +// Get the attribute as a double +double attr3; +if ( TIXML_SUCCESS != pElem->QueryDoubleAttribute( "myAttribute", &attr3 ) ) +{ + return; +} + +// Get the attribute as a float +float attr4; +if ( TIXML_SUCCESS != pElem->QueryFloatAttribute( "myAttribute", &attr4 ) ) +{ + return; +} +@endcode + +TinyXML++ uses templates for automatic type conversion. + +Example: +@code +try +{ + // Load a document + ticpp::Document doc( pFilename ); + doc.LoadFile(); + + // Get an element by chaining calls - no return values to check, no TiXmlHandle + ticpp::Element* pElem = doc.FirstChildElement()->NextSibling(); + + // GetAttribute can determine the type of the pointer, and convert automatically + + // Get the attribute as a string + std::string attr; + pElem->GetAttribute( "myAttribute", &attr ); + + // Get the attribute as an int + int attr2; + pElem->GetAttribute( "myAttribute", &attr2 ); + + // Get the attribute as an float + float attr3; + pElem->GetAttribute( "myAttribute", &attr3 ); + + // Get the attribute as an double + double attr4; + pElem->GetAttribute( "myAttribute", &attr4 ); + + // Get the attribute as an bool + bool attr5; + pElem->GetAttribute( "myAttribute", &attr5 ); + +} +catch( ticpp::Exception& ex ) +{ + // If any function has an error, execution will enter here. + // Report the error + std::cout << ex.what(); +} +@endcode +

Use STL style iterators to move through nodes and attributes

+TinyXML has two ways to iterate: + +First Method: +@code + for( child = parent->FirstChild( false ); child; child = child->NextSibling( false ) ) +@endcode + +Second Method: +@code + child = 0; + while( child = parent->IterateChildren( child ) ) +@endcode + +Although both methods work quite well, the syntax is not familiar. +TinyXML++ introduces iterators: +@code +ticpp::Iterator< ticpp::Node > child; +for ( child = child.begin( parent ); child != child.end(); child++ ) +@endcode + +Iterators have the added advantage of filtering by type: +@code +// Only iterates through Comment nodes +ticpp::Iterator< ticpp::Comment > child; +for ( child = child.begin( parent ); child != child.end(); child++ ) +@endcode + +@code +// Only iterates through Element nodes with value "ElementValue" +ticpp::Iterator< ticpp::Element > child( "ElementValue" ); +for ( child = child.begin( parent ); child != child.end(); child++ ) +@endcode + +Finally, Iterators also work with Attributes +@code +ticpp::Iterator< ticpp::Attribute > attribute; +for ( attribute = attribute.begin( element ); attribute != attribute.end(); attribute++ ) +@endcode + +*/ diff --git a/src/core/utility/datatypes/any.h b/src/core/utility/datatypes/any.h new file mode 100644 index 00000000..f9c40e00 --- /dev/null +++ b/src/core/utility/datatypes/any.h @@ -0,0 +1,191 @@ +/** + * @file argos3/core/utility/datatypes/any.h + * + * @brief This file provides the definition of the any type. + * + * The any type is a generic container that allows one to store objects of different + * types in a standard container. The implementation in this file is heavily + * inspired by the corresponding boost::any implementation + * (http://www.boost.org/libs/any) + * + * @author Carlo Pinciroli + */ + +#include +#include +#include + +#include + +namespace argos { + + class CAny { + + public: + + /** + * Constructor + */ + CAny() : + m_pcRef(NULL) {} + + /** + * Constructor with templetized argument + */ + template + CAny(const T& t_object) : + m_pcRef(new CReference(t_object)) {} + + /** + * Copy constructor + * Clone the reference to the object, if the reference is not NULL + */ + CAny(const CAny& c_any) : + m_pcRef(c_any.m_pcRef ? c_any.m_pcRef->Clone() : NULL) {} + + /** + * Destructor + */ + ~CAny() { + if(m_pcRef != NULL) + delete m_pcRef; + } + + /** + * Redefinition of the swap function of the std library + */ + CAny& swap(CAny& c_any) { + std::swap(m_pcRef, c_any.m_pcRef); + return *this; + } + + /** + * Assignment operator when object is operand + */ + template + CAny& operator=(const T& t_object) { + CAny(t_object).swap(*this); + return *this; + } + + /** + * Assignment operator when any type is operand + */ + CAny& operator=(const CAny& c_any) { + CAny(c_any).swap(*this); + return *this; + } + + public: + + /** + * Abstract reference to object + */ + class CAbstractReference { + public: + /** + * Destructor + */ + virtual ~CAbstractReference() = 0; + /** + * Returns the type info on the referenced object + */ + virtual const std::type_info& GetType() const = 0; + /** + * Clone the reference + */ + virtual CAbstractReference* Clone() const = 0; + }; + + /** + * Actual templetized reference to object + */ + template + class CReference : public CAbstractReference { + public: + CReference(const T& t_object) : m_tObject(t_object) {} + /** + * Destructor + */ + virtual ~CReference() {} + /** + * @return The type info on the referenced object + */ + virtual const std::type_info& GetType() const { + return typeid(T); + } + /** + * Clone the reference + */ + virtual CAbstractReference* Clone() const { + return new CReference(m_tObject); + } + private: + + /** + * Actual referenced object + */ + T m_tObject; + }; + + private: + + /** + * Pointer to object reference + */ + CAbstractReference* m_pcRef; + + }; + + /** + * Performs a cast on the any type to the desired type, when the any type is + * passed by non-const pointer + */ + template + T* any_cast(CAny* pc_any) { + /* NULL pointer passed? */ + if(pc_any == NULL) + return NULL; + /* OK, Non-NULL pointer; do types match? */ + if(pc_any->m_pcRef->GetType() == typeid(T)) + /* Types match -> return cast object */ + return &(static_cast*>(pc_any->m_pcRef))->m_tObject; + else + /* Types don't match -> return NULL */ + return NULL; + } + + /** + * Performs a cast on the any type to the desired type, when the any type is + * passed by const pointer + */ + template + const T* any_cast(const CAny* pc_any) { + if(pc_any == NULL) + return NULL; + if(pc_any->m_pcRef->GetType() == typeid(T)) + return &(static_cast*>(pc_any->m_pcRef))->m_tObject; + } + + /** + * Performs a cast on the any type to the desired type, when the any type is + * passed by const reference + */ + template + T any_cast(const CAny& c_any) { + /* Fall back to using pass-by-pointer version */ + const T* ptResult = any_cast(&c_any); + /* Did the cast succeed? */ + if(ptResult != NULL) + return *ptResult; + else { + char* pchDemangledOperandType = abi::__cxa_demangle(c_any.m_pcRef->GetType().name(), NULL, NULL, NULL); + char* pchDemangledTargetType = abi::__cxa_demangle(typeid(T).name(), NULL, NULL, NULL); + THROW_ARGOSEXCEPTION("Failed any_cast conversion from " << + pchDemangledOperandType << + " to " << + pchDemangledTargetType); + } + } + +} diff --git a/src/core/utility/datatypes/byte_array.cpp b/src/core/utility/datatypes/byte_array.cpp new file mode 100644 index 00000000..4f24b492 --- /dev/null +++ b/src/core/utility/datatypes/byte_array.cpp @@ -0,0 +1,368 @@ +/** + * @file argos3/core/utility/datatypes/byte_array.cpp + * + * @author Carlo Pinciroli - + */ + +#include "byte_array.h" + +namespace argos { + + /****************************************/ + /****************************************/ + + CByteArray::CByteArray(const UInt8* pun_buffer, + size_t un_size) { + AddBuffer(pun_buffer, un_size); + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::AddBuffer(const UInt8* pun_buffer, + size_t un_size) { + for(size_t i = 0; i < un_size; ++i) { + m_vecBuffer.push_back(pun_buffer[i]); + } + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::FetchBuffer(UInt8* pun_buffer, + size_t un_size) { + if(Size() < un_size) THROW_ARGOSEXCEPTION("Attempting to extract too many bytes from byte array (" << un_size << " requested, " << Size() << " available)"); + for(size_t i = 0; i < un_size; ++i) { + *(pun_buffer+i) = m_vecBuffer[i]; + } + m_vecBuffer.erase(m_vecBuffer.begin(), m_vecBuffer.begin() + un_size); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator<<(UInt8 un_value) { + m_vecBuffer.push_back(un_value); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator>>(UInt8& un_value) { + if(Size() < 1) THROW_ARGOSEXCEPTION("Attempting to extract too many bytes from byte array (1 requested, " << Size() << " available)"); + un_value = m_vecBuffer.front(); + m_vecBuffer.erase(m_vecBuffer.begin()); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator<<(SInt8 n_value) { + m_vecBuffer.push_back(n_value); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator>>(SInt8& n_value) { + if(Size() < 1) THROW_ARGOSEXCEPTION("Attempting to extract too many bytes from byte array (1 requested, " << Size() << " available)"); + n_value = m_vecBuffer.front(); + m_vecBuffer.erase(m_vecBuffer.begin()); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator<<(UInt16 un_value) { + UInt8* punByte = reinterpret_cast(&un_value); + m_vecBuffer.push_back(punByte[0]); + m_vecBuffer.push_back(punByte[1]); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator>>(UInt16& un_value) { + if(Size() < 2) THROW_ARGOSEXCEPTION("Attempting to extract too many bytes from byte array (2 requested, " << Size() << " available)"); + UInt8* punByte = reinterpret_cast(&un_value); + punByte[0] = m_vecBuffer[0]; + punByte[1] = m_vecBuffer[1]; + m_vecBuffer.erase(m_vecBuffer.begin(), m_vecBuffer.begin() + 2); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator<<(SInt16 n_value) { + UInt8* punByte = reinterpret_cast(&n_value); + m_vecBuffer.push_back(punByte[0]); + m_vecBuffer.push_back(punByte[1]); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator>>(SInt16& n_value) { + if(Size() < 2) THROW_ARGOSEXCEPTION("Attempting to extract too many bytes from byte array (2 requested, " << Size() << " available)"); + UInt8* punByte = reinterpret_cast(&n_value); + punByte[0] = m_vecBuffer[0]; + punByte[1] = m_vecBuffer[1]; + m_vecBuffer.erase(m_vecBuffer.begin(), m_vecBuffer.begin() + 2); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator<<(UInt32 un_value) { + UInt8* punByte = reinterpret_cast(&un_value); + m_vecBuffer.push_back(punByte[0]); + m_vecBuffer.push_back(punByte[1]); + m_vecBuffer.push_back(punByte[2]); + m_vecBuffer.push_back(punByte[3]); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator>>(UInt32& un_value) { + if(Size() < 4) THROW_ARGOSEXCEPTION("Attempting to extract too many bytes from byte array (4 requested, " << Size() << " available)"); + UInt8* punByte = reinterpret_cast(&un_value); + punByte[0] = m_vecBuffer[0]; + punByte[1] = m_vecBuffer[1]; + punByte[2] = m_vecBuffer[2]; + punByte[3] = m_vecBuffer[3]; + m_vecBuffer.erase(m_vecBuffer.begin(), m_vecBuffer.begin() + 4); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator<<(SInt32 un_value) { + UInt8* punByte = reinterpret_cast(&un_value); + m_vecBuffer.push_back(punByte[0]); + m_vecBuffer.push_back(punByte[1]); + m_vecBuffer.push_back(punByte[2]); + m_vecBuffer.push_back(punByte[3]); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator>>(SInt32& un_value) { + if(Size() < 4) THROW_ARGOSEXCEPTION("Attempting to extract too many bytes from byte array (4 requested, " << Size() << " available)"); + UInt8* punByte = reinterpret_cast(&un_value); + punByte[0] = m_vecBuffer[0]; + punByte[1] = m_vecBuffer[1]; + punByte[2] = m_vecBuffer[2]; + punByte[3] = m_vecBuffer[3]; + m_vecBuffer.erase(m_vecBuffer.begin(), m_vecBuffer.begin() + 4); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator<<(UInt64 un_value) { + UInt8* punByte = reinterpret_cast(&un_value); + m_vecBuffer.push_back(punByte[0]); + m_vecBuffer.push_back(punByte[1]); + m_vecBuffer.push_back(punByte[2]); + m_vecBuffer.push_back(punByte[3]); + m_vecBuffer.push_back(punByte[4]); + m_vecBuffer.push_back(punByte[5]); + m_vecBuffer.push_back(punByte[6]); + m_vecBuffer.push_back(punByte[7]); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator>>(UInt64& un_value) { + if(Size() < 8) THROW_ARGOSEXCEPTION("Attempting to extract too many bytes from byte array (8 requested, " << Size() << " available)"); + UInt8* punByte = reinterpret_cast(&un_value); + punByte[0] = m_vecBuffer[0]; + punByte[1] = m_vecBuffer[1]; + punByte[2] = m_vecBuffer[2]; + punByte[3] = m_vecBuffer[3]; + punByte[4] = m_vecBuffer[4]; + punByte[5] = m_vecBuffer[5]; + punByte[6] = m_vecBuffer[6]; + punByte[7] = m_vecBuffer[7]; + m_vecBuffer.erase(m_vecBuffer.begin(), m_vecBuffer.begin() + 8); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator<<(SInt64 un_value) { + UInt8* punByte = reinterpret_cast(&un_value); + m_vecBuffer.push_back(punByte[0]); + m_vecBuffer.push_back(punByte[1]); + m_vecBuffer.push_back(punByte[2]); + m_vecBuffer.push_back(punByte[3]); + m_vecBuffer.push_back(punByte[4]); + m_vecBuffer.push_back(punByte[5]); + m_vecBuffer.push_back(punByte[6]); + m_vecBuffer.push_back(punByte[7]); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator>>(SInt64& un_value) { + if(Size() < 8) THROW_ARGOSEXCEPTION("Attempting to extract too many bytes from byte array (8 requested, " << Size() << " available)"); + UInt8* punByte = reinterpret_cast(&un_value); + punByte[0] = m_vecBuffer[0]; + punByte[1] = m_vecBuffer[1]; + punByte[2] = m_vecBuffer[2]; + punByte[3] = m_vecBuffer[3]; + punByte[4] = m_vecBuffer[4]; + punByte[5] = m_vecBuffer[5]; + punByte[6] = m_vecBuffer[6]; + punByte[7] = m_vecBuffer[7]; + m_vecBuffer.erase(m_vecBuffer.begin(), m_vecBuffer.begin() + 8); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator<<(unsigned long int un_value) { + UInt32 unSize = sizeof(un_value); + UInt8* punByte = reinterpret_cast(&un_value); + for(UInt32 i = 0; i < unSize; ++i) { + m_vecBuffer.push_back(punByte[i]); + } + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator>>(unsigned long int& un_value) { + UInt32 unSize = sizeof(un_value); + if(Size() < unSize) THROW_ARGOSEXCEPTION("Attempting to extract too many bytes from byte array (" << unSize << " requested, " << Size() << " available)"); + UInt8* punByte = reinterpret_cast(&un_value); + for(UInt32 i = 0; i < unSize; ++i) { + *punByte = m_vecBuffer[i]; + ++punByte; + } + m_vecBuffer.erase(m_vecBuffer.begin(), m_vecBuffer.begin() + unSize); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator<<(signed long int n_value) { + UInt32 unSize = sizeof(n_value); + UInt8* punByte = reinterpret_cast(&n_value); + for(UInt32 i = 0; i < unSize; ++i) { + m_vecBuffer.push_back(punByte[i]); + } + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator>>(signed long int& n_value) { + UInt32 unSize = sizeof(n_value); + if(Size() < unSize) THROW_ARGOSEXCEPTION("Attempting to extract too many bytes from byte array (" << unSize << " requested, " << Size() << " available)"); + UInt8* punByte = reinterpret_cast(&n_value); + for(UInt32 i = 0; i < unSize; ++i) { + *punByte = m_vecBuffer[i]; + ++punByte; + } + m_vecBuffer.erase(m_vecBuffer.begin(), m_vecBuffer.begin() + unSize); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator<<(Real f_value) { + UInt32 unSize = sizeof(f_value); + UInt8* punByte = reinterpret_cast(&f_value); + for(UInt32 i = 0; i < unSize; ++i) { + m_vecBuffer.push_back(punByte[i]); + } + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator>>(Real& f_value) { + UInt32 unSize = sizeof(f_value); + if(Size() < unSize) THROW_ARGOSEXCEPTION("Attempting to extract too many bytes from byte array (" << unSize << " requested, " << Size() << " available)"); + UInt8* punByte = reinterpret_cast(&f_value); + for(UInt32 i = 0; i < unSize; ++i) { + *punByte = m_vecBuffer[i]; + ++punByte; + } + m_vecBuffer.erase(m_vecBuffer.begin(), m_vecBuffer.begin() + unSize); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator<<(const std::string& str_value) { + for(size_t i = 0; i < str_value.size(); ++i) { + *this << static_cast(str_value[i]); + } + *this << static_cast(0); + return *this; + } + + /****************************************/ + /****************************************/ + + CByteArray& CByteArray::operator>>(std::string& str_value) { + str_value.clear(); + if(Empty()) THROW_ARGOSEXCEPTION("Attempting to extract values from empty byte array"); + size_t i = 0; + while(i < Size() && m_vecBuffer[i] != '\0') { + str_value += m_vecBuffer[i]; + ++i; + } + if(m_vecBuffer[i] == '\0') { + ++i; + } + m_vecBuffer.erase(m_vecBuffer.begin(), m_vecBuffer.begin() + i); + return *this; + } + + /****************************************/ + /****************************************/ + + std::ostream& operator<<(std::ostream& c_os, const CByteArray& c_byte_array) { + c_os << "CByteArray ["; + for(size_t i = 0; i < c_byte_array.Size(); ++i) { + c_os << " " << c_byte_array.m_vecBuffer[i]; + } + c_os << " ]" << std::endl; + return c_os; + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/utility/datatypes/byte_array.h b/src/core/utility/datatypes/byte_array.h new file mode 100644 index 00000000..9f70ecae --- /dev/null +++ b/src/core/utility/datatypes/byte_array.h @@ -0,0 +1,304 @@ +/** + * @file argos3/core/utility/datatypes/byte_array.h + * + * @brief Defines the byte array class. + * + * This file provides the definition of the argos::CByteArray class. + * + * @author Carlo Pinciroli - + */ + +#ifndef BYTE_ARRAY_H +#define BYTE_ARRAY_H + +#include +#include +#include +#include + +namespace argos { + + /** + * Byte array utility class. + * This class is useful for serializing any kind of data into a byte array, + * to be then streamed to something. It is used by the argos::CMemento class + * as a basic tool to store the state of an object and retrieve it. + */ + class CByteArray { + + public: + + /** + * Class constructor. + */ + CByteArray() {} + + /** + * Class copy constructor. + */ + CByteArray(const CByteArray& c_byte_array) : + m_vecBuffer(c_byte_array.m_vecBuffer) {} + + /** + * Class constructor. + * Copies the given buffer into the byte array. The original + * buffer can be safely deleted. + * @param pun_buffer the original buffer to copy from. + * @param un_size the size of the original buffer. + */ + CByteArray(const UInt8* pun_buffer, + size_t un_size); + + /** + * Returns the current size of the byte array. + * @return the current size of the byte array. + */ + inline size_t Size() const { + return m_vecBuffer.size(); + } + + /** + * Returns true if the byte array is empty. + * @return true if the byte array is empty. + */ + inline bool Empty() const { + return m_vecBuffer.empty(); + } + + /** + * Assignment operator. + * Deep-copies the given byte array into the current byte array. + */ + inline CByteArray& operator=(const CByteArray& c_byte_array) { + if(this != &c_byte_array) { + m_vecBuffer = c_byte_array.m_vecBuffer; + } + return *this; + } + + /** + * Read/write index operator. + * @param un_index the index of the wanted element. + * @returns a reference to the wanted element. + */ + inline UInt8& operator[](size_t un_index) { + if(un_index >= Size()) THROW_ARGOSEXCEPTION("CByteArray: index out of bounds [index = " << un_index << ", size=" << Size() << "]"); + return m_vecBuffer.at(un_index); + } + + /** + * Read-only index operator. + * @param un_index the index of the wanted element. + * @returns the value of the wanted element. + */ + inline UInt8 operator[](size_t un_index) const { + if(un_index >= Size()) THROW_ARGOSEXCEPTION("CByteArray: index out of bounds [index = " << un_index << ", size=" << Size() << "]"); + return m_vecBuffer.at(un_index); + } + + /** + * Appends bytes to the byte array. + * @param pun_buffer the byte buffer to copy from. + * @param un_size the size of the byte buffer. + * @return a reference to this byte array. + */ + CByteArray& AddBuffer(const UInt8* pun_buffer, + size_t un_size); + + /** + * Moves elements from the byte array into the passed buffer. + * The elements whose values were written into the target buffer are removed from the byte array. + * @param pun_buffer the byte buffer to write into. + * @param un_size the size of the target byte buffer. + * @return a reference to this byte array. + */ + CByteArray& FetchBuffer(UInt8* pun_buffer, + size_t un_size); + + /** + * Appends a 8-bit unsigned integer to the byte array. + * @param un_value the value of the 8-bit unsigned integer. + * @return a reference to this byte array. + */ + CByteArray& operator<<(UInt8 un_value); + /** + * Moves an 8-bit unsigned integer from the byte array to the target variable. + * The element whose value was written into the target buffer are removed from the byte array. + * @param un_value the buffer for the 8-bit unsigned integer. + * @return a reference to this byte array. + */ + CByteArray& operator>>(UInt8& un_value); + /** + * Appends a 8-bit signed integer to the byte array. + * @param n_value the value of the 8-bit signed integer. + * @return a reference to this byte array. + */ + CByteArray& operator<<(SInt8 n_value); + /** + * Moves an 8-bit signed integer from the byte array to the target variable. + * The element whose value was written into the target buffer are removed from the byte array. + * @param n_value the buffer for the 8-bit unsigned integer. + * @return a reference to this byte array. + */ + CByteArray& operator>>(SInt8& n_value); + /** + * Appends a 16-bit unsigned integer to the byte array. + * @param un_value the value of the 16-bit unsigned integer. + * @return a reference to this byte array. + */ + CByteArray& operator<<(UInt16 un_value); + /** + * Moves a 16-bit unsigned integer from the byte array to the target variable. + * The element whose value was written into the target buffer are removed from the byte array. + * @param un_value the buffer for the 16-bit unsigned integer. + * @return a reference to this byte array. + */ + CByteArray& operator>>(UInt16& un_value); + /** + * Appends a 16-bit signed integer to the byte array. + * @param n_value the value of the 16-bit signed integer. + * @return a reference to this byte array. + */ + CByteArray& operator<<(SInt16 n_value); + /** + * Moves a 16-bit signed integer from the byte array to the target variable. + * The element whose value was written into the target buffer are removed from the byte array. + * @param n_value the buffer for the 16-bit signed integer. + * @return a reference to this byte array. + */ + CByteArray& operator>>(SInt16& n_value); + /** + * Appends a 32-bit unsigned integer to the byte array. + * @param un_value the value of the 32-bit unsigned integer. + * @return a reference to this byte array. + */ + CByteArray& operator<<(UInt32 un_value); + /** + * Moves a 32-bit unsigned integer from the byte array to the target variable. + * The element whose value was written into the target buffer are removed from the byte array. + * @param un_value the buffer for the 32-bit unsigned integer. + * @return a reference to this byte array. + */ + CByteArray& operator>>(UInt32& un_value); + /** + * Appends a 32-bit signed integer to the byte array. + * @param n_value the value of the 32-bit signed integer. + * @return a reference to this byte array. + */ + CByteArray& operator<<(SInt32 n_value); + /** + * Moves a 32-bit signed integer from the byte array to the target variable. + * The element whose value was written into the target buffer are removed from the byte array. + * @param n_value the buffer for the 32-bit signed integer. + * @return a reference to this byte array. + */ + CByteArray& operator>>(SInt32& n_value); + /** + * Appends a 64-bit unsigned integer to the byte array. + * @param un_value the value of the 64-bit unsigned integer. + * @return a reference to this byte array. + */ + CByteArray& operator<<(UInt64 un_value); + /** + * Moves a 64-bit unsigned integer from the byte array to the target variable. + * The element whose value was written into the target buffer are removed from the byte array. + * @param un_value the buffer for the 64-bit unsigned integer. + * @return a reference to this byte array. + */ + CByteArray& operator>>(UInt64& un_value); + /** + * Appends a 64-bit signed integer to the byte array. + * @param n_value the value of the 64-bit signed integer. + * @return a reference to this byte array. + */ + CByteArray& operator<<(SInt64 n_value); + /** + * Moves a 64-bit signed integer from the byte array to the target variable. + * The element whose value was written into the target buffer are removed from the byte array. + * @param n_value the buffer for the 64-bit signed integer. + * @return a reference to this byte array. + */ + CByteArray& operator>>(SInt64& n_value); + /** + * Appends an unsigned long integer to the byte array. + * This function should be avoided as much as possible because + * the size of long integers changes on 32- and 64-bit + * architectures. + * @param un_value the value of the unsigned long integer. + * @return a reference to this byte array. + */ + CByteArray& operator<<(unsigned long int un_value); + /** + * Moves an unsigned long integer from the byte array to the target variable. + * The element whose value was written into the target buffer are removed from the byte array. + * This function should be avoided as much as possible because + * the size of long integers changes on 32- and 64-bit + * architectures. + * @param un_value the buffer for the unsigned long integer. + * @return a reference to this byte array. + */ + CByteArray& operator>>(unsigned long int& un_value); + /** + * Appends a signed long integer to the byte array. + * This function should be avoided as much as possible because + * the size of long integers changes on 32- and 64-bit + * architectures. + * @param n_value the value of the signed long integer. + * @return a reference to this byte array. + */ + CByteArray& operator<<(signed long int n_value); + /** + * Moves a signed long integer from the byte array to the target variable. + * The element whose value was written into the target buffer are removed from the byte array. + * This function should be avoided as much as possible because + * the size of long integers changes on 32- and 64-bit + * architectures. + * @param un_value the buffer for the signed long integer. + * @return a reference to this byte array. + */ + CByteArray& operator>>(signed long int& n_value); + /** + * Appends an argos::Real to the byte array. + * @param f_value the value of the argos::Real. + * @return a reference to this byte array. + */ + CByteArray& operator<<(Real f_value); + /** + * Moves an argos::Real from the byte array to the target variable. + * The element whose value was written into the target buffer are removed from the byte array. + * @param f_value the buffer for the argos::Real. + * @return a reference to this byte array. + */ + CByteArray& operator>>(Real& f_value); + /** + * Appends a std::string to the byte array. + * @param str_value the value of the std::string. + * @return a reference to this byte array. + */ + CByteArray& operator<<(const std::string& str_value); + /** + * Moves a std::string from the byte array to the target variable. + * The element whose value was written into the target buffer are removed from the byte array. + * @param str_value the buffer for the std::string. + * @return a reference to this byte array. + */ + CByteArray& operator>>(std::string& str_value); + + /** + * Stream operator. + * It streams the byte array through the given stream. Useful for files, for instance. + * @param c_os the C++ output stream the byte array to. + * @param c_byte_array the byte array to stream. + * @return the new state of the output stream. + */ + friend std::ostream& operator<<(std::ostream& c_os, const CByteArray& c_byte_array); + + private: + + std::vector m_vecBuffer; + + }; + +} + +#endif diff --git a/src/core/utility/datatypes/color.cpp b/src/core/utility/datatypes/color.cpp new file mode 100644 index 00000000..94c4d207 --- /dev/null +++ b/src/core/utility/datatypes/color.cpp @@ -0,0 +1,32 @@ +/** + * @file argos3/core/utility/datatypes/color.cpp + * + * @brief This file provides + * + * @author Carlo Pinciroli - + */ + +#include "color.h" + +namespace argos { + + CColor CColor::BLACK(0,0,0); + CColor CColor::WHITE(255, 255, 255); + CColor CColor::RED(255,0,0); + CColor CColor::GREEN(0,255,0); + CColor CColor::BLUE(0,0,255); + CColor CColor::MAGENTA(255,0,255); + CColor CColor::CYAN(0,255,255); + CColor CColor::YELLOW(255,255,0); + CColor CColor::ORANGE(255,140,0); + CColor CColor::GRAY10(26,26,26); + CColor CColor::GRAY20(51,51,51); + CColor CColor::GRAY30(77,77,77); + CColor CColor::GRAY40(102,102,102); + CColor CColor::GRAY50(127,127,127); + CColor CColor::GRAY60(153,153,153); + CColor CColor::GRAY70(179,179,179); + CColor CColor::GRAY80(204,204,204); + CColor CColor::GRAY90(229,229,229); + +} diff --git a/src/core/utility/datatypes/color.h b/src/core/utility/datatypes/color.h new file mode 100644 index 00000000..73b2e412 --- /dev/null +++ b/src/core/utility/datatypes/color.h @@ -0,0 +1,290 @@ +/** + * @file argos3/core/utility/datatypes/color.h + * + * @brief Basic color-related definitions. + * + * This file provides the definition of the class argos::CColor. + * + * @author Carlo Pinciroli - + */ + +#ifndef COLOR_H +#define COLOR_H + +#include +#include +#include +#include + +namespace argos { + + /** + * The basic color type. + * In ARGoS, color is encoded into the RGBA schema. + */ + class CColor { + + public: + + static CColor BLACK; + static CColor WHITE; + static CColor RED; + static CColor GREEN; + static CColor BLUE; + static CColor MAGENTA; + static CColor CYAN; + static CColor YELLOW; + static CColor ORANGE; + static CColor GRAY10; + static CColor GRAY20; + static CColor GRAY30; + static CColor GRAY40; + static CColor GRAY50; + static CColor GRAY60; + static CColor GRAY70; + static CColor GRAY80; + static CColor GRAY90; + + /** + * Class constructor. + */ + CColor() {} + + /** + * Class constructor. + */ + explicit CColor(const UInt8 un_red, + const UInt8 un_green, + const UInt8 un_blue, + const UInt8 un_alpha = 255) throw() : + m_tChannels(un_red, un_green, un_blue, un_alpha) {} + + /** + * Returns the color in grayscale. + * @returns the color in grayscale. The returned value is in the range [0,1]. + */ + inline Real ToGrayScale() const throw() { + return + 0.299f * m_tChannels.m_unRed + + 0.587f * m_tChannels.m_unGreen + + 0.114f * m_tChannels.m_unBlue; + } + + /** + * Returns the red channel of the color. + * @returns the red channel of the color. + */ + inline UInt8 GetRed() const throw() { return m_tChannels.m_unRed; } + /** + * Sets the red channel of the color. + * @param un_red the red channel of the color. + */ + inline void SetRed(const UInt8 un_red) throw() { m_tChannels.m_unRed = un_red; } + + /** + * Returns the green channel of the color. + * @returns the green channel of the color. + */ + inline UInt8 GetGreen() const throw() { return m_tChannels.m_unGreen; } + /** + * Sets the green channel of the color. + * @param un_green the green channel of the color. + */ + inline void SetGreen(const UInt8 un_green) throw() { m_tChannels.m_unGreen = un_green; } + + /** + * Returns the blue channel of the color. + * @returns the blue channel of the color. + */ + inline UInt8 GetBlue() const throw() { return m_tChannels.m_unBlue; } + /** + * Sets the blue channel of the color. + * @param un_blue the blue channel of the color. + */ + inline void SetBlue(const UInt8 un_blue) throw() { m_tChannels.m_unBlue = un_blue; } + + /** + * Returns the alpha channel of the color. + * @returns the alpha channel of the color. + */ + inline UInt8 GetAlpha() const throw() { return m_tChannels.m_unAlpha; } + /** + * Sets the alpha channel of the color. + * @param un_alpha the alpha channel of the color. + */ + inline void SetAlpha(const UInt8 un_alpha) throw() { m_tChannels.m_unAlpha = un_alpha; } + + /** + * Sets the RGBA values of the color. + * @param un_red the red channel of the color. + * @param un_green the green channel of the color. + * @param un_blue the blue channel of the color. + * @param un_alpha the alpha channel of the color. + */ + inline void Set(const UInt8 un_red, + const UInt8 un_green, + const UInt8 un_blue, + const UInt8 un_alpha = 255) throw() { + SetRed(un_red); + SetGreen(un_green); + SetBlue(un_blue); + SetAlpha(un_alpha); + } + + /** + * Cast operator. + * Casts the color into a 32-bit structure. + */ + inline operator UInt32() { + return *reinterpret_cast(&m_tChannels); + } + + /** + * Returns true if the given color is identical to the current. + * @param c_color2 the color to compare the current color to. + * @return true if the given color is identical to the current. + */ + inline bool operator==(const CColor& c_color2) const throw() { + return m_tChannels == c_color2.m_tChannels; + } + + /** + * Returns true if the given color is different from the current. + * @param c_color2 the color to compare the current color to. + * @return true if the given color is different from the current. + */ + inline bool operator!=(const CColor& c_color2) const throw() { + return m_tChannels != c_color2.m_tChannels; + } + + /** + * Output stream operator. + * Operator to serialize a color into a C++ output stream. When a color + * corresponds to one of the predefined constants (such as argos::CColor::RED) + * it streams the color name. For instance, if the current color is + * argos::CColor::RED, the output is a string containing red. Otherwise, + * the color is output as a tuple <r,g,b,a>. + * @param os the C++ output stream. + * @param c_color the color to stream. + * @return the new state of the C++ output stream. + */ + friend std::ostream& operator<<(std::ostream& os, + const CColor& c_color) { + if (c_color == CColor::BLACK) os << "black"; + else if (c_color == CColor::WHITE) os << "white"; + else if (c_color == CColor::RED) os << "red"; + else if (c_color == CColor::GREEN) os << "green"; + else if (c_color == CColor::BLUE) os << "blue"; + else if (c_color == CColor::MAGENTA) os << "magenta"; + else if (c_color == CColor::CYAN) os << "cyan"; + else if (c_color == CColor::YELLOW) os << "yellow"; + else if (c_color == CColor::ORANGE) os << "orange"; + else if (c_color == CColor::GRAY10) os << "gray10"; + else if (c_color == CColor::GRAY20) os << "gray20"; + else if (c_color == CColor::GRAY30) os << "gray30"; + else if (c_color == CColor::GRAY40) os << "gray40"; + else if (c_color == CColor::GRAY50) os << "gray50"; + else if (c_color == CColor::GRAY60) os << "gray60"; + else if (c_color == CColor::GRAY70) os << "gray70"; + else if (c_color == CColor::GRAY80) os << "gray80"; + else if (c_color == CColor::GRAY90) os << "gray90"; + else { + os << c_color.m_tChannels.m_unRed + << "," << c_color.m_tChannels.m_unGreen + << "," << c_color.m_tChannels.m_unBlue + << "," << c_color.m_tChannels.m_unAlpha; + } + return os; + } + + /** + * Input stream operator. + * Operator to get a color from a C++ input stream. The input stream + * value can contain predefined color names, such as red. The + * color can be also input as a tuple <r,g,b,a>. + * @param is the C++ input stream. + * @param c_color the color to fill. + * @return the new state of the C++ input stream. + */ + friend std::istream& operator>>(std::istream& is, + CColor& c_color) { + try { + std::string strColor; + is >> strColor; + if (strColor == "black") c_color = CColor::BLACK; + else if (strColor == "white") c_color = CColor::WHITE; + else if (strColor == "red") c_color = CColor::RED; + else if (strColor == "green") c_color = CColor::GREEN; + else if (strColor == "blue") c_color = CColor::BLUE; + else if (strColor == "magenta") c_color = CColor::MAGENTA; + else if (strColor == "cyan") c_color = CColor::CYAN; + else if (strColor == "yellow") c_color = CColor::YELLOW; + else if (strColor == "orange") c_color = CColor::ORANGE; + else if (strColor == "gray10") c_color = CColor::GRAY10; + else if (strColor == "gray20") c_color = CColor::GRAY20; + else if (strColor == "gray30") c_color = CColor::GRAY30; + else if (strColor == "gray40") c_color = CColor::GRAY40; + else if (strColor == "gray50") c_color = CColor::GRAY50; + else if (strColor == "gray60") c_color = CColor::GRAY60; + else if (strColor == "gray70") c_color = CColor::GRAY70; + else if (strColor == "gray80") c_color = CColor::GRAY80; + else if (strColor == "gray90") c_color = CColor::GRAY90; + else { + UInt8 unValues[4]; + ParseValues(strColor, 4, unValues, ','); + c_color.Set(unValues[0], unValues[1], unValues[2], unValues[3]); + } + } + catch(CARGoSException& ex) { + THROW_ARGOSEXCEPTION_NESTED("Error while parsing color input string", ex); + } + return is; + } + + + private: + + struct TChannels { + UInt8 m_unRed; + UInt8 m_unGreen; + UInt8 m_unBlue; + UInt8 m_unAlpha; + + TChannels() : + m_unRed(0), + m_unGreen(0), + m_unBlue(0), + m_unAlpha(255) {} + + TChannels(const UInt8 un_red, + const UInt8 un_green, + const UInt8 un_blue, + const UInt8 un_alpha = 255) : + m_unRed(un_red), + m_unGreen(un_green), + m_unBlue(un_blue), + m_unAlpha(un_alpha) {} + + inline bool operator==(const TChannels& t_channels) const { + return + (m_unRed == t_channels.m_unRed) && + (m_unGreen == t_channels.m_unGreen) && + (m_unBlue == t_channels.m_unBlue) && + (m_unAlpha == t_channels.m_unAlpha); + } + + inline bool operator!=(const TChannels& t_channels) const { + return + (m_unRed != t_channels.m_unRed) || + (m_unGreen != t_channels.m_unGreen) || + (m_unBlue != t_channels.m_unBlue) || + (m_unAlpha != t_channels.m_unAlpha); + } + + } m_tChannels; + + }; + +} + +#endif diff --git a/src/core/utility/datatypes/datatypes.h b/src/core/utility/datatypes/datatypes.h new file mode 100644 index 00000000..025a97ac --- /dev/null +++ b/src/core/utility/datatypes/datatypes.h @@ -0,0 +1,117 @@ +/** + * @file argos3/core/utility/datatypes/datatypes.h + * + * This file provides the basic data type definitions of ARGoS. + * The definitions provided here are cross-platform. For instance, a argos::SInt16 + * variable will be a 16-bit signed integer on all platforms ARGoS is ported to. + * + * The argos::Real type wraps the native C++ types float and double. + * Whether argos::Real is defined as float or double depends on the + * compilation flags set when building ARGoS. If nothing was specified, which is + * the default, argos::Real will be defined as float. Otherwise, if the + * macro -DARGOS_DOUBLE_PRECISION is specified, Real is defined as + * double. + * + * @author Carlo Pinciroli - + */ + +#ifndef DATATYPES_H +#define DATATYPES_H + +#ifdef __cplusplus +#include +#endif + +/** + * Collects all ARGoS code. + */ +#ifdef __cplusplus +namespace argos { +#endif + + /** + * The basic floating point type in ARGoS. + * Real is defined either as float or double, depending on the flags set + * for compilation. If you don't specify anything, ARGoS defaults to using floats. + * If you compiled with -DARGOS_DOUBLE_PRECISION, ARGoS will be compiled with + * doubles. + */ +#ifdef ARGOS_DOUBLE_PRECISION + typedef double Real; +#else + typedef float Real; +#endif + + /** + * 8-bit signed integer. + */ + typedef signed char SInt8; + +#ifdef __cplusplus + /** + * Overloaded operator to have C++ streams handle correctly 8-bit signed integers. + * @see SInt8 + */ + inline std::ostream& operator<<(std::ostream& c_os, const SInt8 n_value) { + c_os << static_cast(n_value); + return c_os; + } +#endif + /** + * 8-bit unsigned integer. + */ + typedef unsigned char UInt8; +#ifdef __cplusplus + /** + * Overloaded operator to have C++ streams handle correctly 8-bit unsigned integers. + * @see UInt8 + */ + inline std::ostream& operator<<(std::ostream& c_os, const UInt8 un_value) { + c_os << static_cast(un_value); + return c_os; + } +#endif + /** + * 16-bit signed integer. + */ + typedef signed short SInt16; + /** + * 16-bit unsigned integer. + */ + typedef unsigned short UInt16; + +#ifdef __dsPIC30 + /** + * 32-bit signed integer. + */ + typedef signed long int SInt32; + /** + * 32-bit unsigned integer. + */ + typedef unsigned long int UInt32; +#else + /** + * 32-bit signed integer. + */ + typedef signed int SInt32; + /** + * 32-bit unsigned integer. + */ + typedef unsigned int UInt32; +#endif + + /** + * 64-bit signed integer. + */ + typedef signed long long SInt64; + /** + * 64-bit unsigned integer. + */ + typedef unsigned long long UInt64; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/core/utility/logging/argos_colored_text.h b/src/core/utility/logging/argos_colored_text.h new file mode 100644 index 00000000..e27b1fb9 --- /dev/null +++ b/src/core/utility/logging/argos_colored_text.h @@ -0,0 +1,135 @@ +/** + * @file argos3/core/utility/logging/argos_colored_text.h + * + * @brief This file provides a list of functions to add colors to std::ostreams. + * + * Credit for some of the ideas in this file to Vincent Godin. + * See http://www.codeproject.com/KB/cpp/cout_color.aspx (accessed May 10th, 2010). + * + * @author Carlo Pinciroli - + */ + +#ifndef ARGOS_COLORED_TEXT_H +#define ARGOS_COLORED_TEXT_H + +#include + +namespace argos { + + /** + * The possible attributes of the logged text. + */ + enum EARGoSLogAttributes { + ARGOS_LOG_ATTRIBUTE_RESET = 0, + ARGOS_LOG_ATTRIBUTE_BRIGHT = 1, + ARGOS_LOG_ATTRIBUTE_DIM = 2, + ARGOS_LOG_ATTRIBUTE_UNDERSCORE = 3, + ARGOS_LOG_ATTRIBUTE_BLINK = 5, + ARGOS_LOG_ATTRIBUTE_REVERSE = 7, + ARGOS_LOG_ATTRIBUTE_HIDDEN = 8 + }; + + /** + * The possible colors of the logged text. + */ + enum EARGoSLogColors { + ARGOS_LOG_COLOR_BLACK = 0, + ARGOS_LOG_COLOR_RED = 1, + ARGOS_LOG_COLOR_GREEN = 2, + ARGOS_LOG_COLOR_YELLOW = 3, + ARGOS_LOG_COLOR_BLUE = 4, + ARGOS_LOG_COLOR_MAGENTA = 5, + ARGOS_LOG_COLOR_CYAN = 6, + ARGOS_LOG_COLOR_WHITE = 7 + }; + + /** + * Stream modifier to set attribute and color of the subsequent text. + */ + struct SLogColor { + /** + * The text attribute. + */ + UInt8 Attribute; + /** + * The foreground color. + */ + UInt8 Foreground; + + /** + * Constructor with standard settings. + */ + SLogColor() : + Attribute(ARGOS_LOG_ATTRIBUTE_RESET), + Foreground(ARGOS_LOG_COLOR_WHITE) {} + /** + * Constructor with custom settings. + */ + SLogColor(EARGoSLogAttributes e_attribute, + EARGoSLogColors e_foreground_color) : + Attribute(e_attribute), + Foreground(e_foreground_color) {} + }; + + /** + * Stream operator that accepts the stream modifier. + * @see SLogColor + */ + inline std::ostream& operator<<(std::ostream& c_os, const SLogColor& s_log_color) + { + c_os << "\033[" + << s_log_color.Attribute << ";" + << (s_log_color.Foreground + 30) << "m"; + return c_os; + } + + /** + * Utility macro to ease the definition of the presets. + */ +#define DEFINE_ARGOS_STREAM_COLOR_HELPER(lc_color, uc_color) \ + inline std::ostream& lc_color(std::ostream& c_os) { \ + c_os << SLogColor(ARGOS_LOG_ATTRIBUTE_BRIGHT, ARGOS_LOG_COLOR_ ## uc_color); \ + return c_os; \ + } + + /** + * Bright red text modifier. + */ + DEFINE_ARGOS_STREAM_COLOR_HELPER(red, RED); + /** + * Bright green text modifier. + */ + DEFINE_ARGOS_STREAM_COLOR_HELPER(green, GREEN); + /** + * Bright yellow text modifier. + */ + DEFINE_ARGOS_STREAM_COLOR_HELPER(yellow, YELLOW); + /** + * Bright blue text modifier. + */ + DEFINE_ARGOS_STREAM_COLOR_HELPER(blue, BLUE); + /** + * Bright magenta text modifier. + */ + DEFINE_ARGOS_STREAM_COLOR_HELPER(magenta, MAGENTA); + /** + * Bright cyan text modifier. + */ + DEFINE_ARGOS_STREAM_COLOR_HELPER(cyan, CYAN); + /** + * Bright white text modifier. + */ + DEFINE_ARGOS_STREAM_COLOR_HELPER(white, WHITE); + + /** + * Resets the text to the default settings. + * The default settings are white text on black background. + */ + inline std::ostream& reset(std::ostream& c_os) { + c_os << SLogColor(ARGOS_LOG_ATTRIBUTE_RESET, ARGOS_LOG_COLOR_WHITE); + return c_os; + } + +} + +#endif diff --git a/src/core/utility/logging/argos_log.cpp b/src/core/utility/logging/argos_log.cpp new file mode 100644 index 00000000..f01b7bd0 --- /dev/null +++ b/src/core/utility/logging/argos_log.cpp @@ -0,0 +1,14 @@ +/** + * @file argos3/core/utility/logging/argos_log.cpp + * + * @author Carlo Pinciroli - + */ + +#include "argos_log.h" + +namespace argos { + + CARGoSLog LOG(std::cout, SLogColor(ARGOS_LOG_ATTRIBUTE_BRIGHT, ARGOS_LOG_COLOR_GREEN)); + CARGoSLog LOGERR(std::cerr, SLogColor(ARGOS_LOG_ATTRIBUTE_BRIGHT, ARGOS_LOG_COLOR_RED)); + +} diff --git a/src/core/utility/logging/argos_log.h b/src/core/utility/logging/argos_log.h new file mode 100644 index 00000000..c117c3e2 --- /dev/null +++ b/src/core/utility/logging/argos_log.h @@ -0,0 +1,179 @@ +/** + * @file argos3/core/utility/logging/argos_log.h + * + * @brief This file provides the ARGoSLogger class. + * + * The LOG and LOGERR statements write to standard out and standard err, + * respectively. LOG is meant to report normal events that are considered + * interesting for the user. LOGERR reports errors and warning the user + * must consider during an experimental run. Their usage is identical to + * the standard C++ statements std::cout and std::cerr. In fact, LOG and + * LOGERR redirect these streams. + * + * @author Carlo Pinciroli - + */ + +#ifndef ARGOSLOG_H +#define ARGOSLOG_H + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ARGOS_THREADSAFE_LOG +#include +#include +#include +#include +#endif + +namespace argos { + class CARGOSLogger; +} + +#include + +namespace argos { + + class CARGoSLog { + + private: + + /** The stream to write to */ + std::ostream& m_cStream; + + /** The default output color */ + SLogColor m_sLogColor; + + /** True when we want to use color */ + bool m_bColoredOutput; + +#ifdef ARGOS_THREADSAFE_LOG + /** Maps a thread to its visualization order */ + std::map m_mapStreamOrder; + + /** Contains the buffer streams */ + std::vector m_vecStreams; + + /** The mutex to protect the operations on the buffers */ + pthread_mutex_t m_tMutex; +#endif + + public: + + CARGoSLog(std::ostream& c_stream, + const SLogColor& s_log_color, + bool b_colored_output_enabled = true) : + m_cStream(c_stream), + m_sLogColor(s_log_color), + m_bColoredOutput(b_colored_output_enabled) { +#ifdef ARGOS_THREADSAFE_LOG + pthread_mutex_init(&m_tMutex, NULL); + AddThreadSafeBuffer(); +#endif + } + + ~CARGoSLog() { +#ifdef ARGOS_THREADSAFE_LOG + pthread_mutex_destroy(&m_tMutex); + while(!m_vecStreams.empty()) { + delete m_vecStreams.back(); + m_vecStreams.pop_back(); + } +#endif + } + + inline void EnableColoredOutput() { + m_bColoredOutput = true; + } + + inline void DisableColoredOutput() { + m_bColoredOutput = false; + } + + inline bool IsColoredOutput() const { + return m_bColoredOutput; + } + + inline std::ostream& GetStream() { + return m_cStream; + } + +#ifdef ARGOS_THREADSAFE_LOG + inline void Flush() { + pthread_mutex_lock(&m_tMutex); + for(size_t i = 0; i < m_vecStreams.size(); ++i) { + m_cStream << m_vecStreams[i]->str(); + m_vecStreams[i]->str(""); + } + pthread_mutex_unlock(&m_tMutex); + } + + inline void AddThreadSafeBuffer() { + pthread_mutex_lock(&m_tMutex); + m_mapStreamOrder.insert(std::make_pair(pthread_self(), m_vecStreams.size())); + m_vecStreams.push_back(new std::stringstream); + pthread_mutex_unlock(&m_tMutex); + } +#endif + + inline CARGoSLog& operator<<(std::ostream& (*c_stream)(std::ostream&)) { +#ifdef ARGOS_THREADSAFE_LOG + *(m_vecStreams[m_mapStreamOrder.find(pthread_self())->second]) << c_stream; +#else + m_cStream << c_stream; +#endif + return *this; + } + + inline CARGoSLog& operator<<(const std::_Setw& t_msg) { +#ifdef ARGOS_THREADSAFE_LOG + *(m_vecStreams[m_mapStreamOrder.find(pthread_self())->second]) << t_msg; +#else + m_cStream << t_msg; +#endif + return *this; + } + + inline CARGoSLog& operator<<(const std::_Setiosflags& t_msg) { +#ifdef ARGOS_THREADSAFE_LOG + *(m_vecStreams[m_mapStreamOrder.find(pthread_self())->second]) << t_msg; +#else + m_cStream << t_msg; +#endif + return *this; + } + + template CARGoSLog& operator<<(const T t_msg) { + if(m_bColoredOutput) { +#ifdef ARGOS_THREADSAFE_LOG + *(m_vecStreams[m_mapStreamOrder.find(pthread_self())->second]) << m_sLogColor << t_msg << reset; +#else + m_cStream << m_sLogColor << t_msg << reset; +#endif + } + else { +#ifdef ARGOS_THREADSAFE_LOG + *(m_vecStreams[m_mapStreamOrder.find(pthread_self())->second]) << t_msg; +#else + m_cStream << m_sLogColor << t_msg << reset; +#endif + } + return *this; + } + + }; + + extern CARGoSLog LOG; + extern CARGoSLog LOGERR; + +} + +#define RLOG LOG << "[" << GetRobot().GetRobotId() << "] " +#define RLOGERR LOGERR << "[" << GetRobot().GetRobotId() << "] " + +#endif diff --git a/src/core/utility/math/angles.cpp b/src/core/utility/math/angles.cpp new file mode 100644 index 00000000..80091449 --- /dev/null +++ b/src/core/utility/math/angles.cpp @@ -0,0 +1,34 @@ +/** + * @file argos3/core/utility/math/angles.cpp + * + * @brief Contains definitions for angle management, such as the CRadians and CDegrees classes. + * + * Contains definitions for angle management, such as the CRadians and CDegrees classes, as well as + * trigonometric and conversion functions. + * + * @author Carlo Pinciroli + */ + +#include "angles.h" + +namespace argos { + + const CRadians CRadians::PI(ARGOS_PI); + const CRadians CRadians::TWO_PI(2.0f * CRadians::PI); + const CRadians CRadians::PI_OVER_TWO(CRadians::PI / 2.0f); + const CRadians CRadians::PI_OVER_THREE(CRadians::PI / 3.0f); + const CRadians CRadians::PI_OVER_FOUR(CRadians::PI / 4.0f); + const CRadians CRadians::PI_OVER_SIX(CRadians::PI / 6.0f); + const CRadians CRadians::ZERO; + + const CRange CRadians::SIGNED_RANGE(-CRadians::PI, CRadians::PI); + const CRange CRadians::UNSIGNED_RANGE(CRadians(), CRadians::TWO_PI); + const Real CRadians::RADIANS_TO_DEGREES(180.0/CRadians::PI.GetValue()); + const CRange CRadians::ASEBA_RANGE(-32768, 32767); + + const CRange CDegrees::SIGNED_RANGE(CDegrees(-180.0), CDegrees(180.0)); + const CRange CDegrees::UNSIGNED_RANGE(CDegrees(0.0), CDegrees(360.0)); + const Real CDegrees::DEGREES_TO_RADIANS(CRadians::PI.GetValue()/180.0); + const CRange CDegrees::ASEBA_RANGE(-32768, 32767); + +} diff --git a/src/core/utility/math/angles.h b/src/core/utility/math/angles.h new file mode 100644 index 00000000..c28f3129 --- /dev/null +++ b/src/core/utility/math/angles.h @@ -0,0 +1,627 @@ +/** + * @file argos3/core/utility/math/angles.h + * @author Carlo Pinciroli + * + * @brief Contains definitions for angle management, such as the CRadians and CDegrees classes. + * + * Use ARGOS_PI when initializing static CRadians or CDegrees variables. + * If instead you use the CRadians::PI constants (or similar) you may + * incur in the 'static initialization order fiasco'. + * + * For normal usage (main() code and all the class methods) you have to + * use the normal CRadians::PI variable and its siblings. + */ + +#ifndef ANGLES_H +#define ANGLES_H + +namespace argos { + class CRadians; + class CDegrees; +} + +#include +#include +#include +#include + +/** + * @brief To be used when initializing static variables + */ +#define ARGOS_PI 3.14159265358979323846264338327950288 + +namespace argos { + + /****************************************/ + /****************************************/ + + /** + * It defines the basic type CRadians, used to store an angle value in radians. + */ + class CRadians { + + public: + + /** + * @brief The PI constant + */ + static const CRadians PI; + + /** + * @brief Set to PI * 2 + */ + static const CRadians TWO_PI; + + /** + * @brief Set to PI / 2 + */ + static const CRadians PI_OVER_TWO; + + /** + * @brief Set to PI / 3 + */ + static const CRadians PI_OVER_THREE; + + /** + * @brief Set to PI / 4 + */ + static const CRadians PI_OVER_FOUR; + + /** + * @brief Set to PI / 6 + */ + static const CRadians PI_OVER_SIX; + + /** + * @brief Set to zero radians + */ + static const CRadians ZERO; + + /** + * @brief Class constructor + * + * It initializes m_fValue to 0 radians. + */ + CRadians() : + m_fValue(0.0) { + } + + /** + * @brief Class constructor + * + * It initializes m_fValue to the passed value. + * + * @param f_value the wanted value in radians + */ + explicit CRadians(Real f_value) : + m_fValue(f_value) { + } + + /** + * @brief Sets the value from a value in degrees + * + * It sets m_fValue (which is in radians) converting from the passed value in degrees. + * + * @param f_value a value in degrees + */ + inline void FromValueInDegrees(Real f_value) { + m_fValue = f_value / RADIANS_TO_DEGREES; + } + + /** + * @brief Sets the value from a value in Aseba format + * + * It sets m_fValue (which is in radians) converting from the passed value in Aseba format. + * + * @param f_value a value in Aseba format + */ + inline void FromValueInAseba(SInt16 n_value) { + ASEBA_RANGE.MapValueIntoRange(*this, n_value, SIGNED_RANGE); + } + + /** + * @brief Returns the value in radians + * + * @return the value in radians + */ + inline Real GetValue() const { + return m_fValue; + } + + /** + * @brief Returns the absolute value in radians + * + * @return the absolute value in radians + */ + inline Real GetAbsoluteValue() const { + return Abs(m_fValue); + } + + /** + * @brief Sets the value in radians + * + * @param f_value the wanted value in radians + */ + inline void SetValue(Real f_value) { + m_fValue = f_value; + } + + /** + * @brief Normalizes the value in the range [-PI:PI] + * + * @return A reference to the current object + * @see CRadians::PI + * @see CRange + */ + inline CRadians& SignedNormalize() { + SIGNED_RANGE.WrapValue(*this); + return *this; + } + + /** + * @brief Normalizes the value in the range [0:TWO_PI] + * + * @return A reference to the current object + * @see CRadians::TWO_PI + * @see CRange + */ + inline CRadians& UnsignedNormalize() { + UNSIGNED_RANGE.WrapValue(*this); + return *this; + } + + inline CRadians& Negate() { + m_fValue = -m_fValue; + return *this; + } + + inline CRadians& operator+() { + return *this; + } + + inline CRadians operator-() const { + return CRadians(-m_fValue); + } + + inline CRadians& operator+=(const CRadians& c_radians) { + m_fValue += c_radians.m_fValue; + return *this; + } + + inline CRadians& operator-=(const CRadians& c_radians) { + m_fValue -= c_radians.m_fValue; + return *this; + } + + inline CRadians& operator*=(Real f_value) { + m_fValue *= f_value; + return *this; + } + + inline CRadians& operator/=(Real f_value) { + m_fValue /= f_value; + return *this; + } + + inline CRadians operator+(const CRadians& c_radians) const { + CRadians cResult(*this); + cResult += c_radians; + return cResult; + } + + inline CRadians operator-(const CRadians& c_radians) const { + CRadians cResult(*this); + cResult -= c_radians; + return cResult; + } + + inline CRadians operator*(Real f_value) const { + CRadians cResult(*this); + cResult *= f_value; + return cResult; + } + + inline friend CRadians operator*(Real f_value, + const CRadians& c_radians) { + CRadians cResult(c_radians); + cResult *= f_value; + return cResult; + } + + inline Real operator/(const CRadians& c_radians) const { + return m_fValue / c_radians.m_fValue; + } + + inline CRadians operator/(Real f_value) const { + CRadians cResult(*this); + cResult /= f_value; + return cResult; + } + + inline bool operator<(const CRadians& c_radians) const { + return m_fValue < c_radians.m_fValue; + } + + inline bool operator<=(const CRadians& c_radians) const { + return m_fValue <= c_radians.m_fValue; + } + + inline bool operator>(const CRadians& c_radians) const { + return m_fValue > c_radians.m_fValue; + } + + inline bool operator>=(const CRadians& c_radians) const { + return m_fValue >= c_radians.m_fValue; + } + + inline bool operator==(const CRadians& c_radians) const { + return m_fValue == c_radians.m_fValue; + } + + inline bool operator!=(const CRadians& c_radians) const { + return m_fValue != c_radians.m_fValue; + } + + /** + * @brief Converts this object to CDegrees + * + * @return the conversion of m_fValue into CDegrees + */ + friend CDegrees ToDegrees(const CRadians& c_radians); + + inline friend std::ostream& operator<<(std::ostream& c_os, + const CRadians& c_radians) { + c_os << "CRadians(" + << c_radians.m_fValue + << " -> " + << c_radians.m_fValue * RADIANS_TO_DEGREES + << " degrees" + << ")"; + return c_os; + } + + inline friend std::istream& operator>>(std::istream& is, + CRadians& c_radians) { + is >> c_radians.m_fValue; + return is; + } + + public: + + static const CRange SIGNED_RANGE; /**< The signed normalization range [-PI:PI] */ + static const CRange UNSIGNED_RANGE; /**< The unsigned normalization range [0:TWO_PI] */ + static const CRange ASEBA_RANGE; /**< The Aseba normalization range [-32768:32767] */ + static const Real RADIANS_TO_DEGREES; /**< Constant to convert from radians to degrees */ + + private: + + Real m_fValue; /**< Actual angle value in radians */ + }; + + /****************************************/ + /****************************************/ + + /** + * It defines the basic type CDegrees, used to store an angle value in degrees. + */ + class CDegrees { + + public: + + /** + * @brief Class constructor + * + * It initializes m_fValue to 0 degrees. + */ + CDegrees() : + m_fValue(0.0) { + } + + /** + * @brief Class constructor + * + * It initializes m_fValue to the passed value. + * + * @param f_value the wanted value in degrees + */ + explicit CDegrees(Real f_value) : + m_fValue(f_value) { + } + + /** + * @brief Sets the value from a value in radians + * + * It sets m_fValue (which is in degrees) converting from the passed value in radians. + * + * @param f_value a value in radians + */ + inline void FromValueInRadians(Real f_value) { + m_fValue = f_value / DEGREES_TO_RADIANS; + } + + /** + * @brief Sets the value from a value in Aseba format + * + * It sets m_fValue (which is in degrees) converting from the passed value in Aseba format. + * + * @param f_value a value in Aseba format + */ + inline void FromValueInAseba(SInt16 n_value) { + ASEBA_RANGE.MapValueIntoRange(*this, n_value, SIGNED_RANGE); + } + + /** + * @brief Returns the value in degrees + * + * @return the value in degrees + */ + inline Real GetValue() const { + return m_fValue; + } + + /** + * @brief Sets the value in degrees + * + * @param f_value the wanted value in degrees + */ + inline void SetValue(Real f_value) { + m_fValue = f_value; + } + + /** + * @brief Normalizes the value in the range [-180:180] + * + * @return A reference to the current object + * @see CRange + */ + CDegrees& SignedNormalize() { + SIGNED_RANGE.WrapValue(*this); + return (*this); + } + + /** + * @brief Normalizes the value in the range [0:360] + * + * @return A reference to the current object + * @see CRange + */ + CDegrees& UnsignedNormalize() { + UNSIGNED_RANGE.WrapValue(*this); + return (*this); + } + + inline CDegrees& operator+() { + return *this; + } + + inline CDegrees operator-() const { + return CDegrees(-m_fValue); + } + + inline CDegrees& operator+=(const CDegrees& c_degrees) { + m_fValue += c_degrees.m_fValue; + return *this; + } + + inline CDegrees& operator-=(const CDegrees& c_degrees) { + m_fValue -= c_degrees.m_fValue; + return *this; + } + + inline CDegrees& operator*=(Real f_value) { + m_fValue *= f_value; + return *this; + } + + inline CDegrees& operator/=(Real f_value) { + m_fValue /= f_value; + return *this; + } + + inline CDegrees operator+(const CDegrees& c_degrees) const { + CDegrees cResult(*this); + cResult += c_degrees; + return cResult; + } + + inline CDegrees operator-(const CDegrees& c_degrees) const { + CDegrees cResult(*this); + cResult -= c_degrees; + return cResult; + } + + inline CDegrees operator*(Real f_value) const { + CDegrees cResult(*this); + cResult *= f_value; + return cResult; + } + + inline friend CDegrees operator*(Real f_value, + const CDegrees& c_degrees) { + CDegrees cResult(c_degrees); + cResult *= f_value; + return cResult; + } + + inline Real operator/(const CDegrees& c_degrees) const { + return m_fValue / c_degrees.m_fValue; + } + + inline CDegrees operator/(Real f_value) const { + CDegrees cResult(*this); + cResult /= f_value; + return cResult; + } + + inline bool operator<(const CDegrees& c_degrees) const { + return m_fValue < c_degrees.m_fValue; + } + + inline bool operator<=(const CDegrees& c_degrees) const { + return m_fValue <= c_degrees.m_fValue; + } + + inline bool operator>(const CDegrees& c_degrees) const { + return m_fValue > c_degrees.m_fValue; + } + + inline bool operator>=(const CDegrees& c_degrees) const { + return m_fValue >= c_degrees.m_fValue; + } + + inline bool operator==(const CDegrees& c_degrees) const { + return m_fValue == c_degrees.m_fValue; + } + + inline bool operator!=(const CDegrees& c_degrees) const { + return m_fValue != c_degrees.m_fValue; + } + + /** + * @brief Converts this object to CRadians + * + * @return the conversion of m_fValue into CRadians + */ + friend CRadians ToRadians(const CDegrees& c_degrees); + + inline friend std::ostream& operator<<(std::ostream& c_os, + const CDegrees& c_degrees) { + c_os << "CDegrees(" + << c_degrees.m_fValue + << ")"; + return c_os; + } + + inline friend std::istream& operator>>(std::istream& is, + CDegrees& c_degrees) { + is >> c_degrees.m_fValue; + return is; + } + + private: + + Real m_fValue; /**< Actual angle value in radians */ + static const CRange SIGNED_RANGE; /**< The signed normalization range [-180:180] */ + static const CRange UNSIGNED_RANGE; /**< The unsigned normalization range [0:360] */ + static const CRange ASEBA_RANGE; /**< The Aseba normalization range [-32768:32767] */ + static const Real DEGREES_TO_RADIANS; /**< Constant to convert from degrees to radians */ + + }; + + /****************************************/ + /****************************************/ + + /** + * @brief Converts CRadians to CDegrees + * + * @param c_radians the object to convert + * @return the converted CDegrees object + */ + inline CDegrees ToDegrees(const CRadians& c_radians) { + return CDegrees(c_radians.m_fValue * CRadians::RADIANS_TO_DEGREES); + } + + /** + * @brief Converts CDegrees to CRadians + * + * @param c_degrees the object to convert + * @return the converted CRadians object + */ + inline CRadians ToRadians(const CDegrees& c_degrees) { + return CRadians(c_degrees.m_fValue * CDegrees::DEGREES_TO_RADIANS); + } + + /****************************************/ + /****************************************/ + +#ifdef ARGOS_DOUBLE_PRECISION +# define ARGOS_SIN ::sin +# define ARGOS_ASIN ::asin +# define ARGOS_COS ::cos +# define ARGOS_TAN ::tan +# define ARGOS_ACOS ::acos +# define ARGOS_ATAN2 ::atan2 +#else +# define ARGOS_SIN ::sinf +# define ARGOS_ASIN ::asinf +# define ARGOS_COS ::cosf +# define ARGOS_TAN ::tanf +# define ARGOS_ACOS ::acosf +# define ARGOS_ATAN2 ::atan2f +#endif + + /** + * @brief Computes the sine of the passed value in radians + * + * @param c_radians the angle in CRadians + * + * @return the sine of the passed value + */ + inline Real Sin(const CRadians& c_radians) { + return ARGOS_SIN(c_radians.GetValue()); + } + + /** + * @brief Computes the cosine of the passed value in radians + * + * @param c_radians the angle in CRadians + * + * @return the cosine of the passed value + */ + inline Real Cos(const CRadians& c_radians) { + return ARGOS_COS(c_radians.GetValue()); + } + + /** + * @brief Computes the tangent of the passed value in radians + * + * @param c_radians the angle in CRadians + * + * @return the tangent of the passed value + */ + inline Real Tan(const CRadians& c_radians) { + return ARGOS_TAN(c_radians.GetValue()); + } + + /** + * @brief Computes the arcsine of the passed value + * + * @param f_value a value in the range [-1:1] + * + * @return the arcsine in CRadians + */ + inline CRadians ASin(Real f_value) { + return CRadians(ARGOS_ASIN(f_value)); + } + + /** + * @brief Computes the arccosine of the passed value + * + * @param f_value a value in the range [-1:1] + * + * @return the arccosine in CRadians + */ + inline CRadians ACos(Real f_value) { + return CRadians(ARGOS_ACOS(f_value)); + } + + /** + * @brief Computes the arctangent of the passed values. + * + * It internally uses the standard function ::atan2(y,x). + * + * @param f_y the extent on the y axis + * @param f_x the extent on the x axis + * + * @return the arctangent in CRadians + */ + inline CRadians ATan2(const Real f_y, const Real f_x) { + return CRadians(ARGOS_ATAN2(f_y, f_x)); + } + + /****************************************/ + /****************************************/ + +} + +#endif diff --git a/src/core/utility/math/general.h b/src/core/utility/math/general.h new file mode 100644 index 00000000..76ba011d --- /dev/null +++ b/src/core/utility/math/general.h @@ -0,0 +1,220 @@ +/** + * @file argos3/core/utility/math/general.h + * + * @brief Contains the definition of some basic math functions. + * + * @author Carlo Pinciroli + */ + +#ifndef GENERAL_H +#define GENERAL_H + +#include +#include +#include + + +namespace argos { + /****************************************/ + /****************************************/ + + /** + * Returns the absolute value of the passed argument. + * @return the absolute value of the passed argument. + */ + template T Abs(const T& t_v) { + if (t_v > T(0)) return t_v; + if (t_v < T(0)) return -t_v; + return T(0); + } + + /** + * Returns the absolute value of the passed argument. + * Specific implementation for the SInt32 type. + * @return the absolute value of the passed argument. + * @see Abs() + */ + inline SInt32 Abs(SInt32 t_v) { + if (t_v > 0) return t_v; + if (t_v < 0) return -t_v; + return 0; + } + + /** + * Returns the absolute value of the passed argument. + * Specific implementation for the Real type. + * @return the absolute value of the passed argument. + */ + inline Real Abs(Real t_v) { + if (t_v > 0.0f) return t_v; + if (t_v < 0.0f) return -t_v; + return 0.0f; + } + + /****************************************/ + /****************************************/ + +#ifdef ARGOS_DOUBLE_PRECISION +#define Log ::log +#define Sqrt ::sqrt +#else +#define Log ::logf +#define Sqrt ::sqrtf +#endif + + /****************************************/ + /****************************************/ + + /** + * Returns the smaller of the two passed arguments. + * This version accepts const references as arguments. + * @return the smaller of the two passed arguments. + */ + template T Min(const T& t_v1, const T& t_v2) { + return t_v1 < t_v2 ? t_v1 : t_v2; + } + + /** + * Returns the smaller of the two passed arguments. + * This version accepts non-const references as arguments. + * @return the smaller of the two passed arguments. + */ + template T& Min(T& t_v1, T& t_v2) { + return t_v1 < t_v2 ? t_v1 : t_v2; + } + + /** + * Returns the bigger of the two passed arguments. + * This version accepts const references as arguments. + * @return the bigger of the two passed arguments. + */ + template T Max(const T& t_v1, const T& t_v2) { + return t_v1 > t_v2 ? t_v1 : t_v2; + } + + /** + * Returns the bigger of the two passed arguments. + * This version accepts non-const references as arguments. + * @return the bigger of the two passed arguments. + */ + template T& Max(T& t_v1, T& t_v2) { + return t_v1 > t_v2 ? t_v1 : t_v2; + } + + /****************************************/ + /****************************************/ + + /** + * Returns the sign of the value of the passed argument. + * @return -1 if the value is negative, +1 if it is positive, or 0 if the value is 0. + */ + template SInt32 Sign(const T& t_v) { + if (t_v > T(0)) return 1; + if (t_v < T(0)) return -1; + return 0; + } + + /****************************************/ + /****************************************/ + + /** + * Returns the square of the value of the passed argument. + * @return the square of the value of the passed argument. + */ + template T Square(const T& t_v) { + return t_v * t_v; + } + + /****************************************/ + /****************************************/ + + /** + * Rounds the passed floating-point value to the closest lower integer. + * Profiled, it takes 50% the time of ::floor(). + * @return the closest lower integer to the passed value. + */ + inline SInt32 Floor(Real f_value) { + SInt32 nI = static_cast (f_value); + if (f_value >= 0.0f) return nI; + return nI - 1; + } + + /** + * Rounds the passed floating-point value to the closest higher integer. + * Profiled, it takes 50% the time of ::ceil(). + * @return the closest higher integer to the passed value. + */ + inline SInt32 Ceil(Real f_value) { + SInt32 nI = static_cast (f_value); + if (nI < f_value) return nI + 1; + return nI; + } + + /** + * Rounds the passed floating-point value to the integer closest to zero. + * If the passed value is >0, Floor() is called. Otherwise, Ceil() is called. + * @return the integer closest to zero. + */ + inline SInt32 RoundClosestToZero(Real f_value) { + if (f_value > 0.0f) return Floor(f_value); + return Ceil(f_value); + } + + /****************************************/ + /****************************************/ + + /** + * Tests whether a floating-point value is lower than another. + * This function perform the following test: + *
+     *           Abs(f_value1 - f_value2)
+     * --------------------------------------------- <= f_epsilon
+     * Max (1.0f, Max(Abs(f_value1), Abs(f_value2)))
+     * 
+ * @param f_value1 the first value to test + * @param f_value2 the second value to test + * @param f_epsilon the tolerance of the test + * @return true if f_value1 < f_value2, false otherwise. + * @see DoubleEq() + */ + inline bool DoubleEqAbsolute(Real f_value1, Real f_value2, Real f_epsilon) { + return Abs (f_value1 - f_value2) <= f_epsilon * Max (1.0f, Max (Abs (f_value1), Abs (f_value2))); + } + + /** + * Tests whether a floating-point value is lower than another. + * This method is like DoubleEqAbsolute(), but with f_epsilon = 0.0001. + * @param f_value1 the first value to test + * @param f_value2 the second value to test + * @return true if f_value1 < f_value2, false otherwise. + * @see DoubleEqAbsolute() + */ + inline bool DoubleEq(Real f_value1, Real f_value2) { + return Abs (f_value1 - f_value2) <= 0.0001f * Max (1.0f, Max (Abs (f_value1), Abs (f_value2))); + } + + /****************************************/ + /****************************************/ + + /** + * Return the value of the linear interpolation + * @param f_x the value of x that you want to interpolate + * @param t_points vector of points NOTE: the vector has to be ordered in INCREASING ORDER. + * @return value of y. + */ + inline Real Interpolate(Real f_x, const std::vector >& c_points) { + std::pair cP0 = c_points.at(0); + std::pair cP1; + for (UInt32 i = 1; i < c_points.size(); ++i) { + cP1 = c_points.at(i); + if (cP1.first >= f_x) { + break; + } else if (i < c_points.size() - 1) { + cP0 = cP1; + } + } + return (f_x * (cP0.second - cP1.second)) / (cP0.first - cP1.first)+(-cP1.first * cP0.second + cP0.first * cP1.second) / (cP0.first - cP1.first);; + } +} + +#endif diff --git a/src/core/utility/math/matrix.h b/src/core/utility/math/matrix.h new file mode 100644 index 00000000..a3899c41 --- /dev/null +++ b/src/core/utility/math/matrix.h @@ -0,0 +1,213 @@ +/** + * @file argos3/core/utility/math/matrix.h + * + * @brief Contains the definition of a MxN templated matrix + * + * @author Michael Allwright + */ + +#ifndef MATRIX_H +#define MATRIX_H + +#include +#include +#include +#include + +namespace argos { + + template + class CMatrix { + + /* Matrices of different dimensions can access each others data */ + template friend class CMatrix; + + public: + CMatrix() { + for(UInt32 i = 0; i < ROWS * COLS; i++) + m_fValues[i] = 0; + } + + CMatrix(const Real* f_values) { + Set(f_values); + } + + CMatrix(const CMatrix& c_matrix) { + Set(c_matrix.m_fValues); + } + + CMatrix& operator=(const CMatrix& c_matrix) { + if(this != &c_matrix) { + Set(c_matrix.m_fValues); + } + return *this; + } + + inline Real& operator()(UInt32 un_row, + UInt32 un_col) { + ARGOS_ASSERT(un_row < ROWS && un_col < COLS, + "Matrix index out of bounds: un_row = " << + un_row << + ", un_col = " << + un_col); + return m_fValues[un_row * COLS + un_col]; + } + + inline Real operator()(UInt32 un_row, + UInt32 un_col) const { + ARGOS_ASSERT(un_row < ROWS && un_col < COLS, + "Matrix index out of bounds: un_row = " << + un_row << + ", un_col = " << + un_col); + return m_fValues[un_row * COLS + un_col]; + } + + inline Real operator()(UInt32 un_idx) const { + ARGOS_ASSERT(un_idx < ROWS * COLS, + "Matrix index out of bounds: un_idx = " << + un_idx); + return m_fValues[un_idx]; + } + + inline Real& operator()(UInt32 un_idx) { + ARGOS_ASSERT(un_idx < ROWS * COLS, + "Matrix index out of bounds: un_idx = " << + un_idx); + return m_fValues[un_idx]; + } + + void Set(const Real* f_values) { + for(UInt32 i = 0; i < ROWS * COLS; i++) + m_fValues[i] = f_values[i]; + } + + CMatrix GetTransposed() { + Real fNewValues[COLS * ROWS]; + for(UInt32 i = 0; i < ROWS; i++) + for(UInt32 j = 0; j < COLS; j++) + fNewValues[j * ROWS + i] = m_fValues[i * COLS + j]; + + return CMatrix(fNewValues); + } + + template + CMatrix& GetSubmatrix(CMatrix& c_matrix, + UInt32 un_offset_row, + UInt32 un_offset_col) { + ARGOS_ASSERT((SMROWS - 1 + un_offset_row) < ROWS && + (SMCOLS - 1 + un_offset_col) < COLS, + "Submatrix range is out of bounds: cannot extract a " << + SMROWS << "x" << SMCOLS << " submatrix from a " << + ROWS << "x" << COLS << " matrix with offsets " << + " un_offset_row = " << + un_offset_row << + ", un_offset_col = " << + un_offset_col); + + for(UInt32 i = 0; i < SMROWS; i++) + for(UInt32 j = 0; j < SMCOLS; j++) + c_matrix.m_fValues[i * SMCOLS + j] = m_fValues[(i + un_offset_row) * COLS + (j + un_offset_col)]; + + return c_matrix; + } + + bool operator==(const CMatrix& c_matrix) const { + for(UInt32 i = 0; i < ROWS * COLS; i++) { + if(m_fValues[i] != c_matrix.m_fValues[i]) + return false; + } + return true; + } + + CMatrix& operator+=(const CMatrix& c_matrix) { + for(UInt32 i = 0; i < ROWS * COLS; i++) { + m_fValues[i] += c_matrix.m_fValues[i]; + } + return *this; + } + + CMatrix& operator-=(const CMatrix& c_matrix) { + for(UInt32 i = 0; i < ROWS * COLS; i++) { + m_fValues[i] -= c_matrix.m_fValues[i]; + } + return *this; + } + + CMatrix& operator*=(Real f_scale) { + for(UInt32 i = 0; i < ROWS * COLS; i++) { + m_fValues[i] *= f_scale; + } + return *this; + } + + CMatrix operator+(const CMatrix& c_matrix) const { + CMatrix cResult = (*this); + cResult += c_matrix; + return cResult; + } + + CMatrix operator-(const CMatrix& c_matrix) const { + CMatrix cResult = (*this); + cResult -= c_matrix; + return cResult; + } + + CMatrix& operator*=(const CMatrix& c_matrix) { + Real fNewValues[ROWS * COLS]; + for(UInt32 i = 0; i < ROWS; i++) { + for(UInt32 j = 0; j < COLS; j++) { + fNewValues[i * COLS + j] = 0.0f; + for(UInt32 k = 0; k < COLS; k++) { + fNewValues[i * COLS + j] += m_fValues[i * COLS + k] * c_matrix.m_fValues[k * COLS + j]; + } + } + } + Set(fNewValues); + return *this; + } + + template + CMatrix operator*(const CMatrix& c_matrix) const { + Real fNewValues[ROWS * OTRCOLS]; + for(UInt32 i = 0; i < ROWS; i++) { + for(UInt32 j = 0; j < OTRCOLS; j++) { + fNewValues[i * OTRCOLS + j] = 0.0f; + for(UInt32 k = 0; k < COLS; k++) { + fNewValues[i * OTRCOLS + j] += m_fValues[i * COLS + k] * c_matrix.m_fValues[k * OTRCOLS + j]; + } + } + } + return CMatrix(fNewValues); + } + + friend std::ostream& operator<<(std::ostream& c_os, + const CMatrix c_matrix) { + + std::ios_base::fmtflags unInitalFlags = c_os.flags(); + std::streamsize nInitalPrec = c_os.precision(); + + c_os.setf(std::ios::fixed); + c_os.precision(1); + + for(UInt32 i = 0; i < ROWS; i++) { + c_os << "| "; + for(UInt32 j = 0; j < COLS; j++) { + c_os << std::setw(6) << c_matrix(i, j) << " "; + } + c_os << "|" << std::endl; + } + + c_os.flags(unInitalFlags); + c_os.precision(nInitalPrec); + return c_os; + } + + protected: + + Real m_fValues[ROWS * COLS]; + + }; +} + +#endif diff --git a/src/core/utility/math/quaternion.h b/src/core/utility/math/quaternion.h new file mode 100644 index 00000000..351d36d6 --- /dev/null +++ b/src/core/utility/math/quaternion.h @@ -0,0 +1,279 @@ +#ifndef CQUATERNION_H +#define CQUATERNION_H + +#include + +namespace argos { + + class CQuaternion { + + public: + CQuaternion() { + m_fValues[0] = 1.0; + m_fValues[1] = 0.0; + m_fValues[2] = 0.0; + m_fValues[3] = 0.0; + } + + CQuaternion(const CQuaternion& c_quaternion) { + *this = c_quaternion; + } + + CQuaternion(Real f_real, + Real f_img1, + Real f_img2, + Real f_img3) { + m_fValues[0] = f_real; + m_fValues[1] = f_img1; + m_fValues[2] = f_img2; + m_fValues[3] = f_img3; + } + + CQuaternion(const CRadians& c_radians, + const CVector3& c_vector3) { + FromAngleAxis(c_radians, c_vector3); + } + + inline CQuaternion(const CVector3& c_vector1, + const CVector3& c_vector2) { + BetweenTwoVectors(c_vector1, c_vector2); + } + + inline Real GetW() const { + return m_fValues[0]; + } + + inline Real GetX() const { + return m_fValues[1]; + } + + inline Real GetY() const { + return m_fValues[2]; + } + + inline Real GetZ() const { + return m_fValues[3]; + } + + inline CQuaternion Conjugate() const { + return CQuaternion(m_fValues[0], + -m_fValues[1], + -m_fValues[2], + -m_fValues[3]); + } + + inline CQuaternion Inverse() const { + return CQuaternion(m_fValues[0], + -m_fValues[1], + -m_fValues[2], + -m_fValues[3]); + } + + inline Real Length() const { + return ::sqrt(SquareLength()); + } + + inline Real SquareLength() const { + return + Square(m_fValues[0]) + + Square(m_fValues[1]) + + Square(m_fValues[2]) + + Square(m_fValues[3]); + } + + inline CQuaternion& Normalize() { + Real fInvLength = 1.0 / Length(); + m_fValues[0] *= fInvLength; + m_fValues[1] *= fInvLength; + m_fValues[2] *= fInvLength; + m_fValues[3] *= fInvLength; + return *this; + } + + inline CQuaternion& FromAngleAxis(const CRadians& c_angle, + const CVector3& c_vector) { + CRadians fHalfAngle = c_angle * 0.5; + Real fSin = Sin(fHalfAngle); + m_fValues[0] = Cos(fHalfAngle); + m_fValues[1] = c_vector.GetX() * fSin; + m_fValues[2] = c_vector.GetY() * fSin; + m_fValues[3] = c_vector.GetZ() * fSin; + return *this; + } + + inline void ToAngleAxis(CRadians& c_angle, + CVector3& c_vector) const { + Real fSquareLength = + Square(m_fValues[1]) + + Square(m_fValues[2]) + + Square(m_fValues[3]); + + if(fSquareLength > 0.0f) { + c_angle = 2.0f * ACos(m_fValues[0]); + Real fInvLength = 1.0f / ::sqrt(fSquareLength); + c_vector.Set(m_fValues[1] * fInvLength, + m_fValues[2] * fInvLength, + m_fValues[3] * fInvLength); + } + else { + /* By default, to ease the support of robot orientation, no rotation refers to the Z axis */ + c_angle = CRadians::ZERO; + c_vector = CVector3::Z; + } + } + + inline CQuaternion& FromEulerAngles(const CRadians& c_z_angle, + const CRadians& c_y_angle, + const CRadians& c_x_angle) { + (*this) = CQuaternion(c_x_angle, CVector3::X) * + CQuaternion(c_y_angle, CVector3::Y) * + CQuaternion(c_z_angle, CVector3::Z); + return (*this); + } + + inline void ToEulerAngles(CRadians& c_z_angle, + CRadians& c_y_angle, + CRadians& c_x_angle) const { + /* With the ZYX convention, gimbal lock happens when + cos(y_angle) = 0, that is when y_angle = +- pi/2 + In this condition, the Z and X axis overlap and we + lose one degree of freedom. It's a problem of the + Euler representation of rotations that is not + present when we deal with quaternions. + For reasons of speed, we consider gimbal lock + happened when fTest > 0.499 and when fTest < -0.499. + */ + /* Computed to understand if we have gimbal lock or not */ + Real fTest = + m_fValues[1] * m_fValues[3] + + m_fValues[0] * m_fValues[2]; + + if(fTest > 0.499f) { + /* Gimbal lock */ + c_x_angle = CRadians::ZERO; + c_y_angle = CRadians::PI_OVER_TWO; + c_z_angle = ATan2(2.0f * (m_fValues[1] * m_fValues[2] + m_fValues[0] * m_fValues[3]), + 1.0f - 2.0f * (m_fValues[1] * m_fValues[1] + m_fValues[3] * m_fValues[3])); + } + else if(fTest < -0.499f) { + /* Gimbal lock */ + c_x_angle = CRadians::ZERO; + c_y_angle = -CRadians::PI_OVER_TWO; + c_z_angle = ATan2(2.0f * (m_fValues[1] * m_fValues[2] + m_fValues[0] * m_fValues[3]), + 1.0f - 2.0f * (m_fValues[1] * m_fValues[1] + m_fValues[3] * m_fValues[3])); + } + else { + /* Normal case */ + Real fSqValues[4] = { + Square(m_fValues[0]), + Square(m_fValues[1]), + Square(m_fValues[2]), + Square(m_fValues[3]) + }; + + c_x_angle = ATan2(2.0 * (m_fValues[0] * m_fValues[1] - m_fValues[2] * m_fValues[3]), + fSqValues[0] - fSqValues[1] - fSqValues[2] + fSqValues[3]); + c_y_angle = ASin(2.0 * (m_fValues[1] * m_fValues[3] + m_fValues[0] * m_fValues[2])); + c_z_angle = ATan2(2.0 * (m_fValues[0] * m_fValues[3] - m_fValues[1] * m_fValues[2]), + fSqValues[0] + fSqValues[1] - fSqValues[2] - fSqValues[3]); + } + } + + inline CQuaternion& BetweenTwoVectors(const CVector3& c_vector1, + const CVector3& c_vector2) { + m_fValues[0] = ::sqrt(c_vector1.SquareLength() * c_vector2.SquareLength()) + c_vector1.DotProduct(c_vector2); + CVector3 cCrossProd(c_vector1); + cCrossProd.CrossProduct(c_vector2); + m_fValues[1] = cCrossProd.GetX(); + m_fValues[2] = cCrossProd.GetY(); + m_fValues[3] = cCrossProd.GetZ(); + Normalize(); + return *this; + } + + inline CQuaternion& operator=(const CQuaternion& c_quaternion) { + if (&c_quaternion != this) { + m_fValues[0] = c_quaternion.m_fValues[0]; + m_fValues[1] = c_quaternion.m_fValues[1]; + m_fValues[2] = c_quaternion.m_fValues[2]; + m_fValues[3] = c_quaternion.m_fValues[3]; + } + return *this; + } + + inline CQuaternion& operator+=(const CQuaternion& c_quaternion) { + m_fValues[0] += c_quaternion.m_fValues[0]; + m_fValues[1] += c_quaternion.m_fValues[1]; + m_fValues[2] += c_quaternion.m_fValues[2]; + m_fValues[3] += c_quaternion.m_fValues[3]; + return *this; + } + + inline CQuaternion& operator-=(const CQuaternion& c_quaternion) { + m_fValues[0] -= c_quaternion.m_fValues[0]; + m_fValues[1] -= c_quaternion.m_fValues[1]; + m_fValues[2] -= c_quaternion.m_fValues[2]; + m_fValues[3] -= c_quaternion.m_fValues[3]; + return *this; + } + + inline CQuaternion& operator*=(const CQuaternion& c_quaternion) { + Real newv[4]; + newv[0] = m_fValues[0] * c_quaternion.m_fValues[0] - + m_fValues[1] * c_quaternion.m_fValues[1] - + m_fValues[2] * c_quaternion.m_fValues[2] - + m_fValues[3] * c_quaternion.m_fValues[3]; + newv[1] = m_fValues[0] * c_quaternion.m_fValues[1] + + m_fValues[1] * c_quaternion.m_fValues[0] + + m_fValues[2] * c_quaternion.m_fValues[3] - + m_fValues[3] * c_quaternion.m_fValues[2]; + newv[2] = m_fValues[0] * c_quaternion.m_fValues[2] - + m_fValues[1] * c_quaternion.m_fValues[3] + + m_fValues[2] * c_quaternion.m_fValues[0] + + m_fValues[3] * c_quaternion.m_fValues[1]; + newv[3] = m_fValues[0] * c_quaternion.m_fValues[3] + + m_fValues[1] * c_quaternion.m_fValues[2] - + m_fValues[2] * c_quaternion.m_fValues[1] + + m_fValues[3] * c_quaternion.m_fValues[0]; + m_fValues[0] = newv[0]; + m_fValues[1] = newv[1]; + m_fValues[2] = newv[2]; + m_fValues[3] = newv[3]; + return *this; + } + + inline CQuaternion operator+(const CQuaternion& c_quaternion) const { + CQuaternion result(*this); + result += c_quaternion; + return result; + } + + inline CQuaternion operator-(const CQuaternion& c_quaternion) const { + CQuaternion result(*this); + result -= c_quaternion; + return result; + } + + inline CQuaternion operator*(const CQuaternion& c_quaternion) const { + CQuaternion result(*this); + result *= c_quaternion; + return result; + } + + inline friend std::ostream& operator<<(std::ostream& c_os, const CQuaternion& c_quaternion) { + c_os << "CQuaternion(" + << c_quaternion.m_fValues[0] << "," + << c_quaternion.m_fValues[1] << "," + << c_quaternion.m_fValues[2] << "," + << c_quaternion.m_fValues[3] << ")"; + return c_os; + } + + private: + + Real m_fValues[4]; + }; + +} + +#endif diff --git a/src/core/utility/math/range.h b/src/core/utility/math/range.h new file mode 100644 index 00000000..45d1eb64 --- /dev/null +++ b/src/core/utility/math/range.h @@ -0,0 +1,140 @@ +#ifndef CRANGE_H +#define CRANGE_H + +#include +#include +#include +#include + +namespace argos { + + template class CRange { + + public: + + CRange(const T& t_min, + const T& t_max) : + m_tMin(t_min), + m_tMax(t_max), + m_tSpan(m_tMax - m_tMin) { + ARGOS_ASSERT(t_min < t_max, + "Error initializing CRange(" << + t_min << ", " << t_max << "): " << + t_min << " is not < " << t_max); + } + + inline T GetMin() const { + return m_tMin; + } + + inline void SetMin(const T& t_min) { + ARGOS_ASSERT(t_min < m_tMax, + "Error setting min CRange bound (" << + t_min << "): " << + t_min << " is not < " << m_tMax); + m_tMin = t_min; + /* Same as, but faster than + m_tSpan = m_tMax - m_tMin; */ + m_tSpan = m_tMax; + m_tSpan -= m_tMin; + } + + inline T GetMax() const { + return m_tMax; + } + + inline void SetMax(const T& t_max) { + ARGOS_ASSERT(m_tMin < t_max, + "Error setting max CRange bound (" << + t_max << "): " << + m_tMin << " is not < " << t_max); + m_tMax = t_max; + /* Same as, but faster than + m_tSpan = m_tMax - m_tMin; */ + m_tSpan = m_tMax; + m_tSpan -= m_tMin; + } + + inline T GetSpan() const { + return m_tSpan; + } + + inline void Set(const T& t_min, const T& t_max) { + ARGOS_ASSERT(t_min < t_max, + "Error setting CRange bounds (" << + t_min << ", " << t_max << "): " << + t_min << " is not < " << t_max); + m_tMin = t_min; + m_tMax = t_max; + /* Same as, but faster than + m_tSpan = m_tMax - m_tMin; */ + m_tSpan = m_tMax; + m_tSpan -= m_tMin; + } + + inline bool WithinMinBoundIncludedMaxBoundIncluded(const T& t_value) const { + return t_value >= m_tMin && t_value <= m_tMax; + } + + inline bool WithinMinBoundIncludedMaxBoundExcluded(const T& t_value) const { + return t_value >= m_tMin && t_value < m_tMax; + } + + inline bool WithinMinBoundExcludedMaxBoundIncluded(const T& t_value) const { + return t_value > m_tMin && t_value <= m_tMax; + } + + inline bool WithinMinBoundExcludedMaxBoundExcluded(const T& t_value) const { + return t_value > m_tMin && t_value < m_tMax; + } + + inline void TruncValue(T& t_value) const { + if (t_value > m_tMax) t_value = m_tMax; + if (t_value < m_tMin) t_value = m_tMin; + } + + inline Real NormalizeValue(const T& t_value) const { + T tTmpValue(t_value); + TruncValue(tTmpValue); + return static_cast(tTmpValue - m_tMin) / + static_cast(m_tSpan); + } + + template void MapValueIntoRange(U& t_output_value, + const T& t_input_value, + const CRange& c_range) const { + t_output_value = (NormalizeValue(t_input_value) * + c_range.GetSpan()) + c_range.GetMin(); + } + + inline void WrapValue(T& t_value) const { + while(t_value > m_tMax) t_value -= m_tSpan; + while(t_value < m_tMin) t_value += m_tSpan; + } + + inline friend std::ostream& operator<<(std::ostream& os, + const CRange& c_range) { + os << c_range.m_tMin << ":" + << c_range.m_tMax; + return os; + } + + inline friend std::istream& operator>>(std::istream& is, + CRange& c_range) { + T tValues[2]; + ParseValues (is, 2, tValues, ':'); + c_range.Set(tValues[0], tValues[1]); + return is; + } + + private: + + T m_tMin; + T m_tMax; + T m_tSpan; + + }; + +} + +#endif diff --git a/src/core/utility/math/ray.h b/src/core/utility/math/ray.h new file mode 100644 index 00000000..b8238761 --- /dev/null +++ b/src/core/utility/math/ray.h @@ -0,0 +1,131 @@ +#ifndef CRAY_H +#define CRAY_H + +namespace argos { + class CRay; +} + +#include +#include + +namespace argos { + + class CRay { + + public: + + CRay() { + } + + CRay(const CVector3& c_start, + const CVector3& c_end) : + m_cStart(c_start), m_cEnd(c_end) { + } + + CRay(const CVector3& c_start, + const CVector3& c_direction, + Real f_length) { + Set(c_start, c_direction, f_length); + } + + inline const CVector3& GetStart() const { + return m_cStart; + } + + inline const CVector3& GetEnd() const { + return m_cEnd; + } + + inline void SetStart(const CVector3& c_start) { + m_cStart = c_start; + } + + inline void SetEnd(const CVector3& c_end) { + m_cEnd = c_end; + } + + inline void Set(const CVector3& c_start, const CVector3& c_end) { + m_cStart = c_start; + m_cEnd = c_end; + } + + inline void Set(const CVector3& c_start, const CVector3& c_direction, Real f_length) { + m_cStart = c_start; + /* Same as, but faster than + m_cEnd = m_cStart + f_length * c_direction; */ + m_cEnd = m_cStart; + m_cEnd += f_length * c_direction; + } + + inline void GetDirection(CVector3& c_buffer) const { + /* Same as, but faster than + c_buffer = (m_cEnd - m_cStart).Normalize(); */ + c_buffer = m_cEnd; + c_buffer -= m_cStart; + c_buffer.Normalize(); + } + + inline void GetInverseDirection(CVector3& c_buffer) const { + /* Same as, but faster than + c_buffer = (m_cEnd - m_cStart).Normalize(); */ + c_buffer = m_cStart; + c_buffer -= m_cEnd; + c_buffer.Normalize(); + } + + inline Real GetLength() const { + return (m_cEnd - m_cStart).Length(); + } + + inline void ToVector(CVector3& c_buffer) const { + /* Same as, but faster than + c_buffer = m_cEnd - m_cStart; */ + c_buffer = m_cEnd; + c_buffer -= m_cStart; + } + + /* Returns the point on the line corresponding to f_t, + which is in the range [0:1] */ + inline void GetPoint(CVector3& c_point, + Real f_t) const { + ARGOS_ASSERT(f_t >= 0.0f && f_t <= 1.0f, + "CRay::GetPoint(): the f_t parameter must be in the range [0:1], but f_t = " << f_t); + c_point.SetX(m_cStart.GetX() + f_t * (m_cEnd.GetX() - m_cStart.GetX())); + c_point.SetY(m_cStart.GetY() + f_t * (m_cEnd.GetY() - m_cStart.GetY())); + c_point.SetZ(m_cStart.GetZ() + f_t * (m_cEnd.GetZ() - m_cStart.GetZ())); + } + + /* Returns the distance from the ray start to the point on the line corresponding to f_t, + which is in the range [0:1] */ + inline Real GetDistance(Real f_t) const { + ARGOS_ASSERT(f_t >= 0.0f && f_t <= 1.0f, + "CRay::GetDistance(): the f_t parameter must be in the range [0:1], but f_t = " << f_t); + return ::sqrt(Square(f_t * (m_cEnd.GetX() - m_cStart.GetX())) + + Square(f_t * (m_cEnd.GetY() - m_cStart.GetY())) + + Square(f_t * (m_cEnd.GetZ() - m_cStart.GetZ()))); + } + + /* Return a segment that is the projection of the ray onto the + specified plane */ + inline CSegment ProjectOntoXY() const { + return CSegment(m_cStart.ProjectOntoXY(),m_cEnd.ProjectOntoXY()); + } + + inline CSegment ProjectOntoXZ() const { + return CSegment(m_cStart.ProjectOntoXZ(),m_cEnd.ProjectOntoXZ()); + } + + inline CSegment ProjectOntoYZ() const { + return CSegment(m_cStart.ProjectOntoYZ(),m_cEnd.ProjectOntoYZ()); + } + + private: + + CVector3 m_cStart; + CVector3 m_cEnd; + + }; + +} + +#endif diff --git a/src/core/utility/math/rng.cpp b/src/core/utility/math/rng.cpp new file mode 100644 index 00000000..7ee3a803 --- /dev/null +++ b/src/core/utility/math/rng.cpp @@ -0,0 +1,612 @@ +/** + * @file argos3/core/utility/math/rng.cpp + * + * @brief This file provides the definition of + * + * @author Carlo Pinciroli - + */ + +#include "rng.h" +#include +#include +#include +#include +#include + +#ifndef CROSSCOMPILING +# include +#endif + +namespace argos { + + /****************************************/ + /****************************************/ + + std::map CRandom::m_mapCategories; + +#ifndef CROSSCOMPILING + /* Creates an array of all the available generator types, terminated by a null pointer */ + const gsl_rng_type** CRandom::m_pptRNGTypes = gsl_rng_types_setup(); +#endif + + /* Checks that a category exists. It internally creates an iterator that points to the category, if found. */ +#define CHECK_CATEGORY(category) \ + std::map::iterator itCategory = m_mapCategories.find(category); \ + if(itCategory == m_mapCategories.end()) { \ + THROW_ARGOSEXCEPTION("CRandom:: can't find category \"" << category << "\"."); \ + } + + /****************************************/ + /****************************************/ + + CRandom::CRNG::CRNG(UInt32 un_seed, + const std::string& str_type) : + m_unSeed(un_seed), + m_strType(str_type), + m_ptRNG(NULL), +#ifdef CROSSCOMPILING + m_pchRNGState(NULL), +#endif + m_pcIntegerRNGRange(NULL) { + CreateRNG(); + } + + /****************************************/ + /****************************************/ + + CRandom::CRNG::CRNG(CByteArray& c_buffer) : + m_ptRNG(NULL), +#ifdef CROSSCOMPILING + m_pchRNGState(NULL), +#endif + m_pcIntegerRNGRange(NULL) { + /* Get the seed of the generator */ + c_buffer >> m_unSeed; + /* Get the type of the generator */ + c_buffer >> m_strType; + /* Create the RNG */ + CreateRNG(); + /* Restore the state of the generator */ +#ifndef CROSSCOMPILING + size_t unStateSize = gsl_rng_size(m_ptRNG); + UInt8* pStateBuffer = new UInt8[unStateSize]; + c_buffer.FetchBuffer(pStateBuffer, unStateSize); + ::memcpy(gsl_rng_state(m_ptRNG), pStateBuffer, unStateSize); + /* Cleanup */ + delete[] pStateBuffer; +#else + c_buffer.FetchBuffer(reinterpret_cast(m_pchRNGState), 256); + setstate_r(m_pchRNGState, m_ptRNG); +#endif + } + + /****************************************/ + /****************************************/ + + CRandom::CRNG::CRNG(const CRNG& c_rng) : + m_unSeed(c_rng.m_unSeed), + m_strType(c_rng.m_strType), + m_ptRNG(NULL), +#ifdef CROSSCOMPILING + m_pchRNGState(NULL), +#endif + m_pcIntegerRNGRange(new CRange(*c_rng.m_pcIntegerRNGRange)) { + /* Clone RNG of original */ +#ifndef CROSSCOMPILING + m_ptRNG = gsl_rng_clone(c_rng.m_ptRNG); +#else + m_ptRNG = new random_data; + ::memset(m_ptRNG, 0, sizeof(random_data)); + m_pchRNGState = new char[256]; + SInt32 nError = initstate_r(m_unSeed, m_pchRNGState, 256, m_ptRNG); + if(nError != 0) { + THROW_ARGOSEXCEPTION("Unable to create random number generator (initstate_r returned " << nError << ")."); + } + ::memcpy(m_pchRNGState, c_rng.m_pchRNGState, 256); + setstate_r(m_pchRNGState, m_ptRNG); +#endif + } + + /****************************************/ + /****************************************/ + + CRandom::CRNG::~CRNG() { + DisposeRNG(); + } + + /****************************************/ + /****************************************/ + + void CRandom::CRNG::CreateRNG() { +#ifndef CROSSCOMPILING + /* Look for RNG type in the RNG type list */ + bool bFound = false; + const gsl_rng_type** pptRNGType = GetRNGTypes(); + while((!bFound) && (pptRNGType != NULL)) { + if(m_strType == (*pptRNGType)->name) { + bFound = true; + } + else { + ++pptRNGType; + } + } + if(!bFound) { + /* RNG type not found, error! */ + THROW_ARGOSEXCEPTION("Unknown random number generator type '" << m_strType << "'."); + } + /* We found the wanted RNG type, create the actual RNG */ + m_ptRNG = gsl_rng_alloc(*pptRNGType); + gsl_rng_set(m_ptRNG, m_unSeed); + /* Initialize RNG range */ + m_pcIntegerRNGRange = new CRange(gsl_rng_min(m_ptRNG), + gsl_rng_max(m_ptRNG)); +#else + /* Initialize RNG */ + m_ptRNG = new random_data; + ::memset(m_ptRNG, 0, sizeof(random_data)); + m_pchRNGState = new char[256]; + SInt32 nError = initstate_r(m_unSeed, m_pchRNGState, 256, m_ptRNG); + if(nError != 0) { + THROW_ARGOSEXCEPTION("Unable to create random number generator (initstate_r returned " << nError << ")."); + } + /* Initialize RNG range */ + m_pcIntegerRNGRange = new CRange(0, RAND_MAX); +#endif + } + + /****************************************/ + /****************************************/ + + void CRandom::CRNG::DisposeRNG() { +#ifndef CROSSCOMPILING + gsl_rng_free(m_ptRNG); +#else + delete m_ptRNG; + delete[] m_pchRNGState; +#endif + delete m_pcIntegerRNGRange; + } + + /****************************************/ + /****************************************/ + + void CRandom::CRNG::SaveState(CByteArray& c_buffer) { + /* Dump the seed of the generator */ + c_buffer << m_unSeed; + /* Dump the type of the generator */ + c_buffer << m_strType; + /* Dump the state of the generator */ +#ifndef CROSSCOMPILING + UInt8* pStateBuffer = reinterpret_cast(gsl_rng_state(m_ptRNG)); + c_buffer.AddBuffer(pStateBuffer, gsl_rng_size(m_ptRNG)); +#else + c_buffer.AddBuffer(reinterpret_cast(m_pchRNGState), 256); +#endif + } + + /****************************************/ + /****************************************/ + + void CRandom::CRNG::LoadState(CByteArray& c_buffer) { + /* Get the seed of the generator */ + c_buffer >> m_unSeed; + /* Get the type of the generator */ + std::string strReadType; + c_buffer >> strReadType; +#ifndef CROSSCOMPILING + /* Check if the type read and the current type match */ + if(strReadType != m_strType) { + /* The types don't match. + We need to recreate the generator with the right type */ + m_strType = strReadType; + DisposeRNG(); + CreateRNG(); + } + /* Get the state of the generator */ + size_t unStateSize = gsl_rng_size(m_ptRNG); + UInt8* pStateBuffer = new UInt8[unStateSize]; + c_buffer.FetchBuffer(pStateBuffer, unStateSize); + /* Restore the state of the RNG */ + ::memcpy(gsl_rng_state(m_ptRNG), pStateBuffer, unStateSize); + /* Cleanup */ + delete[] pStateBuffer; +#else + c_buffer.FetchBuffer(reinterpret_cast(m_pchRNGState), 256); + setstate_r(m_pchRNGState, m_ptRNG); +#endif + } + + /****************************************/ + /****************************************/ + + void CRandom::CRNG::Reset() { +#ifndef CROSSCOMPILING + gsl_rng_set(m_ptRNG, m_unSeed); +#else + initstate_r(m_unSeed, m_pchRNGState, 256, m_ptRNG); +#endif + } + + /****************************************/ + /****************************************/ + + bool CRandom::CRNG::Bernoulli(Real f_true) { +#ifndef CROSSCOMPILING + return gsl_rng_uniform(m_ptRNG) < f_true; +#else + UInt32 unNumber; + random_r(m_ptRNG, reinterpret_cast(&unNumber)); + return m_pcIntegerRNGRange->NormalizeValue(unNumber) < f_true; +#endif + } + + /****************************************/ + /****************************************/ + + CRadians CRandom::CRNG::Uniform(const CRange& c_range) { +#ifndef CROSSCOMPILING + return c_range.GetMin() + gsl_rng_uniform(m_ptRNG) * c_range.GetSpan(); +#else + UInt32 unNumber; + random_r(m_ptRNG, reinterpret_cast(&unNumber)); + CRadians cRetVal; + m_pcIntegerRNGRange->MapValueIntoRange(cRetVal, unNumber, c_range); + return cRetVal; +#endif + } + + /****************************************/ + /****************************************/ + + Real CRandom::CRNG::Uniform(const CRange& c_range) { +#ifndef CROSSCOMPILING + return c_range.GetMin() + gsl_rng_uniform(m_ptRNG) * c_range.GetSpan(); +#else + UInt32 unNumber; + random_r(m_ptRNG, reinterpret_cast(&unNumber)); + Real fRetVal; + m_pcIntegerRNGRange->MapValueIntoRange(fRetVal, unNumber, c_range); + return fRetVal; +#endif + } + + /****************************************/ + /****************************************/ + + SInt32 CRandom::CRNG::Uniform(const CRange& c_range) { +#ifndef CROSSCOMPILING + SInt32 nRetVal; + m_pcIntegerRNGRange->MapValueIntoRange(nRetVal, gsl_rng_get(m_ptRNG), c_range); + return nRetVal; +#else + UInt32 unNumber; + random_r(m_ptRNG, reinterpret_cast(&unNumber)); + SInt32 nRetVal; + m_pcIntegerRNGRange->MapValueIntoRange(nRetVal, unNumber, c_range); + return nRetVal; +#endif + } + + /****************************************/ + /****************************************/ + + UInt32 CRandom::CRNG::Uniform(const CRange& c_range) { +#ifndef CROSSCOMPILING + UInt32 unRetVal; + m_pcIntegerRNGRange->MapValueIntoRange(unRetVal, gsl_rng_get(m_ptRNG), c_range); + return unRetVal; +#else + UInt32 unNumber; + random_r(m_ptRNG, reinterpret_cast(&unNumber)); + UInt32 unRetVal; + m_pcIntegerRNGRange->MapValueIntoRange(unRetVal, unNumber, c_range); + return unRetVal; +#endif + } + + /****************************************/ + /****************************************/ + + Real CRandom::CRNG::Exponential(Real f_mean) { +#ifndef CROSSCOMPILING + return gsl_ran_exponential(m_ptRNG, f_mean); +#else + CRange fRange(0.0f, 1.0f); + return -log(Uniform(fRange)) / f_mean; +#endif + } + + /****************************************/ + /****************************************/ + + Real CRandom::CRNG::Gaussian(Real f_std_dev, + Real f_mean) { +#ifndef CROSSCOMPILING + return f_mean + gsl_ran_gaussian(m_ptRNG, f_std_dev); +#else + /* This is the Box-Muller method in its cartesian variant + see http://www.dspguru.com/dsp/howtos/how-to-generate-white-gaussian-noise + */ + CRange fRange(-1.0f, 1.0f); + Real fNum1, fNum2; + Real fSquare; + do { + fNum1 = Uniform(fRange); + fNum2 = Uniform(fRange); + fSquare = fNum1 * fNum1 + fNum2 * fNum2; + } while(fSquare >= 1); + return f_mean + f_std_dev * fNum1; +#endif + } + + /****************************************/ + /****************************************/ + + Real CRandom::CRNG::Rayleigh(Real f_sigma) { +#ifndef CROSSCOMPILING + return gsl_ran_rayleigh(m_ptRNG, f_sigma); +#else + /* Draw a number uniformly from (0,1) --- bounds excluded */ + Real fValue; + CRange cUnitRange(0.0f, 1.0f); + do { + fValue = Uniform(cUnitRange); + } + while(! cUnitRange.WithinMinBoundExcludedMaxBoundExcluded(fValue)); + /* Calculate the value to return from the definition of Rayleigh distribution + * http://en.wikipedia.org/wiki/Rayleigh_distribution#Generating_Rayleigh-distributed_random_variates + */ + return f_sigma * Sqrt(-2.0f * Log(fValue)); +#endif + } + +/****************************************/ + /****************************************/ + + Real CRandom::CRNG::Lognormal(Real f_sigma, Real f_mu) { +#ifndef CROSSCOMPILING + return gsl_ran_lognormal(m_ptRNG, f_mu, f_sigma); +#else + /* Draw a number uniformly from (0,1) */ + Real fValue; + fValue = Gaussian(1,0); + /* Calculate the value to return from the definition of Lognormal distribution + * http://en.wikipedia.org/wiki/Log-normal_distribution#Generating_log-normally_distributed_random_variates + */ + return std::exp(f_mu + f_sigma * fValue); +#endif + } + + /****************************************/ + /****************************************/ + + CRandom::CCategory::CCategory(const std::string& str_id, + UInt32 un_seed) : + m_strId(str_id), + m_unSeed(un_seed), + m_cSeeder(un_seed), + m_cSeedRange(1, std::numeric_limits::max()) {} + + /****************************************/ + /****************************************/ + + CRandom::CCategory::CCategory(CByteArray& c_buffer) : + m_unSeed(0), + m_cSeeder(0), + m_cSeedRange(1, std::numeric_limits::max()) { + LoadState(c_buffer); + } + + /****************************************/ + /****************************************/ + + CRandom::CCategory::~CCategory() { + while(! m_vecRNGList.empty()) { + delete m_vecRNGList.back(); + m_vecRNGList.pop_back(); + } + } + + /****************************************/ + /****************************************/ + + void CRandom::CCategory::SetSeed(UInt32 un_seed) { + m_unSeed = un_seed; + m_cSeeder.SetSeed(m_unSeed); + } + + /****************************************/ + /****************************************/ + + void CRandom::CCategory::SaveState(CByteArray& c_buffer) { + /* Dump id */ + c_buffer << m_strId; + /* Dump seed */ + c_buffer << m_unSeed; + /* Dump number of RNG in this category */ + c_buffer << m_vecRNGList.size(); + /* Dump the state of each RNG */ + for(size_t i = 0; i < m_vecRNGList.size(); ++i) { + m_vecRNGList[i]->SaveState(c_buffer); + } + /* Dump seeder */ + m_cSeeder.SaveState(c_buffer); + } + + /****************************************/ + /****************************************/ + + void CRandom::CCategory::LoadState(CByteArray& c_buffer) { + /* Empty the RNG list */ + while(!m_vecRNGList.empty()) { + delete m_vecRNGList.back(); + m_vecRNGList.pop_back(); + } + /* Get id */ + c_buffer >> m_strId; + /* Get seed */ + c_buffer >> m_unSeed; + /* Get number of RNG in this category */ + size_t unRNGNum; + c_buffer >> unRNGNum; + /* Create new generators */ + for(size_t i = 0; i < unRNGNum; ++i) { + m_vecRNGList.push_back(new CRNG(c_buffer)); + } + /* Get seeder */ + m_cSeeder.LoadState(c_buffer); + } + + /****************************************/ + /****************************************/ + + CRandom::CRNG* CRandom::CCategory::CreateRNG(const std::string& str_type) { + /* Get seed from internal RNG */ + UInt32 unSeed = m_cSeeder.Uniform(m_cSeedRange); + /* Create new RNG */ + m_vecRNGList.push_back(new CRNG(unSeed, str_type)); + return m_vecRNGList.back(); + } + + /****************************************/ + /****************************************/ + + void CRandom::CCategory::ResetRNGs() { + /* Reset internal RNG */ + m_cSeeder.Reset(); + ReseedRNGs(); + /* Reset the RNGs */ + for(size_t i = 0; i < m_vecRNGList.size(); ++i) { + m_vecRNGList[i]->Reset(); + } + } + + /****************************************/ + /****************************************/ + + void CRandom::CCategory::ReseedRNGs() { + for(size_t i = 0; i < m_vecRNGList.size(); ++i) { + /* Get seed from internal RNG */ + m_vecRNGList[i]->SetSeed(m_cSeeder.Uniform(m_cSeedRange)); + } + } + + /****************************************/ + /****************************************/ + + bool CRandom::CreateCategory(const std::string& str_category, + UInt32 un_seed) { + /* Is there a category already? */ + std::map::iterator itCategory = m_mapCategories.find(str_category); + if(itCategory == m_mapCategories.end()) { + /* No, create it */ + m_mapCategories.insert( + std::pair(str_category, + new CRandom::CCategory(str_category, + un_seed))); + return true; + } + return false; + } + + /****************************************/ + /****************************************/ + + CRandom::CCategory& CRandom::GetCategory(const std::string& str_category) { + CHECK_CATEGORY(str_category); + return *(itCategory->second); + } + + /****************************************/ + /****************************************/ + + bool CRandom::ExistsCategory(const std::string& str_category) { + try { + CHECK_CATEGORY(str_category); + return true; + } + catch(CARGoSException& ex) { + return false; + } + } + + /****************************************/ + /****************************************/ + + void CRandom::RemoveCategory(const std::string& str_category) { + CHECK_CATEGORY(str_category); + delete itCategory->second; + m_mapCategories.erase(itCategory); + } + + /****************************************/ + /****************************************/ + + CRandom::CRNG* CRandom::CreateRNG(const std::string& str_category, + const std::string& str_type) { + CHECK_CATEGORY(str_category); + return itCategory->second->CreateRNG(str_type); + } + + /****************************************/ + /****************************************/ + + UInt32 CRandom::GetSeedOf(const std::string& str_category) { + CHECK_CATEGORY(str_category); + return itCategory->second->GetSeed(); + } + + /****************************************/ + /****************************************/ + + void CRandom::SetSeedOf(const std::string& str_category, + UInt32 un_seed) { + CHECK_CATEGORY(str_category); + itCategory->second->SetSeed(un_seed); + } + + /****************************************/ + /****************************************/ + + void CRandom::SaveState(CByteArray& c_buffer) { + /* Dump the number of categories */ + c_buffer << m_mapCategories.size(); + /* Dump the categories */ + for(std::map::iterator itCategory = m_mapCategories.begin(); + itCategory != m_mapCategories.end(); + ++itCategory) { + itCategory->second->SaveState(c_buffer); + } + } + + /****************************************/ + /****************************************/ + + void CRandom::LoadState(CByteArray& c_buffer) { + /* Get the number of categories */ + size_t unCategories; + c_buffer >> unCategories; + /* Restore the categories */ + for(UInt32 i = 0; i < unCategories; ++i) { + CCategory* pcCat = new CCategory(c_buffer); + m_mapCategories.insert( + std::pair(pcCat->GetId(), pcCat)); + } + } + + /****************************************/ + /****************************************/ + + void CRandom::Reset() { + for(std::map::iterator itCategory = m_mapCategories.begin(); + itCategory != m_mapCategories.end(); + ++itCategory) { + itCategory->second->ResetRNGs(); + } + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/utility/math/rng.h b/src/core/utility/math/rng.h new file mode 100644 index 00000000..e193bd00 --- /dev/null +++ b/src/core/utility/math/rng.h @@ -0,0 +1,430 @@ +/** + * @file argos3/core/utility/math/rng.h + * + * @brief This file provides the definition of the random number generator of ARGoS. + * + * It is meant to be used not only inside ARGoS, but also from controllers, loop functions + * as well as external applications. + * + * In general, RNGs have the greatest impact on the reproducibility of experiments. + * However, once the random seed has been set, the series of random numbers generated + * by an RNG is fixed. In this way, experiments are reproducible. + * To further complicate things, ARGoS is a multi-threaded program. Multi-threading and + * RNGs create issues to reproducibility. In fact, if the RNGs were shared among the + * threads, it would be impossible to predict the order in which the RNGs are accessed, + * because thread scheduling is not controllable nor predictable. + * For this reason, a common solution is to assign a separated RNG to each thread. For + * ARGoS, this is not a viable method because we want the number of threads to be + * set by the user and we want the outcome of an experiment to be reproducible no matter + * how many threads are used. + * + * All these considerations brought us to the design of a RNG with a different approach. + * The class argos::CRandom is a static factory that creates argos::CRandom::CRNG + * objects. If a component (sensor, actuator, controller, etc.) needs to get random + * numbers, it has to first create a local RNG class (typically, at init time). Internally, + * the factory assigns a seed to it. + * + * To allow for usage from different components (such as ARGoS main code, an evolutionary + * algorithm, controllers, etc.) RNGs are divided up in categories. The ARGoS core uses + * the argos category. When ARGoS is reset, all the RNGs in this category are reset + * as well, but not the RNGs in other categories. Categories give the user complete power on the + * RNGs, while ensuring compartmentalization. + * + * If all you want is drawing random numbers inside a controller, you can safely create new + * argos::CRandom::CRNG objects in the argos category. If, instead, you intend to + * wrap ARGoS around an optimization algorithm and want to maintain control of the way random + * number generators are managed, it is necessary for you to create your own category. + * + * To create a new RNG inside the argos category, this is enough: + *
+ * argos::CRandom::CRNG* m_pcRNG = argos::CRandom::CreateRNG("argos");
+ * 
+ * + * When you want to use random numbers with a custom category, you need to + * first call the function providing a label for the category and a base seed: + * + *
+ * argos::CRandom::CreateCategory("my_category", my_seed);
+ * 
+ * + * Once the category is created, you can get a new RNG with a call to the function + * + *
+ * argos::CRandom::CRNG* m_pcRNG = argos::CRandom::CreateRNG("my_category");
+ * 
+ * + * @author Carlo Pinciroli - + */ + +#ifndef ARGOS_RANDOM +#define ARGOS_RANDOM + +namespace argos { + class CRandom; +} + +#include +#include +#include +#include + +#ifndef CROSSCOMPILING +# include +#else +# include +#endif + +namespace argos { + + /** + * The RNG factory. + * This class allows one to create new categories of RNGs and manage them. In addition, + * this class allows one to create new RNGs and assign them to categories. + */ + class CRandom { + + public: + + /** + * The RNG. + * This class is the real random number generator. You need an instance of this class + * to be able to generate random numbers. + */ + class CRNG : public CMemento { + + public: + + /** + * Class constructor. + * To create a new RNG from user code, never use this method. Use CreateRNG() instead. + * @param un_seed the seed of the RNG. + * @param str_type the type of RNG to use. By default, Mersenne Twister is used. For a list of available RNG types, see GetRNGTypes(). + */ + CRNG(UInt32 un_seed, + const std::string& str_type = "mt19937"); + /** + * Class constructor. + * To create a new RNG from user code, never use this method. Use CreateRNG() instead. + * Constructs the class from a byte array. + * @param c_buffer the byte array to deserialize. + */ + CRNG(CByteArray& c_buffer); + /** + * Class copy constructor. + * To create a new RNG from user code, never use this method. Use CreateRNG() instead. + */ + CRNG(const CRNG& c_rng); + /** + * Class destructor. + */ + virtual ~CRNG(); + + /** + * Returns the seed of this RNG. + * @return the seed of this RNG. + */ + inline UInt32 GetSeed() const throw() { + return m_unSeed; + } + + /** + * Sets the seed of this RNG. + * @param un_seed the new seed for this RNG. + */ + inline void SetSeed(UInt32 un_seed) throw() { + m_unSeed = un_seed; + } + + /** + * Returns the type of this RNG. + * @return the type of this RNG. + */ + inline std::string GetType() const throw() { + return m_strType; + } + + /** + * Sets the type of this RNG. + * @param str_type the new type for this RNG. + */ + inline void SetType(const std::string& str_type) { + m_strType = str_type; + } + + /** + * Saves the state of the RNG into a byte array. + * @param c_buffer the target byte array. + */ + virtual void SaveState(CByteArray& c_buffer); + /** + * Loads the state of the RNG from a byte array. + * @param c_buffer the byte array. + */ + virtual void LoadState(CByteArray& c_buffer); + + /** + * Reset the RNG. + * Reset the RNG to the current seed value. + */ + void Reset(); + + /** + * Returns a random value from a Bernoulli distribution. + * @param f_true the probability to return a 1. + * @returns a random value from a Bernoulli distribution (true/false). + */ + bool Bernoulli(Real f_true = 0.5); + /** + * Returns a random value from a uniform distribution. + * @param c_range the range of values to draw one from. + * @return a random value from the range [min,max). + */ + CRadians Uniform(const CRange& c_range); + /** + * Returns a random value from a uniform distribution. + * @param c_range the range of values to draw one from. + * @return a random value from the range [min,max). + */ + Real Uniform(const CRange& c_range); + /** + * Returns a random value from a uniform distribution. + * @param c_range the range of values to draw one from. + * @return a random value from the range [min,max). + */ + SInt32 Uniform(const CRange& c_range); + /** + * Returns a random value from a uniform distribution. + * @param c_range the range of values to draw one from. + * @return a random value from the range [min,max). + */ + UInt32 Uniform(const CRange& c_range); + /** + * Returns a random value from an exponential distribution. + * @param f_mean the mean of the exponential distribution. + * @return a random value from an exponential distribution. + */ + Real Exponential(Real f_mean); + /** + * Returns a random value from a Gaussian distribution. + * @param f_std_dev the standard deviation of the Gaussian distribution. + * @param f_mean the mean of the Gaussian distribution. + * @return a random value from the Gaussian distribution. + */ + Real Gaussian(Real f_std_dev, Real f_mean = 0.0f); + /** + * Returns a random value from a Rayleigh distribution. + * @param f_sigma parameter sigma of the distribution + * @return a random value from the Rayleigh distribution. + */ + Real Rayleigh(Real f_sigma); + + /** + * Returns a random value from a Lognormal distribution. + * @param f_sigma parameter sigma of the distribution + * @param f_mu parameter mu of the distribution + * @return a random value from the Lognormal distribution. + */ + Real Lognormal(Real f_sigma, Real f_mu); + + private: + + void CreateRNG(); + void DisposeRNG(); + + private: + + UInt32 m_unSeed; + std::string m_strType; +#ifndef CROSSCOMPILING + gsl_rng* m_ptRNG; +#else + random_data* m_ptRNG; + char* m_pchRNGState; +#endif + CRange* m_pcIntegerRNGRange; + + }; + + /** + * The RNG category. + * This class stores a specific category of RNGs. + */ + class CCategory : public CMemento { + + public: + + /** + * Class constructor. + * @param str_id the id of the category. + * @param un_seed the seed of the category. + */ + CCategory(const std::string& str_id, + UInt32 un_seed); + /** + * Class copy constructor. + */ + CCategory(CByteArray& c_buffer); + /** + * Class destructor. + */ + virtual ~CCategory(); + + /** + * Returns the id of the category. + * @return the id of the category. + */ + inline const std::string& GetId() const throw() { + return m_strId; + } + /** + * Sets the new id of the category. + * @param str_id the new id of the category. + */ + void SetId(const std::string& str_id) { + m_strId = str_id; + } + + /** + * Returns the seed of the category. + * @return the seed of the category. + */ + inline UInt32 GetSeed() const { + return m_unSeed; + } + /** + * Sets the new seed of the category. + * @param str_id the new seed of the category. + */ + void SetSeed(UInt32 un_seed); + + /** + * Saves the state of the RNG category into a byte array. + * @param c_buffer the target byte array. + */ + virtual void SaveState(CByteArray& c_buffer); + /** + * Loads the state of the RNG category from a byte array. + * @param c_buffer the byte array. + */ + virtual void LoadState(CByteArray& c_buffer); + + /** + * Creates a new RNG inside this category. + * @param str_type the type of RNG to use. By default, Mersenne Twister is used. For a list of available RNG types, see GetRNGTypes(). + * @return the pointer to a new RNG inside this category. + */ + CRNG* CreateRNG(const std::string& str_type = "mt19937"); + + /** + * Resets the RNGs in this category. + */ + void ResetRNGs(); + + /** + * Sets new seed for the RNGs in this category. + */ + void ReseedRNGs(); + + private: + + std::string m_strId; + std::vector m_vecRNGList; + UInt32 m_unSeed; + CRNG m_cSeeder; + CRange m_cSeedRange; + }; + + public: + + /** + * Creates a new category. + * @param str_category the id of the category. + * @param un_seed the base seed of the category. + * @return true if the category was created; false if a category with the passed if exists already. + */ + static bool CreateCategory(const std::string& str_category, + UInt32 un_seed); + /** + * Returns a reference to the wanted category. + * @param str_category the id of the category. + * @return a reference to the wanted category. + */ + static CCategory& GetCategory(const std::string& str_category); + + /** + * Returns true if the given category exists in the pool. + * @param str_category the id of the category. + * @return true if the given category exists in the pool. + */ + static bool ExistsCategory(const std::string& str_category); + + /** + * Removes the wanted category. + * @param str_category the id of the category. + */ + static void RemoveCategory(const std::string& str_category); + + /** + * Creates a new RNG inside the given category. + * @param str_category the id of the category. + * @param str_type the type of RNG to use. By default, Mersenne Twister is used. For a list of available RNG types, see GetRNGTypes(). + * @return the pointer to a new RNG inside this category. + */ + static CRNG* CreateRNG(const std::string& str_category, + const std::string& str_type = "mt19937"); + + /** + * Returns the seed of the wanted category. + * @param str_category the id of the category. + * @return the seed of the wanted category. + */ + static UInt32 GetSeedOf(const std::string& str_category); + + /** + * Sets the new seed of the wanted category. + * @param str_category the id of the category. + * @param str_id the new seed of the wanted category. + */ + static void SetSeedOf(const std::string& str_category, + UInt32 un_seed); + + /** + * Saves the state of all the RNG categories into a byte array. + * @param c_buffer the target byte array. + */ + virtual void SaveState(CByteArray& c_buffer); + /** + * Loads the state of all the RNG categories from a byte array. + * @param c_buffer the byte array. + */ + virtual void LoadState(CByteArray& c_buffer); + + /** + * Resets all the RNG categories. + */ + static void Reset(); + + /** + * Returns the list of available RNG types. + * Internally, GSL is used to manage random numbers, so the available types + * correspond to those available in your installed version of GSL. + * @return the list of available RNG types. + */ +#ifndef CROSSCOMPILING + inline static const gsl_rng_type** GetRNGTypes() { + return m_pptRNGTypes; + } +#endif + + private: + + static std::map m_mapCategories; +#ifndef CROSSCOMPILING + static const gsl_rng_type** m_pptRNGTypes; +#endif + + }; + +} + +#endif diff --git a/src/core/utility/math/rotationmatrix2.cpp b/src/core/utility/math/rotationmatrix2.cpp new file mode 100644 index 00000000..ceee78e6 --- /dev/null +++ b/src/core/utility/math/rotationmatrix2.cpp @@ -0,0 +1,31 @@ +/** + * @file argos3/core/utility/math/rotationmatrix2.cpp + * + * @brief Contains the implementation of a 2x2 rotation matrix + * + * @author Michael Allwright + */ + +#include "rotationmatrix2.h" +#include "angles.h" + +namespace argos { + + /****************************************/ + /****************************************/ + + void CRotationMatrix2::SetFromAngle(const CRadians& c_angle) { + Real cos_angle = Cos(c_angle); + Real sin_angle = Sin(c_angle); + + /* Set values */ + m_fValues[0] = cos_angle; + m_fValues[1] = -sin_angle; + m_fValues[2] = sin_angle; + m_fValues[3] = cos_angle; + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/utility/math/rotationmatrix2.h b/src/core/utility/math/rotationmatrix2.h new file mode 100644 index 00000000..61bcb422 --- /dev/null +++ b/src/core/utility/math/rotationmatrix2.h @@ -0,0 +1,41 @@ +/** + * @file argos3/core/utility/math/rotationmatrix2.h + * + * @brief Contains the definition of a 2x2 rotation matrix + * + * @author Michael Allwright + */ + +#ifndef ROTATION_MATRIX2_H +#define ROTATION_MATRIX2_H + +namespace argos { + class CRadians; +} + +#include "squarematrix.h" +#include "vector2.h" + +namespace argos { + + class CRotationMatrix2 : public CSquareMatrix<2> { + + public: + CRotationMatrix2() : CSquareMatrix<2>() { + SetIdentityMatrix(); + } + + CRotationMatrix2(const CRadians& c_angle) : CSquareMatrix<2>() { + SetFromAngle(c_angle); + } + + void SetFromAngle(const CRadians& c_angle); + + CVector2 operator*(const CVector2& c_vector) const { + return CVector2(m_fValues[0]*c_vector.m_fX + m_fValues[1]*c_vector.m_fY, + m_fValues[2]*c_vector.m_fX + m_fValues[3]*c_vector.m_fY); + } + }; +} + +#endif diff --git a/src/core/utility/math/rotationmatrix3.cpp b/src/core/utility/math/rotationmatrix3.cpp new file mode 100644 index 00000000..cf69ad18 --- /dev/null +++ b/src/core/utility/math/rotationmatrix3.cpp @@ -0,0 +1,41 @@ +/** + * @file argos3/core/utility/math/rotationmatrix3.cpp + * + * @brief Contains the implementation of a 3x3 rotation matrix + * + * @author Michael Allwright + */ + +#include "rotationmatrix3.h" +#include "quaternion.h" + +namespace argos { + + /****************************************/ + /****************************************/ + + void CRotationMatrix3::SetFromQuaternion(const CQuaternion& c_quaternion) { + /* This code is adapted from the Bullet source code */ + Real d = c_quaternion.SquareLength(); + Real s = 2.0f / d; + Real xs = c_quaternion.GetX() * s, ys = c_quaternion.GetY() * s, zs = c_quaternion.GetZ() * s; + Real wx = c_quaternion.GetW() * xs, wy = c_quaternion.GetW() * ys, wz = c_quaternion.GetW() * zs; + Real xx = c_quaternion.GetX() * xs, xy = c_quaternion.GetX() * ys, xz = c_quaternion.GetX() * zs; + Real yy = c_quaternion.GetY() * ys, yz = c_quaternion.GetY() * zs, zz = c_quaternion.GetZ() * zs; + + /* Set values */ + m_fValues[0] = 1.0f - (yy + zz); + m_fValues[1] = xy - wz; + m_fValues[2] = xz + wy; + m_fValues[3] = xy + wz; + m_fValues[4] = 1.0f - (xx + zz); + m_fValues[5] = yz - wx; + m_fValues[6] = xz - wy; + m_fValues[7] = yz + wx; + m_fValues[8] = 1.0f - (xx + yy); + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/utility/math/rotationmatrix3.h b/src/core/utility/math/rotationmatrix3.h new file mode 100644 index 00000000..f0d6ac20 --- /dev/null +++ b/src/core/utility/math/rotationmatrix3.h @@ -0,0 +1,42 @@ +/** + * @file argos3/core/utility/math/rotationmatrix3.h + * + * @brief Contains the definition of a 3x3 rotation matrix + * + * @author Michael Allwright + */ + +#ifndef ROTATION_MATRIX3_H +#define ROTATION_MATRIX3_H + +namespace argos { + class CQuaternion; +} + +#include "squarematrix.h" +#include "vector3.h" + +namespace argos { + + class CRotationMatrix3 : public CSquareMatrix<3> { + + public: + CRotationMatrix3() : CSquareMatrix<3>() { + SetIdentityMatrix(); + } + + CRotationMatrix3(const CQuaternion& c_quaternion) : CSquareMatrix<3>() { + SetFromQuaternion(c_quaternion); + } + + void SetFromQuaternion(const CQuaternion& c_quaternion); + + CVector3 operator*(const CVector3& c_vector) const { + return CVector3(m_fValues[0]*c_vector.m_fX + m_fValues[1]*c_vector.m_fY + m_fValues[2]*c_vector.m_fZ, + m_fValues[3]*c_vector.m_fX + m_fValues[4]*c_vector.m_fY + m_fValues[5]*c_vector.m_fZ, + m_fValues[6]*c_vector.m_fX + m_fValues[7]*c_vector.m_fY + m_fValues[8]*c_vector.m_fZ); + } + }; +} + +#endif diff --git a/src/core/utility/math/segment.cpp b/src/core/utility/math/segment.cpp new file mode 100644 index 00000000..63227770 --- /dev/null +++ b/src/core/utility/math/segment.cpp @@ -0,0 +1,75 @@ +#include "segment.h" + +namespace argos { + + /****************************************/ + /****************************************/ + + bool CSegment::Intersect(const CSegment& c_segment, + CVector2& c_intersection) { + Real fDenom = (((c_segment.GetEnd().GetY() - c_segment.GetStart().GetY())*(m_cEnd.GetX() - m_cStart.GetX())) - + ((c_segment.GetEnd().GetX() - c_segment.GetStart().GetX())*(m_cEnd.GetY() - m_cStart.GetY()))); + Real fNumA = (((c_segment.GetEnd().GetX() - c_segment.GetStart().GetX())*(m_cStart.GetY() - c_segment.GetStart().GetY())) - + ((c_segment.GetEnd().GetY() - c_segment.GetStart().GetY())*(m_cStart.GetX() - c_segment.GetStart().GetX()))); + Real fNumB = (((m_cEnd.GetX() - m_cStart.GetX())*(m_cStart.GetY() - c_segment.GetStart().GetY())) - + ((m_cEnd.GetY() - m_cStart.GetY())*(m_cStart.GetX() - c_segment.GetStart().GetX()))); + if( fDenom == 0.0f ) { + // the segments are coincident + if(fNumA == 0.0f && fNumB == 0.0f) { + c_intersection = m_cStart; + return true; + } + // the segments are parallel + return false; + } + // Get the intersection point, it it exists + Real fUA = fNumA / fDenom; + Real fUB = fNumB / fDenom; + if( fUA >= 0.0f && fUA <= 1.0f && fUB >= 0.0f && fUB <= 1.0f ) { + c_intersection.SetX(m_cStart.GetX() + fUA*(m_cEnd.GetX() - m_cStart.GetX())); + c_intersection.SetY(m_cStart.GetY() + fUA*(m_cEnd.GetY() - m_cStart.GetY())); + return true; + } + return false; + } + + + /** + * This function computes the minimum distance points of a + * given point from the line identified by a segment, i.e., + * the projection point to the line and the closest point on + * the segment + * (http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/) + * + * @return a real value indicating wether the closest point + * lays within the segment, or oustise. A value < 0 indicates + * that the intersection is before the start point, a value + * between 0 and 1 indicates that the intersection is within + * the segment, and a value > 1 indicates it is beyond the end + * point. + */ + Real CSegment::GetMinimumDistancePoints(const CVector2& c_point, + CVector2& c_closest_point, + CVector2& c_closest_segment_point) { + Real fSegLenSquare = GetSquareLength(); + Real fSegLenFrac = ( (c_point.GetX() - m_cStart.GetX())*(m_cEnd.GetX() - m_cStart.GetX()) + + (c_point.GetY() - m_cStart.GetY())*(m_cEnd.GetY() - m_cStart.GetY()) ) / fSegLenSquare; + c_closest_point.SetX(m_cStart.GetX() + fSegLenFrac*(m_cEnd.GetX()-m_cStart.GetX())); + c_closest_point.SetY(m_cStart.GetY() + fSegLenFrac*(m_cEnd.GetY()-m_cStart.GetY())); + if( fSegLenFrac < 0 ) { + c_closest_segment_point = m_cStart; + } + else if( fSegLenFrac > 1 ) { + c_closest_segment_point = m_cEnd; + } + else { + c_closest_segment_point = c_closest_point; + } + return fSegLenFrac; + } + + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/utility/math/segment.h b/src/core/utility/math/segment.h new file mode 100644 index 00000000..40fab04b --- /dev/null +++ b/src/core/utility/math/segment.h @@ -0,0 +1,146 @@ +#ifndef SEGMENT_H +#define SEGMENT_H + +namespace argos { + class CSegment; +} + +#include + +namespace argos { + + class CSegment { + + public: + + CSegment() { + } + + CSegment(const CVector2& c_start, + const CVector2& c_end) : + m_cStart(c_start), m_cEnd(c_end) { + } + + CSegment(const CVector2& c_start, + const CVector2& c_direction, + Real f_length) { + Set(c_start, c_direction, f_length); + } + + inline const CVector2& GetStart() const { + return m_cStart; + } + + inline const CVector2& GetEnd() const { + return m_cEnd; + } + + inline void SetStart(const CVector2& c_start) { + m_cStart = c_start; + } + + inline void SetEnd(const CVector2& c_end) { + m_cEnd = c_end; + } + + inline void Set(const CVector2& c_start, + const CVector2& c_end) { + m_cStart = c_start; + m_cEnd = c_end; + } + + inline void Set(const CVector2& c_start, + const CVector2& c_direction, + Real f_length) { + m_cStart = c_start; + /* Same as, but faster than + m_cEnd = m_cStart + f_length * c_direction; */ + m_cEnd = m_cStart; + m_cEnd += f_length * c_direction; + } + + inline void GetDirection(CVector2& c_buffer) const { + /* Same as, but faster than + c_buffer = (m_cEnd - m_cStart).Normalize(); */ + c_buffer = m_cEnd; + c_buffer -= m_cStart; + c_buffer.Normalize(); + } + + inline void GetInverseDirection(CVector2& c_buffer) const { + /* Same as, but faster than + c_buffer = (m_cEnd - m_cStart).Normalize(); */ + c_buffer = m_cStart; + c_buffer -= m_cEnd; + c_buffer.Normalize(); + } + + inline Real GetLength() const { + return (m_cEnd - m_cStart).Length(); + } + + inline Real GetSquareLength() const { + return (m_cEnd - m_cStart).SquareLength(); + } + + inline void ToVector(CVector2& c_buffer) const { + /* Same as, but faster than + c_buffer = m_cEnd - m_cStart; */ + c_buffer = m_cEnd; + c_buffer -= m_cStart; + } + + /* Returns the point on the line corresponding to f_t, + which is in the range [0:1] */ + inline void GetPoint(CVector2& c_point, + Real f_t) const { + ARGOS_ASSERT(f_t >= 0.0f && f_t <= 1.0f, + "CSegment::GetPoint(): the f_t parameter must be in the range [0:1], but f_t = " << f_t); + c_point.SetX(m_cStart.GetX() + f_t * (m_cEnd.GetX() - m_cStart.GetX())); + c_point.SetY(m_cStart.GetY() + f_t * (m_cEnd.GetY() - m_cStart.GetY())); + } + + /* Returns the distance from the segment start to the point on the line corresponding to f_t, + which is in the range [0:1] */ + inline Real GetDistance(Real f_t) const { + ARGOS_ASSERT(f_t >= 0.0f && f_t <= 1.0f, + "CSegment::GetDistance(): the f_t parameter must be in the range [0:1], but f_t = " << f_t); + return ::sqrt(Square(f_t * (m_cEnd.GetX() - m_cStart.GetX())) + + Square(f_t * (m_cEnd.GetY() - m_cStart.GetY()))); + } + + + /** + * This function check intersection with another segment (from http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/) + * + * @return true if there is an intersection or if the two segments are coincident (in this case, the starting point is returned), false otherwise + */ + bool Intersect(const CSegment& c_segment, + CVector2& c_intersection); + /** + * This function computes the minimum distance points of a + * given point from the line identified by a segment, i.e., + * the projection point to the line and the closest point on + * the segment + * (http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/) + * + * @return a real value indicating wether the closest point + * lays within the segment, or oustise. A value < 0 indicates + * that the intersection is before the start point, a value + * between 0 and 1 indicates that the intersection is within + * the segment, and a value > 1 indicates it is beyond the end + * point. + */ + Real GetMinimumDistancePoints(const CVector2& c_point, + CVector2& c_closest_point, + CVector2& c_closest_segment_point); + private: + + CVector2 m_cStart; + CVector2 m_cEnd; + + }; + +} + +#endif diff --git a/src/core/utility/math/squarematrix.h b/src/core/utility/math/squarematrix.h new file mode 100644 index 00000000..9c5f7903 --- /dev/null +++ b/src/core/utility/math/squarematrix.h @@ -0,0 +1,58 @@ +/** + * @file argos3/core/utility/math/squarematrix.h + * + * @brief Contains the definition of a NxN templated square matrix + * + * @author Michael Allwright + */ + +#ifndef SQUARE_MATRIX_H +#define SQUARE_MATRIX_H + +#include "matrix.h" + +namespace argos { + + template + class CSquareMatrix : public CMatrix { + + public: + CSquareMatrix() : CMatrix() {} + + void SetIdentityMatrix() { + for(UInt32 i = 0; i < DIM; i++) { + for(UInt32 j = 0; j < DIM; j++) { + if(i == j) { + CMatrix::m_fValues[i * DIM + j] = 1; + } + else { + CMatrix::m_fValues[i * DIM + j] = 0; + } + } + } + } + + bool IsIdentityMatrix() { + bool bIsIdentMat = true; + for(UInt32 i = 0; i < DIM; i++) { + for(UInt32 j = 0; j < DIM; j++) { + if(i == j) { + if(CMatrix::m_fValues[i * DIM + j] != 1) { + bIsIdentMat = false; + break; + } + } + else { + if(CMatrix::m_fValues[i * DIM + j] != 0) { + bIsIdentMat = false; + break; + } + } + } + } + return bIsIdentMat; + } + }; +} + +#endif diff --git a/src/core/utility/math/vector2.cpp b/src/core/utility/math/vector2.cpp new file mode 100644 index 00000000..eb742a0b --- /dev/null +++ b/src/core/utility/math/vector2.cpp @@ -0,0 +1,15 @@ +/** + * @file argos3/core/utility/math/vector2.cpp + * @author Carlo Pinciroli + * + * @brief Contains the definition of the CVector2 class. + */ + +#include "vector2.h" + +namespace argos { + + const CVector2 CVector2::X(1.0, 0.0); + const CVector2 CVector2::Y(0.0, 1.0); + +} diff --git a/src/core/utility/math/vector2.h b/src/core/utility/math/vector2.h new file mode 100644 index 00000000..5bbe3193 --- /dev/null +++ b/src/core/utility/math/vector2.h @@ -0,0 +1,364 @@ +/** + * @file argos3/core/utility/math/vector2.h + * @author Carlo Pinciroli + * + * @brief Contains the definition of the CVector2 class. + */ + +#ifndef VECTOR2_H +#define VECTOR2_H + +namespace argos { + class CRotationMatrix2; +} + +#include +#include +#include +#include +#include + +namespace argos { + + /** + * It defines a basic bidimensional vector. + */ + class CVector2 { + + friend class CRotationMatrix2; + + public: + + static const CVector2 X; /**< The X axis <1,0> */ + static const CVector2 Y; /**< The Y axis <0,1> */ + + /** + * @brief Class constructor + * + * It initializes the vector coordinates to zero. + */ + CVector2() : + m_fX(0.0), + m_fY(0.0) { + } + + /** + * @brief Class constructor + * + * It initializes the vector coordinates to the passed values. + * + * @param f_x the wanted value for the x coordinate + * @param f_y the wanted value for the y coordinate + */ + CVector2(Real f_x, + Real f_y) : + m_fX(f_x), + m_fY(f_y) { + } + + /** + * @brief Class constructor + * + * It initializes the vector coordinates from the passed length and angle. + * + * @param f_length the wanted length + * @param f_angle the wanted angle in radians + */ + CVector2(Real f_length, + const CRadians& f_angle) : + m_fX(Cos(f_angle) * f_length), + m_fY(Sin(f_angle) * f_length) { + } + + /** + * @brief It returns the X coordinate + * + * @return the vector's X coordinate + */ + inline Real GetX() const { + return m_fX; + } + + /** + * @brief It sets the value of the X coordinate + * + * @param f_x the wanted value + */ + inline void SetX(Real f_x) { + m_fX = f_x; + } + + /** + * @brief It returns the Y coordinate + * + * @return the vector's Y coordinate + */ + inline Real GetY() const { + return m_fY; + } + + /** + * @brief It sets the value of the Y coordinate + * + * @param f_y the wanted value + */ + inline void SetY(Real f_y) { + m_fY = f_y; + } + + /** + * @brief It sets the value of the X and Y coordinates + * + * @param f_x the wanted X value + * @param f_y the wanted Y value + */ + inline void Set(Real f_x, Real f_y) { + m_fX = f_x; + m_fY = f_y; + } + + /** + * @brief Sets the vector coordinates from angle and length + * + * It sets the vector coordinates from the passed angle and length. + * + * @param f_angle the wanted angle in radians + * @param f_length the wanted length + */ + inline void SetFromAngleAndLength(const CRadians& f_angle, + Real f_length) + { + m_fX = Cos(f_angle) * f_length; + m_fY = Sin(f_angle) * f_length; + } + + /** + * @brief It returns the square length of the vector + * + * @return the square length of the vector + */ + inline Real SquareLength() const { + return Square(m_fX) + Square(m_fY); + } + + /** + * @brief It returns the length of the vector + * + * @return the length of the vector + */ + inline Real Length() const { + return ::sqrt(SquareLength()); + } + + /** + * @brief It normalizes the vector + * + * It normalizes the vector, i.e., it resets the vector coordinates in such a way that the angle is the same, but the length is 1. + * + * @return a reference to the current object after normalization + */ + inline CVector2& Normalize() { + (*this) /= Length(); + return (*this); + } + + /** + * @brief It returns the angle of the vector + * + * @return the angle of the vector in radians + */ + inline CRadians Angle() const { + return ATan2(m_fY, m_fX); + } + + /** + * @brief It rotates the vector by the wanted angle + * + * @return a reference to the current object after the rotation + */ + inline CVector2& Rotate(const CRadians& c_angle) { + Real cosa = Cos(c_angle); + Real sina = Sin(c_angle); + Real fX = m_fX * cosa - m_fY * sina; + Real fY = m_fX * sina + m_fY * cosa; + m_fX = fX; + m_fY = fY; + return *this; + } + + /** + * @brief It computes the dot product of the current vector with the passed one + * + * It computes the dot product of the current vector with the passed one. In other words, it calculates + * m_fX * c_vector2.m_fX + m_fY * c_vector2.m_fY. + * + * @return the dot product of the current vector with the passed one + */ + inline Real DotProduct(const CVector2& c_vector2) const { + return m_fX * c_vector2.m_fX + m_fY * c_vector2.m_fY; + } + + /** + * @brief It scales the vector by the wanted values + * + * It scales the vector by the wanted values. In practice, this means that it multiplies the X coordinate by f_scale_x and the Y coordinate by f_scale_y. + * + * @param f_scale_x the scale factor for the X coordinate + * @param f_scale_y the scale factor for the Y coordinate + * + * @return a reference to the current object after scaling + */ + inline CVector2& Scale(Real f_scale_x, + Real f_scale_y) { + m_fX *= f_scale_x; + m_fY *= f_scale_y; + return *this; + } + + /** + * @brief It transforms a vector into its ortogonal + * + * It transofroms the vector into its ortogonal, simply by swapping y and x coordinates. + * + * @return a reference to the current object after scaling + */ + inline CVector2& Perpendicularize() { + Real fNewX = -m_fY; + m_fY = m_fX; + m_fX = fNewX; + return *this; + } + + /** + * @brief It transforms a vector into positive coordinates + * + * It transofroms the vector into a vector with positive cooridnates. + * + * @return a reference to the current object after scaling + */ + inline CVector2& Absolute() { + m_fX = Abs(m_fX); + m_fY = Abs(m_fY); + return *this; + } + + inline bool operator==(const CVector2& c_vector2) { + return (m_fX == c_vector2.m_fX && m_fY == c_vector2.m_fY); + } + + inline bool operator!=(const CVector2& c_vector2) { + return (m_fX != c_vector2.m_fX || m_fY != c_vector2.m_fY); + } + + inline CVector2& operator+=(const CVector2& c_vector2) { + m_fX += c_vector2.m_fX; + m_fY += c_vector2.m_fY; + return *this; + } + + inline CVector2& operator-=(const CVector2& c_vector2) { + m_fX -= c_vector2.m_fX; + m_fY -= c_vector2.m_fY; + return *this; + } + + inline CVector2& operator*=(Real f_value) { + m_fX *= f_value; + m_fY *= f_value; + return *this; + } + + inline CVector2& operator/=(Real f_value) { + m_fX /= f_value; + m_fY /= f_value; + return *this; + } + + inline CVector2 operator+(const CVector2& c_vector2) const { + CVector2 cResult(*this); + cResult += c_vector2; + return cResult; + } + + inline CVector2 operator-(const CVector2& c_vector2) const { + CVector2 cResult(*this); + cResult -= c_vector2; + return cResult; + } + + inline CVector2 operator*(Real f_value) const { + CVector2 cResult(*this); + cResult *= f_value; + return cResult; + } + + inline CVector2 operator/(Real f_value) const { + CVector2 cResult(*this); + cResult /= f_value; + return cResult; + } + + inline friend CVector2 operator*(Real f_value, + const CVector2& c_vector2) { + return c_vector2 * f_value; + } + + inline CVector2 operator-() const { + return CVector2(-m_fX, -m_fY); + } + + inline friend std::ostream& operator<<(std::ostream& os, + const CVector2& c_vector2) { + os << c_vector2.m_fX << "," + << c_vector2.m_fY; + return os; + } + + inline friend std::istream& operator>>(std::istream& is, + CVector2& c_vector2) { + Real fValues[2]; + ParseValues (is, 2, fValues, ','); + c_vector2.Set(fValues[0], fValues[1]); + return is; + } + + private: + + Real m_fX; /**< The vector X coordinate */ + Real m_fY; /**< The vector Y coordinate */ + + }; + + /****************************************/ + /****************************************/ + + /** + * It computes the square distance between the passed vectors. + * + * @param c_v1 The first vector + * @param c_v2 The second vector + * + * @return the square distance between the passed vectors + */ + inline Real SquareDistance(const CVector2& c_v1, const CVector2& c_v2) { + return (c_v1 - c_v2).SquareLength(); + } + + /** + * It computes the distance between the passed vectors. + * + * @param c_v1 The first vector + * @param c_v2 The second vector + * + * @return the distance between the passed vectors + */ + inline Real Distance(const CVector2& c_v1, const CVector2& c_v2) { + return (c_v1 - c_v2).Length(); + } + + /****************************************/ + /****************************************/ + +} + +#endif diff --git a/src/core/utility/math/vector3.cpp b/src/core/utility/math/vector3.cpp new file mode 100644 index 00000000..ff3cc5b7 --- /dev/null +++ b/src/core/utility/math/vector3.cpp @@ -0,0 +1,39 @@ +/** + * @file common/utility/math/vector3.h + * + * @brief This file provides the definition of + * + * @author Carlo Pinciroli - + */ + +#include "vector3.h" +#include "quaternion.h" + +namespace argos { + + /****************************************/ + /****************************************/ + + const CVector3 CVector3::X(1.0, 0.0, 0.0); + const CVector3 CVector3::Y(0.0, 1.0, 0.0); + const CVector3 CVector3::Z(0.0, 0.0, 1.0); + const CVector3 CVector3::ZERO; + + /****************************************/ + /****************************************/ + + CVector3& CVector3::Rotate(const CQuaternion& c_quaternion) { + CQuaternion cResult; + cResult = c_quaternion; + cResult *= CQuaternion(0.0f, m_fX, m_fY, m_fZ); + cResult *= c_quaternion.Inverse(); + m_fX = cResult.GetX(); + m_fY = cResult.GetY(); + m_fZ = cResult.GetZ(); + return *this; + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/utility/math/vector3.h b/src/core/utility/math/vector3.h new file mode 100644 index 00000000..a7b81285 --- /dev/null +++ b/src/core/utility/math/vector3.h @@ -0,0 +1,355 @@ +/** + * @file common/utility/math/vector3.h + * + * @brief This file provides the definition of + * + * @author Carlo Pinciroli - + */ + +#ifndef VECTOR3_H +#define VECTOR3_H + +namespace argos { + class CVector3; + class CVector2; + class CQuaternion; + class CRotationMatrix3; +} + +#include +#include +#include +#include +#include +#include + +namespace argos { + + class CVector3 { + + friend class CRotationMatrix3; + + public: + + static const CVector3 X; + static const CVector3 Y; + static const CVector3 Z; + static const CVector3 ZERO; + + CVector3() : + m_fX(0.0), + m_fY(0.0), + m_fZ(0.0) { + } + + CVector3(Real f_x, + Real f_y, + Real f_z) : + m_fX(f_x), + m_fY(f_y), + m_fZ(f_z) { + } + + inline CVector3(Real f_radius, + const CRadians& c_inclination, + const CRadians& c_azimuth) { + FromSphericalCoords(f_radius, c_inclination, c_azimuth); + } + + inline Real GetX() const { + return m_fX; + } + + inline void SetX(const Real f_x) { + m_fX = f_x; + } + + inline Real GetY() const { + return m_fY; + } + + inline void SetY(const Real f_y) { + m_fY = f_y; + } + + inline Real GetZ() const { + return m_fZ; + } + + inline void SetZ(const Real f_z) { + m_fZ = f_z; + } + + inline void Set(const Real f_x, + const Real f_y, + const Real f_z) { + m_fX = f_x; + m_fY = f_y; + m_fZ = f_z; + } + + inline CVector3& FromSphericalCoords(Real f_radius, + const CRadians& c_inclination, + const CRadians& c_azimuth) { + m_fX = f_radius * Sin(c_inclination) * Cos(c_azimuth); + m_fY = f_radius * Sin(c_inclination) * Sin(c_azimuth); + m_fZ = f_radius * Cos(c_inclination); + return *this; + } + + inline void ToSphericalCoords(Real& f_radius, + CRadians& c_inclination, + CRadians& c_azimuth) const { + f_radius = Length(); + c_inclination = ACos(m_fZ / f_radius); + c_azimuth = ATan2(m_fY, m_fX); + } + + inline void ToSphericalCoordsHorizontal(Real& f_radius, + CRadians& c_inclination, + CRadians& c_azimuth) const { + f_radius = Length(); + c_inclination = ASin(m_fZ / f_radius); + c_azimuth = ATan2(m_fY, m_fX); + } + + inline Real SquareLength() const { + return Square(m_fX) + Square(m_fY) + Square(m_fZ); + } + + inline Real Length() const { + return ::sqrt(SquareLength()); + } + + inline CVector3& Normalize() { + (*this) /= Length(); + return (*this); + } + + inline CVector3& RotateX(const CRadians& c_angle) { + Real fCos = Cos(c_angle); + Real fSin = Sin(c_angle); + Real fNewY = m_fY * fCos - m_fZ * fSin; + Real fNewZ = m_fY * fSin + m_fZ * fCos; + m_fY = fNewY; + m_fZ = fNewZ; + return *this; + } + + inline CVector3& RotateY(const CRadians& c_angle) { + Real fCos = Cos(c_angle); + Real fSin = Sin(c_angle); + Real fNewX = m_fX * fCos + m_fZ * fSin; + Real fNewZ = m_fZ * fCos - m_fX * fSin; + m_fX = fNewX; + m_fZ = fNewZ; + return *this; + } + + inline CVector3& RotateZ(const CRadians& c_angle) { + Real fCos = Cos(c_angle); + Real fSin = Sin(c_angle); + Real fNewX = m_fX * fCos - m_fY * fSin; + Real fNewY = m_fX * fSin + m_fY * fCos; + m_fX = fNewX; + m_fY = fNewY; + return *this; + } + + /* Fast rotate by angle theta: the passed 2d vector is normalized + and is < cos(theta), sin(theta) > */ + inline CVector3& RotateZ(const CVector2& c_vector) { + Real fNewX = m_fX * c_vector.GetX() - m_fY * c_vector.GetY(); + Real fNewY = m_fX * c_vector.GetY() + m_fY * c_vector.GetX(); + m_fX = fNewX; + m_fY = fNewY; + return *this; + } + + CVector3& Rotate(const CQuaternion& c_quaternion); + + inline CRadians GetZAngle() const { + return ATan2(m_fY, m_fX); + } + + inline Real DotProduct(const CVector3& c_vector3) const { + return + m_fX * c_vector3.m_fX + + m_fY * c_vector3.m_fY + + m_fZ * c_vector3.m_fZ; + } + + inline CVector3& CrossProduct(const CVector3& c_vector3) { + Real fNewX, fNewY, fNewZ; + fNewX = m_fY * c_vector3.m_fZ - m_fZ * c_vector3.m_fY; + fNewY = m_fZ * c_vector3.m_fX - m_fX * c_vector3.m_fZ; + fNewZ = m_fX * c_vector3.m_fY - m_fY * c_vector3.m_fX; + m_fX = fNewX; + m_fY = fNewY; + m_fZ = fNewZ; + return *this; + } + + inline CVector2 ProjectOntoXY() const { + return CVector2(m_fX,m_fY); + } + + inline CVector2 ProjectOntoYZ() const { + return CVector2(m_fY,m_fZ); + } + + inline CVector2 ProjectOntoXZ() const { + return CVector2(m_fX,m_fZ); + } + + inline CVector3& Negate() { + m_fX = -m_fX; + m_fY = -m_fY; + m_fZ = -m_fZ; + return *this; + } + + inline Real operator[](UInt32 un_index) const { + switch(un_index) { + case 0: return m_fX; + case 1: return m_fY; + case 2: return m_fZ; + default: THROW_ARGOSEXCEPTION("Real Vector3::operator[]: index out of bounds"); + } + } + + inline Real& operator[](UInt32 un_index) { + switch(un_index) { + case 0: return m_fX; + case 1: return m_fY; + case 2: return m_fZ; + default: THROW_ARGOSEXCEPTION("Real& Vector3::operator[]: index out of bounds"); + } + } + + inline bool operator==(const CVector3& c_vector3) const { + return m_fX == c_vector3.m_fX && m_fY == c_vector3.m_fY && m_fZ == c_vector3.m_fZ; + } + + inline bool operator!=(const CVector3& c_vector3) const { + return !((*this) == c_vector3); + } + + inline bool operator<(const CVector3& c_vector3) const { + return m_fX < c_vector3.m_fX && m_fY < c_vector3.m_fY && m_fZ < c_vector3.m_fZ; + } + + inline bool operator<=(const CVector3& c_vector3) const { + return m_fX <= c_vector3.m_fX && m_fY <= c_vector3.m_fY && m_fZ <= c_vector3.m_fZ; + } + + inline bool operator>(const CVector3& c_vector3) const { + return m_fX > c_vector3.m_fX && m_fY > c_vector3.m_fY && m_fZ > c_vector3.m_fZ; + } + + inline bool operator>=(const CVector3& c_vector3) const { + return m_fX >= c_vector3.m_fX && m_fY >= c_vector3.m_fY && m_fZ >= c_vector3.m_fZ; + } + + inline CVector3 operator-() const { + return CVector3(-m_fX, -m_fY, -m_fZ); + } + + inline CVector3& operator+=(const CVector3& c_vector3) { + m_fX += c_vector3.m_fX; + m_fY += c_vector3.m_fY; + m_fZ += c_vector3.m_fZ; + return *this; + } + + inline CVector3& operator-=(const CVector3& c_vector3) { + m_fX -= c_vector3.m_fX; + m_fY -= c_vector3.m_fY; + m_fZ -= c_vector3.m_fZ; + return *this; + } + + inline CVector3& operator*=(Real f_value) { + m_fX *= f_value; + m_fY *= f_value; + m_fZ *= f_value; + return *this; + } + + inline CVector3& operator/=(Real f_value) { + m_fX /= f_value; + m_fY /= f_value; + m_fZ /= f_value; + return *this; + } + + inline CVector3 operator+(const CVector3& c_vector3) const { + CVector3 cResult(*this); + cResult += c_vector3; + return cResult; + } + + inline CVector3 operator-(const CVector3& c_vector3) const { + CVector3 cResult(*this); + cResult -= c_vector3; + return cResult; + } + + inline CVector3 operator*(Real f_value) const { + CVector3 cResult(*this); + cResult *= f_value; + return cResult; + } + + inline CVector3 operator/(const Real f_value) const { + CVector3 cResult(*this); + cResult /= f_value; + return cResult; + } + + inline friend CVector3 operator*(Real f_value, + const CVector3& c_vector3) { + return c_vector3 * f_value; + } + + inline friend std::ostream& operator<<(std::ostream& os, + const CVector3& c_vector3) { + os << c_vector3.m_fX << "," + << c_vector3.m_fY << "," + << c_vector3.m_fZ; + return os; + } + + inline friend std::istream& operator>>(std::istream& is, + CVector3& c_vector3) { + Real fValues[3]; + ParseValues(is, 3, fValues, ','); + c_vector3.Set(fValues[0], fValues[1], fValues[2]); + return is; + } + + private: + + Real m_fX; + Real m_fY; + Real m_fZ; + + }; + + /****************************************/ + /****************************************/ + + inline Real SquareDistance(const CVector3& c_v1, const CVector3& c_v2) { + return (c_v1 - c_v2).SquareLength(); + } + + inline Real Distance(const CVector3& c_v1, const CVector3& c_v2) { + return (c_v1 - c_v2).Length(); + } + +/****************************************/ +/****************************************/ + +} + +#endif diff --git a/src/core/utility/plugins/dynamic_loading.cpp b/src/core/utility/plugins/dynamic_loading.cpp new file mode 100644 index 00000000..defe0c09 --- /dev/null +++ b/src/core/utility/plugins/dynamic_loading.cpp @@ -0,0 +1,65 @@ +/** + * @file argos3/core/utility/plugins/dynamic_loading.h + * + * @author Carlo Pinciroli - + */ + +#include "dynamic_loading.h" +#include +#include + +namespace argos { + + /****************************************/ + /****************************************/ + + CDynamicLoading::~CDynamicLoading() { + for(TDLHandleMap::iterator it = m_tOpenLibs.begin(); + it != m_tOpenLibs.end(); + ++it) { + UnloadLibrary(it->second); + } + } + + /****************************************/ + /****************************************/ + + CDynamicLoading::TDLHandle CDynamicLoading::LoadLibrary(const std::string& str_lib) { + TDLHandle tHandle = ::dlopen(str_lib.c_str(), RTLD_NOW); + if(tHandle == NULL) { + THROW_ARGOSEXCEPTION("Can't load library \"" + << str_lib + << "\": " + << ::dlerror()); + } + m_tOpenLibs[str_lib] = tHandle; + return tHandle; + } + + /****************************************/ + /****************************************/ + + void CDynamicLoading::UnloadLibrary(const std::string& str_lib) { + TDLHandleMap::iterator it = m_tOpenLibs.find(str_lib); + if(it != m_tOpenLibs.end()) { + UnloadLibrary(it->second); + } + else { + THROW_ARGOSEXCEPTION("Can't load library \"" + << str_lib + << "\": library does not appear to have been loaded."); + } + } + + /****************************************/ + /****************************************/ + + void CDynamicLoading::UnloadLibrary(TDLHandle t_lib) { + ::dlclose(t_lib); + } + + /****************************************/ + /****************************************/ + +} + diff --git a/src/core/utility/plugins/dynamic_loading.h b/src/core/utility/plugins/dynamic_loading.h new file mode 100644 index 00000000..a62992e5 --- /dev/null +++ b/src/core/utility/plugins/dynamic_loading.h @@ -0,0 +1,74 @@ +/** + * @file argos3/core/utility/plugins/dynamic_loading.h + * + * @author Carlo Pinciroli - + */ + +#ifndef DYNAMIC_LOADING_H +#define DYNAMIC_LOADING_H + +#include +#include + +namespace argos { + + /** + * Allows users to dynamically load shared libraries + */ + class CDynamicLoading { + + public: + + /** + * The handle to a loaded library + */ + typedef void* TDLHandle; + + public: + + /** + * Class constructor + */ + CDynamicLoading() {} + + /** + * Class destructor + */ + ~CDynamicLoading(); + + /** + * Loads a dynamic library + * @param str_lib The path of the dynamic library to load + * @return A handle to the loaded dynamic library + */ + TDLHandle LoadLibrary(const std::string& str_lib); + + /** + * Unloads a dynamic library + * @param str_lib The path of the dynamic library to unload + */ + void UnloadLibrary(const std::string& str_lib); + + /** + * Unloads a dynamic library + * @param t_lib The handle to the dynamic library to unload + */ + void UnloadLibrary(TDLHandle t_lib); + + private: + + /** + * A type definition of the map of loaded dynamic libraries + */ + typedef std::map TDLHandleMap; + + /** + * The map of loaded dynamic libraries + */ + TDLHandleMap m_tOpenLibs; + + }; + +} + +#endif diff --git a/src/core/utility/plugins/factory.h b/src/core/utility/plugins/factory.h new file mode 100644 index 00000000..84a642fc --- /dev/null +++ b/src/core/utility/plugins/factory.h @@ -0,0 +1,160 @@ +/** + * @file argos3/core/utility/plugins/factory.h + * + * @brief This file defines the basic factory template. + + *

This template is very versatile and allows you to create your own factory + * with just a couple of lines of code. + * + *

Let us say that you have a class hierarchy such as: + * + *

+ *   class MyClass { ... };
+ *   class MyFirstDerivate : public MyClass { ... };
+ *   class MySecondDerivate : public MyClass { ... };
+ * 
+ * + * and you want to register MyFirstDerivate and + * MySecondDerivate. In this case, it is enough to add to after the + * definition of class MyClass: + * + *
+ *   typedef CFactory TFactoryMyClass;
+ *   #define REGISTER_MYCLASS(CLASSNAME, LABEL) REGISTER_SYMBOL(MyClass, CLASSNAME, LABEL)
+ * 
+ * + * Then, in your .cpp files that implement MyFirstDerivate and + * MySecondDerivate, add: + * + *
+ *   REGISTER_MYCLASS(MyFirstDerivate, "my_first_derivate")
+ *   REGISTER_MYCLASS(MySecondDerivate, "my_second_derivate")
+ * 
+ * + *

Now, if somewhere in your code you want to create an object of type + * MyFirstDerivate, just write: + * + *

+ *   MyFirstDerivate* x = TFactoryMyClass::New("my_first_derivate");
+ * 
+ * + * @author Carlo Pinciroli - + */ + +#ifndef FACTORY_H +#define FACTORY_H + +#include +#include +#include +#include +#include +#include + +namespace argos { + + /** + * Basic factory template + */ + template + class CFactory { + + public: + /** + * Pointer to the function that creates objects of type TYPE + */ + typedef TYPE* TCreator(); + + /** + * A struct containing the information about the registered types + */ + struct STypeInfo { + TCreator* Creator; + std::string Author; + std::string Version; + std::string BriefDescription; + std::string LongDescription; + std::string Status; + }; + + /** + * The map of registered TYPEs + */ + typedef std::map TTypeMap; + + public: + /** + * Registers a new TYPE creator in the factory + * @param str_label The label associated to the TYPE creator + * @param pc_creator The TYPE creator + */ + static void Register(const std::string& str_label, + const std::string& str_author, + const std::string& str_version, + const std::string& str_brief_desc, + const std::string& str_long_desc, + const std::string& str_status, + TCreator* pc_creator); + /** + * Creates a new object of type TYPE + * @param str_label The label of the TYPE to create + * @return A new object of type TYPE + */ + static TYPE* New(const std::string& str_label); + /** + * Prints the list of registered labels + * @param c_os The std::ostream to write into + */ + static void Print(std::ostream& c_os); + + /** + * Creates and returns the TYPE map + * @return The TYPE map + */ + static TTypeMap& GetTypeMap(); + }; + +/* + * Include the actual template implementation + */ +#include + +/** + * Macro to register a symbol into the factory + * @param BASECLASS The base class of the hiearchy of types to register (corresponds to TYPE in the factory) + * @param CLASSNAME The actual class to register + * @param LABEL The label to associate to CLASSNAME + */ +#define REGISTER_SYMBOL(BASECLASS, \ + CLASSNAME, \ + LABEL, \ + AUTHOR, \ + VERSION, \ + BRIEF_DESCRIPTION, \ + LONG_DESCRIPTION, \ + STATUS) \ + namespace argos { \ + extern "C" { \ + BASECLASS* CLASSNAME ## Creator() { \ + return new CLASSNAME; \ + } \ + } \ + class C ## CLASSNAME ## Proxy { \ + public: \ + C ## CLASSNAME ## Proxy() { \ + CFactory:: \ + Register(LABEL, \ + AUTHOR, \ + VERSION, \ + BRIEF_DESCRIPTION, \ + LONG_DESCRIPTION, \ + STATUS, \ + CLASSNAME ## Creator); \ + } \ + }; \ + C ## CLASSNAME ## Proxy CLASSNAME ## _p; \ + } + +} + +#endif diff --git a/src/core/utility/plugins/factory_impl.h b/src/core/utility/plugins/factory_impl.h new file mode 100644 index 00000000..e8bae390 --- /dev/null +++ b/src/core/utility/plugins/factory_impl.h @@ -0,0 +1,73 @@ +/** + * @author Carlo Pinciroli - + */ + +#ifndef FACTORY_IMPL_H +#define FACTORY_IMPL_H + +/****************************************/ +/****************************************/ + +template +typename CFactory::TTypeMap& CFactory::GetTypeMap() { + static typename CFactory::TTypeMap tTypeMap; + return tTypeMap; +} + +/****************************************/ +/****************************************/ + +template +void CFactory::Register(const std::string& str_label, + const std::string& str_author, + const std::string& str_version, + const std::string& str_brief_desc, + const std::string& str_long_desc, + const std::string& str_status, + TCreator* pc_creator) { + LOG << "Registered symbol \"" + << str_label + << "\"" + << std::endl; + typename CFactory::STypeInfo* psTypeInfo = new CFactory::STypeInfo; + psTypeInfo->Author = str_author; + psTypeInfo->Version = str_version; + psTypeInfo->BriefDescription = str_brief_desc; + psTypeInfo->LongDescription = str_long_desc; + psTypeInfo->Status = str_status; + psTypeInfo->Creator = pc_creator; + GetTypeMap()[str_label] = psTypeInfo; +} + +/****************************************/ +/****************************************/ + +template +TYPE* CFactory::New(const std::string& str_label) { + typename TTypeMap::iterator it = GetTypeMap().find(str_label); + if(it != GetTypeMap().end()) { + return it->second->Creator(); + } + else { + THROW_ARGOSEXCEPTION("Symbol \"" << str_label << "\" not found"); + } +} + +/****************************************/ +/****************************************/ + +template +void CFactory::Print(std::ostream& c_os) { + typename TTypeMap::iterator it; + c_os << "Symbols:" << std::endl; + for(it = GetTypeMap().begin(); + it != GetTypeMap().end(); + ++it) { + c_os << it->first << " (" << it->second->BriefDescription << ")" << std::endl; + } +} + +/****************************************/ +/****************************************/ + +#endif diff --git a/src/core/utility/profiler/profiler.cpp b/src/core/utility/profiler/profiler.cpp new file mode 100644 index 00000000..9ce4193d --- /dev/null +++ b/src/core/utility/profiler/profiler.cpp @@ -0,0 +1,291 @@ +/** + * @file argos3/core/utility/profiler/profiler.cpp + * + * @author Carlo Pinciroli - + */ + +#include "profiler.h" +#include +#include + +namespace argos { + + /****************************************/ + /****************************************/ + + static double TV2Sec(const ::timeval& t_timeval) { + return + static_cast(t_timeval.tv_sec) + + static_cast(t_timeval.tv_usec) * 0.000001; + } + + /****************************************/ + /****************************************/ + + static double CPUUsage(const ::rusage& t_rusage, + double f_elapsed_time) { + return 100.0 * + (TV2Sec(t_rusage.ru_utime) + + TV2Sec(t_rusage.ru_stime)) / + f_elapsed_time; + } + + /****************************************/ + /****************************************/ + +#define DUMP_RUSAGE_FIELD_HUMAN_READABLE(MSG, FIELD) c_os << MSG ": " << t_resources.ru_ ## FIELD << std::endl; + void DumpResourceUsageHumanReadable(std::ostream& c_os, + const ::rusage& t_resources) { + double fUserTime = TV2Sec(t_resources.ru_utime); + c_os << "User time: " << fUserTime << std::endl; + double fSysTime = TV2Sec(t_resources.ru_stime); + c_os << "System time: " << fSysTime << std::endl; + DUMP_RUSAGE_FIELD_HUMAN_READABLE("Maximum resident set size", maxrss); + DUMP_RUSAGE_FIELD_HUMAN_READABLE("Integral shared memory size", ixrss); + DUMP_RUSAGE_FIELD_HUMAN_READABLE("Integral unshared data size", idrss); + DUMP_RUSAGE_FIELD_HUMAN_READABLE("Integral unshared stack size", isrss); + DUMP_RUSAGE_FIELD_HUMAN_READABLE("Page reclaims", minflt); + DUMP_RUSAGE_FIELD_HUMAN_READABLE("Page faults", majflt); + DUMP_RUSAGE_FIELD_HUMAN_READABLE("Swaps", nswap); + DUMP_RUSAGE_FIELD_HUMAN_READABLE("Block input operations", inblock); + DUMP_RUSAGE_FIELD_HUMAN_READABLE("Block output operations", oublock); + DUMP_RUSAGE_FIELD_HUMAN_READABLE("Messages sent", msgsnd); + DUMP_RUSAGE_FIELD_HUMAN_READABLE("Messages received", msgrcv); + DUMP_RUSAGE_FIELD_HUMAN_READABLE("Signals received", nsignals); + DUMP_RUSAGE_FIELD_HUMAN_READABLE("Voluntary context switches", nvcsw); + DUMP_RUSAGE_FIELD_HUMAN_READABLE("Involuntary context switches", nivcsw); + } + + /****************************************/ + /****************************************/ + +#define DUMP_RUSAGE_FIELD_TABLE_ROW(FIELD) c_os << " " << t_resources.ru_ ## FIELD; + void DumpResourceUsageAsTableRow(std::ostream& c_os, + const ::rusage& t_resources) { + double fUserTime = TV2Sec(t_resources.ru_utime); + c_os << fUserTime; + double fSysTime = TV2Sec(t_resources.ru_stime); + c_os << " " << fSysTime; + DUMP_RUSAGE_FIELD_TABLE_ROW(maxrss); + DUMP_RUSAGE_FIELD_TABLE_ROW(ixrss); + DUMP_RUSAGE_FIELD_TABLE_ROW(idrss); + DUMP_RUSAGE_FIELD_TABLE_ROW(isrss); + DUMP_RUSAGE_FIELD_TABLE_ROW(minflt); + DUMP_RUSAGE_FIELD_TABLE_ROW(majflt); + DUMP_RUSAGE_FIELD_TABLE_ROW(nswap); + DUMP_RUSAGE_FIELD_TABLE_ROW(inblock); + DUMP_RUSAGE_FIELD_TABLE_ROW(oublock); + DUMP_RUSAGE_FIELD_TABLE_ROW(msgsnd); + DUMP_RUSAGE_FIELD_TABLE_ROW(msgrcv); + DUMP_RUSAGE_FIELD_TABLE_ROW(nsignals); + DUMP_RUSAGE_FIELD_TABLE_ROW(nvcsw); + DUMP_RUSAGE_FIELD_TABLE_ROW(nivcsw); + } + + /****************************************/ + /****************************************/ + +#define DIFF_RUSAGE_FIELD(FIELD) tResult.ru_ ## FIELD = t_resource1.ru_ ## FIELD - t_resource2.ru_ ## FIELD; + ::rusage operator-(const ::rusage& t_resource1, + const ::rusage& t_resource2) { + ::rusage tResult; + DIFF_RUSAGE_FIELD(utime.tv_sec); + DIFF_RUSAGE_FIELD(utime.tv_usec); + DIFF_RUSAGE_FIELD(stime.tv_sec); + DIFF_RUSAGE_FIELD(stime.tv_usec); + DIFF_RUSAGE_FIELD(maxrss); + DIFF_RUSAGE_FIELD(ixrss); + DIFF_RUSAGE_FIELD(idrss); + DIFF_RUSAGE_FIELD(isrss); + DIFF_RUSAGE_FIELD(minflt); + DIFF_RUSAGE_FIELD(majflt); + DIFF_RUSAGE_FIELD(nswap); + DIFF_RUSAGE_FIELD(inblock); + DIFF_RUSAGE_FIELD(oublock); + DIFF_RUSAGE_FIELD(msgsnd); + DIFF_RUSAGE_FIELD(msgrcv); + DIFF_RUSAGE_FIELD(nsignals); + DIFF_RUSAGE_FIELD(nvcsw); + DIFF_RUSAGE_FIELD(nivcsw); + return tResult; + } + + /****************************************/ + /****************************************/ + + CProfiler::CProfiler(const std::string& str_file_name, + bool b_trunc) { + if(b_trunc) { + m_cOutFile.open(str_file_name.c_str(), + std::ios::trunc | std::ios::out); + } + else { + m_cOutFile.open(str_file_name.c_str(), + std::ios::app | std::ios::out); + } + LOG << "Opened file \"" << str_file_name << "\" for profiling." << std::endl; + int nError = pthread_mutex_init(&m_tThreadResourceUsageMutex, NULL); + if(nError) { + THROW_ARGOSEXCEPTION("Error creating thread profiler mutex " << ::strerror(nError)); + } + } + + /****************************************/ + /****************************************/ + + CProfiler::~CProfiler() { + m_cOutFile.close(); + pthread_mutex_destroy(&m_tThreadResourceUsageMutex); + } + + /****************************************/ + /****************************************/ + + void CProfiler::Start() { + StartWallClock(); + StartCPUProfiler(); + } + + /****************************************/ + /****************************************/ + + void CProfiler::Stop() { + StopCPUProfiler(); + StopWallClock(); + } + + /****************************************/ + /****************************************/ + + void CProfiler::Flush(bool b_human_readable) { + if(b_human_readable) { + FlushHumanReadable(); + } + else { + FlushAsTable(); + } + } + + /****************************************/ + /****************************************/ + + void CProfiler::CollectThreadResourceUsage() { + pthread_mutex_lock(&m_tThreadResourceUsageMutex); + ::rusage tResourceUsage; +#ifdef __APPLE__ + /* FIXME: This is badly broken, but I don't know how to obtain + the equivalent of RUSAGE_THREAD on MacOS. --tc */ + getrusage(RUSAGE_SELF, &tResourceUsage); +#else + ::getrusage(::RUSAGE_THREAD, &tResourceUsage); +#endif + m_vecThreadResourceUsage.push_back(tResourceUsage); + pthread_mutex_unlock(&m_tThreadResourceUsageMutex); + } + + /****************************************/ + /****************************************/ + + void CProfiler::FlushHumanReadable() { + m_cOutFile << "[profiled portion overall]" << std::endl << std::endl; + double fStartTime = TV2Sec(m_tWallClockStart); + double fEndTime = TV2Sec(m_tWallClockEnd); + double fElapsedTime = fEndTime - fStartTime; + m_cOutFile << "Wall clock time: " << fElapsedTime << std::endl; + ::rusage tDiffResourceUsage = m_tResourceUsageEnd - m_tResourceUsageStart; + m_cOutFile << "CPU usage: " << CPUUsage(tDiffResourceUsage, fElapsedTime) << "%" << std::endl; + DumpResourceUsageHumanReadable(m_cOutFile, tDiffResourceUsage); + m_cOutFile << std::endl << "[profiled portion start]" << std::endl << std::endl; + DumpResourceUsageHumanReadable(m_cOutFile, m_tResourceUsageStart); + m_cOutFile << std::endl << "[profiled portion end]" << std::endl << std::endl; + DumpResourceUsageHumanReadable(m_cOutFile, m_tResourceUsageEnd); + m_cOutFile << std::endl << "[process overall]" << std::endl << std::endl; + ::rusage tResourceUsage; +#ifdef __APPLE__ + getrusage(RUSAGE_SELF, &tResourceUsage); +#else + ::getrusage(::RUSAGE_SELF, &tResourceUsage); +#endif + DumpResourceUsageHumanReadable(m_cOutFile, tResourceUsage); + if(! m_vecThreadResourceUsage.empty()) { + for(size_t i = 0; i < m_vecThreadResourceUsage.size(); ++i) { + m_cOutFile << std::endl << "[thread #" << i << " overall]" << std::endl << std::endl; + m_cOutFile << "CPU usage: " << CPUUsage(m_vecThreadResourceUsage[i], fElapsedTime) << "%" << std::endl; + DumpResourceUsageHumanReadable(m_cOutFile, m_vecThreadResourceUsage[i]); + } + } + } + + /****************************************/ + /****************************************/ + + void CProfiler::FlushAsTable() { + double fStartTime = TV2Sec(m_tWallClockStart); + double fEndTime = TV2Sec(m_tWallClockEnd); + double fElapsedTime = fEndTime - fStartTime; + ::rusage tDiffResourceUsage = m_tResourceUsageEnd - m_tResourceUsageStart; + m_cOutFile << "ProfPortion " << fElapsedTime << " " + << CPUUsage(tDiffResourceUsage, fElapsedTime) << " "; + DumpResourceUsageAsTableRow(m_cOutFile, tDiffResourceUsage); + m_cOutFile << std::endl << "PortionStart 0 0 "; + DumpResourceUsageAsTableRow(m_cOutFile, m_tResourceUsageStart); + m_cOutFile << std::endl << "PortionEnd 0 0 "; + DumpResourceUsageAsTableRow(m_cOutFile, m_tResourceUsageEnd); + m_cOutFile << std::endl << "Overall 0 0 "; + ::rusage tResourceUsage; +#ifdef __APPLE__ + getrusage(RUSAGE_SELF, &tResourceUsage); +#else + ::getrusage(::RUSAGE_SELF, &tResourceUsage); +#endif + DumpResourceUsageAsTableRow(m_cOutFile, tResourceUsage); + if(! m_vecThreadResourceUsage.empty()) { + for(size_t i = 0; i < m_vecThreadResourceUsage.size(); ++i) { + m_cOutFile << std::endl << "thread_" << i << " 0 " + << CPUUsage(m_vecThreadResourceUsage[i], fElapsedTime) << " "; + DumpResourceUsageAsTableRow(m_cOutFile, m_vecThreadResourceUsage[i]); + } + } + m_cOutFile << std::endl; + } + + /****************************************/ + /****************************************/ + + void CProfiler::StartWallClock() { + ::gettimeofday(&m_tWallClockStart, NULL); + } + + /****************************************/ + /****************************************/ + + void CProfiler::StopWallClock() { + ::gettimeofday(&m_tWallClockEnd, NULL); + } + + /****************************************/ + /****************************************/ + + void CProfiler::StartCPUProfiler() { +#ifdef __APPLE__ + getrusage(RUSAGE_SELF, &m_tResourceUsageStart); +#else + ::getrusage(::RUSAGE_SELF, &m_tResourceUsageStart); +#endif + } + + /****************************************/ + /****************************************/ + + void CProfiler::StopCPUProfiler() { +#ifdef __APPLE__ + getrusage(RUSAGE_SELF, &m_tResourceUsageEnd); +#else + ::getrusage(::RUSAGE_SELF, &m_tResourceUsageEnd); +#endif + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/utility/profiler/profiler.h b/src/core/utility/profiler/profiler.h new file mode 100644 index 00000000..c790b78a --- /dev/null +++ b/src/core/utility/profiler/profiler.h @@ -0,0 +1,58 @@ +/** + * @file argos3/core/utility/profiler/profiler.h + * + * @author Carlo Pinciroli - + */ +#ifndef PROFILER_H +#define PROFILER_H + +#include +#include +#include + +#include +#include +#include +#include + +namespace argos { + + class CProfiler { + + public: + + CProfiler(const std::string& str_file_name, + bool b_trunc=true); + ~CProfiler(); + + void Start(); + void Stop(); + void Flush(bool b_human_readable); + void CollectThreadResourceUsage(); + + private: + + void StartWallClock(); + void StopWallClock(); + + void StartCPUProfiler(); + void StopCPUProfiler(); + + void FlushHumanReadable(); + void FlushAsTable(); + + private: + + std::ofstream m_cOutFile; + ::timeval m_tWallClockStart; + ::timeval m_tWallClockEnd; + ::rusage m_tResourceUsageStart; + ::rusage m_tResourceUsageEnd; + std::vector< ::rusage > m_vecThreadResourceUsage; + pthread_mutex_t m_tThreadResourceUsageMutex; + + }; + +} + +#endif diff --git a/src/core/utility/string_utilities.cpp b/src/core/utility/string_utilities.cpp new file mode 100644 index 00000000..f9856e59 --- /dev/null +++ b/src/core/utility/string_utilities.cpp @@ -0,0 +1,158 @@ +/* + * 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; version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @file argos3/core/utility/string_utilities.cpp + * + * @brief This file provides the implementation of + * + * @author Carlo Pinciroli - + * @author Vito Trianni - + */ + +#include "string_utilities.h" +#include +#include + +namespace argos { + + void Tokenize(const std::string& str_string, + std::vector& vec_tokens, + const std::string& str_delimiters) { + // this taken from + // http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html + + /* Skip delimiters at beginning */ + std::string::size_type lastPos = + str_string.find_first_not_of(str_delimiters, 0); + + /* Find first "non-delimiter" */ + std::string::size_type pos = str_string.find_first_of(str_delimiters, + lastPos); + + while(std::string::npos != pos || std::string::npos != lastPos) { + /* Found a token, add it to the vector */ + vec_tokens.push_back(str_string.substr(lastPos, pos - lastPos)); + + /* Skip delimiters. Note the "not_of" */ + lastPos = str_string.find_first_not_of(str_delimiters, pos); + + /* Find next "non-delimiter" */ + pos = str_string.find_first_of(str_delimiters, lastPos); + } + } + + /****************************************/ + /****************************************/ + + std::string StringToUpperCase(const std::string& str_string) { + char* buf = new char[str_string.length()]; + str_string.copy(buf, str_string.length()); + + for(unsigned int i = 0; i < str_string.length(); ++i) + buf[i] = toupper(buf[i]); + + std::string r(buf, str_string.length()); + + delete[] buf; + + return r; + } + + /****************************************/ + /****************************************/ + + std::string StringToLowerCase(const std::string& str_string) { + char* buf = new char[str_string.length()]; + str_string.copy(buf, str_string.length()); + + for(unsigned int i = 0; i < str_string.length(); ++i) + buf[i] = tolower(buf[i]); + + std::string r(buf, str_string.length()); + + delete[] buf; + + return r; + } + + /****************************************/ + /****************************************/ + + std::string ExpandARGoSInstallDir(const std::string& str_buffer) { + std::string strRetValue(str_buffer); + /* Look for the variable in the string */ + size_t unPos = strRetValue.find("$ARGOSINSTALLDIR"); + if(unPos != std::string::npos) { + /* The string has been found, + replace it with the actual value of the variable */ + strRetValue.replace(unPos, 16, ::getenv("ARGOSINSTALLDIR")); + } + return strRetValue; + } + + /****************************************/ + /****************************************/ + + void Replace(std::string& str_buffer, + const std::string& str_original, + const std::string& str_new) { + /* Start from the beginning of the string */ + size_t unPos = 0; + do { + /* Look for the substring to replace */ + unPos = str_buffer.find(str_original, unPos); + /* Has it been found? */ + if(unPos != std::string::npos) { + /* Yes, it has been found */ + /* Replace the substring with the new one */ + str_buffer.replace(unPos, str_original.length(), str_new); + /* Update unPos so that it point past the end of the replaced portion */ + unPos += str_new.length(); + /* Make sure unPos does not exceed the string length */ + if(unPos >= str_buffer.length()) { + unPos = std::string::npos; + } + } + /* Continue until the original string has been found */ + } while(unPos != std::string::npos); + } + + /****************************************/ + /****************************************/ + + bool MatchPattern(const std::string& str_input, + const std::string str_pattern) { + /* Code taken from + http://www.opengroup.org/onlinepubs/000095399/functions/regexec.html + */ + UInt32 nStatus; + regex_t tRegExp; + + if(regcomp(&tRegExp, str_pattern.c_str(), REG_EXTENDED | REG_NOSUB) != 0) { + return false; + } + nStatus = regexec(&tRegExp, str_input.c_str(), 0, NULL, 0); + regfree(&tRegExp); + if (nStatus != 0) { + return false; + } + return true; + } + + /****************************************/ + /****************************************/ + +} diff --git a/src/core/utility/string_utilities.h b/src/core/utility/string_utilities.h new file mode 100644 index 00000000..462bd475 --- /dev/null +++ b/src/core/utility/string_utilities.h @@ -0,0 +1,193 @@ +/* + * 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; version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @file argos3/core/utility/string_utilities.h + * + * This file provides the definition of some useful string-related utilities. + * + * @author Carlo Pinciroli - + */ + +#ifndef STRING_UTILITIES_H +#define STRING_UTILITIES_H + +#include +#include +#include +#include +#include + +namespace argos { + + /****************************************/ + /****************************************/ + + /** + * Converts the given parameter to a std::string. + * Example of use: + *
+    * UInt32 unToBeConverted(22);
+    * std::string strConverted = ToString(unToBeConverted);
+    * 
+ * Internally uses an std::ostringstream. If you want to use it with a custom + * type, you must define its std::ostream& operator<<(std::ostream&, type). + * @tparam T the type of the parameter to convert. + * @param t_value the parameter to convert. + * @return the std::string. + */ + template std::string ToString(const T& t_value) { + std::ostringstream ss; + ss.setf(std::ios::boolalpha); + ss << t_value; + return ss.str(); + } + + /****************************************/ + /****************************************/ + + /** + * Converts the given std::string parameter to the wanted type. + * Example of use: + *
+    * std::string strToBeConverted("36");
+    * UInt32 unConverted = FromString(strToBeConverted);
+    * 
+ * Internally uses an std::ostringstream. If you want to use it with a custom + * type, you must define its std::istream& operator>>(std::istream&, type). + * @tparam T the type of the target parameter. + * @param str_value the string to convert. + * @return the converted value. + */ + template T FromString(const std::string& str_value) { + T tReturnValue; + std::istringstream ss(str_value); + ss.setf(std::ios::boolalpha); + ss >> tReturnValue; + return tReturnValue; + } + + /****************************************/ + /****************************************/ + + template void ParseValues(std::istream& str_input, + UInt32 un_num_fields, + T* pt_field_buffer, + const char ch_delimiter = '\n') { + std::vector s(un_num_fields); + UInt32 i = 0; + while(i < un_num_fields && std::getline(str_input, s[i], ch_delimiter)) { + i++; + } + if (i == un_num_fields) { + str_input.clear(); // the istream was read completely and this is fine, so set the flag to 'good' + for(i = 0; i < un_num_fields; i++) { + std::istringstream iss(s[i]); + iss >> pt_field_buffer[i]; + } + } + else { + THROW_ARGOSEXCEPTION("Parse error: expected " << un_num_fields + << " values, but " << i << " have been found in \"" + << str_input << "\""); + } + } + + /****************************************/ + /****************************************/ + + template void ParseValues(const std::string& str_input, + const UInt32 un_num_fields, + T* pt_field_buffer, + const char ch_delimiter = '\n') { + std::istringstream issInput(str_input); + ParseValues(issInput, un_num_fields, pt_field_buffer, ch_delimiter); + } + + /****************************************/ + /****************************************/ + + extern "C" { + + /** + * Tokenizes the given string according to the wanted delimiters (by default just a " "). + * @param str_string the string to tokenize. + * @param vec_tokens the vector to fill with tokens. + * @param str_delimiters the delimiters to use. + */ + void Tokenize(const std::string& str_string, + std::vector& vec_tokens, + const std::string& str_delimiters = " "); + + /** + * Converts a string to upper case. + * @param str_string the string to convert. + * @return the upper case string. + */ + std::string StringToUpperCase(const std::string& str_string); + + /** + * Converts a string to lower case. + * @param str_string the string to convert. + * @return the lower case string. + */ + std::string StringToLowerCase(const std::string& str_string); + + /****************************************/ + /****************************************/ + + /** + * Replaces all occurrences of the string $ARGOSINSTALLDIR into its actual value + * set in the environment. + * @param str_buffer the string to search and replace. + * @return a new string where $ARGOSINSTALLDIR is substituted by its actual value. + */ + std::string ExpandARGoSInstallDir(const std::string& str_buffer); + + /****************************************/ + /****************************************/ + + /** + * Searches into str_buffer for occurrences of str_original and substitutes them + * with str_new. + * @param str_buffer the string to modify. + * @param str_original the string to search for. + * @param str_new the substitute string. + */ + void Replace(std::string& str_buffer, + const std::string& str_original, + const std::string& str_new); + + /****************************************/ + /****************************************/ + + /** + * Returns true if str_pattern is matched by str_input. + * Internally uses the C function regcomp(). + * @param str_input the input string to analyze. + * @param str_pattern the extended regular expression to match. + * @return true if str_pattern is matched by str_input. + */ + bool MatchPattern(const std::string& str_input, + const std::string str_pattern); + + /****************************************/ + /****************************************/ + + } + +} + +#endif