commit ea96ad9c852b427814621b68fc30d811e8210b87
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Mon, 12 Feb 2018 17:23:24 +0100
Initial commit.
Diffstat:
25 files changed, 4858 insertions(+), 0 deletions(-)
diff --git a/COPYING b/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/README.md b/README.md
@@ -0,0 +1,29 @@
+# StarEnclosures
+
+The purpose of this library is to extract enclosures from raw geometry. An
+enclosure is a set of triangles enclosing a given volume. The library manages
+vertices and triangles duplicates, easing the scene definition process. It also
+checks some coherency properties, most noticeably the enclosed medium unicity.
+
+## How to build
+
+StarEnclosures relies on the [CMake](http://www.cmake.org) and the
+[RCMake](https://gitlab.com/vaplv/rcmake/) package to build. It also depends on
+the [RSys](https://gitlab.com/vaplv/rsys/) and
+[Star-3D](https://gitlab.com/meso-star/star-3d/) and
+[Star-SP](https://gitlab.com/meso-star/star-sp/) libraries, the later being
+necessary only when building tests.
+
+First ensure that CMake and a C compiler are installed on your system. Then
+install the RCMake package as well as all the aforementioned prerequisites.
+Finally generate the project from the `cmake/CMakeLists.txt` file by appending
+to the `CMAKE_PREFIX_PATH` variable the install directories of its
+dependencies.
+
+## License
+
+StarEnclosures is Copyright (C) |Meso|Star> 2018 (<contact@meso-star.com>).
+It is free software released under the GPLv3+ license. You are welcome to
+redistribute it under certain conditions; refer to the COPYING files for
+details.
+
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -0,0 +1,133 @@
+# Copyright (C) |Meso|Star> 2016-2018
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+cmake_minimum_required(VERSION 3.0)
+project(Star-Enclosures C)
+enable_testing()
+
+set(SENC_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src)
+option(NO_TEST "Do not build tests" OFF)
+
+################################################################################
+# Check dependencies
+################################################################################
+find_package(RCMake 0.4 REQUIRED)
+find_package(Star3D 0.5 REQUIRED)
+find_package(RSys 0.6.1 REQUIRED)
+
+if(NOT NO_TEST)
+find_package(StarSP 0.7 REQUIRED)
+endif()
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR})
+include(rcmake)
+include(rcmake_runtime)
+
+################################################################################
+# Configure and define targets
+################################################################################
+set(VERSION_MAJOR 0)
+set(VERSION_MINOR 1)
+set(VERSION_PATCH 0)
+set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
+
+set(SENC_FILES_SRC
+ senc_descriptor.c
+ senc_device.c
+ senc_enclosure.c
+ senc_scene.c
+ senc_scene_analyze.c)
+
+set(SENC_FILES_INC_API
+ senc.h
+ senc_s3d_wrapper.h)
+
+set(SENC_FILES_INC
+ senc_descriptor_c.h
+ senc_device_c.h
+ senc_enclosure_c.h
+ senc_enclosure_data.h
+ senc_internal_types.h
+ senc_scene_c.h
+ senc_scene_analyze_c.h)
+
+set(SENC_FILES_DOC COPYING README.md)
+
+# Prepend each file by `SENC_SOURCE_DIR'
+rcmake_prepend_path(SENC_FILES_SRC ${SENC_SOURCE_DIR})
+rcmake_prepend_path(SENC_FILES_INC ${SENC_SOURCE_DIR})
+rcmake_prepend_path(SENC_FILES_INC_API ${SENC_SOURCE_DIR})
+rcmake_prepend_path(SENC_FILES_DOC ${PROJECT_SOURCE_DIR}/../)
+
+add_library(senc SHARED
+ ${SENC_FILES_SRC}
+ ${SENC_FILES_INC}
+ ${SENC_FILES_INC_API})
+target_link_libraries(senc RSys Star3D)
+
+set_target_properties(senc PROPERTIES
+ DEFINE_SYMBOL SENC_SHARED_BUILD
+ VERSION ${VERSION}
+ SOVERSION ${VERSION_MAJOR})
+rcmake_copy_runtime_libraries(senc)
+
+if(CMAKE_COMPILER_IS_GNUCC)
+ set_target_properties(senc PROPERTIES LINK_FLAGS "-lm")
+endif()
+
+rcmake_setup_devel(senc StarEnc ${VERSION} senc_version.h)
+
+################################################################################
+# Add tests
+################################################################################
+if(NOT NO_TEST)
+ function(build_test _name)
+ add_executable(${_name}
+ ${SENC_SOURCE_DIR}/test_senc_utils.h
+ ${SENC_SOURCE_DIR}/${_name}.c)
+ target_link_libraries(${_name} RSys senc)
+ endfunction()
+
+ function(register_test _name)
+ add_test(${_name} ${ARGN})
+ rcmake_set_test_runtime_dirs(${_name} _runtime_dirs)
+ endfunction()
+
+ function(new_test _name)
+ build_test(${_name})
+ register_test(${_name} ${_name})
+ endfunction()
+
+ new_test(test_senc_cube_behind_cube)
+ new_test(test_senc_cube_in_cube)
+ new_test(test_senc_descriptor)
+ new_test(test_senc_device)
+ new_test(test_senc_enclosure)
+ new_test(test_senc_sample_enclosure)
+ new_test(test_senc_scene)
+
+ target_link_libraries(test_senc_sample_enclosure StarSP)
+ rcmake_copy_runtime_libraries(test_senc_sample_enclosure)
+endif()
+
+################################################################################
+# Define output & install directories
+################################################################################
+install(TARGETS senc
+ ARCHIVE DESTINATION bin
+ LIBRARY DESTINATION lib
+ RUNTIME DESTINATION bin)
+install(FILES ${SENC_FILES_INC_API} DESTINATION include/)
+install(FILES ${SENC_FILES_DOC} DESTINATION share/doc/star-enc)
diff --git a/src/senc.h b/src/senc.h
@@ -0,0 +1,222 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef SENC_H
+#define SENC_H
+
+#include <rsys/rsys.h>
+
+/* Library symbol management */
+#if defined(SENC_SHARED_BUILD)
+ #define SENC_API extern EXPORT_SYM
+#elif defined(SENC_STATIC_BUILD)
+ #define SENC_API extern LOCAL_SYM
+#else /* Use shared library */
+ #define SENC_API extern IMPORT_SYM
+#endif
+
+/* Helper macro that asserts if the invocation of the StarEnc function `Func'
+ * returns an error. One should use this macro on StarEnc function calls for
+ * which no explicit error checking is performed. */
+#ifndef NDEBUG
+ #define SENC(Func) ASSERT(senc_ ## Func == RES_OK)
+#else
+ #define SENC(Func) senc_ ## Func
+#endif
+
+/* Syntactic sugar used to inform the library that it can use as many threads
+ * as CPU cores */
+#define SENC_NTHREADS_DEFAULT (~0u)
+
+/* Forward declaration of external opaque data types */
+struct logger;
+struct mem_allocator;
+
+/* Forward declaration of StarEnclosures opaque data types. These data types
+ * are ref counted. Once created with the appropriated `senc_<TYPE>_create'
+ * function, the caller implicitly owns the created data, i.e. its reference
+ * counter is set to 1. The senc_<TYPE>_ref_<get|put> functions get or release
+ * a reference on the data, i.e. they increment or decrement the reference
+ * counter, respectively. When this counter reaches 0, the object is silently
+ * destroyed and cannot be used anymore. */
+struct senc_descriptor;
+struct senc_device;
+struct senc_scene;
+struct senc_enclosure;
+
+/* Enclosure header type */
+struct enclosure_header {
+ /* The ID of the enclosure; guaranteed to be compact */
+ unsigned enclosure_id;
+ /* Number of triangles; a triangle can be accounted for twice, once by side */
+ unsigned triangle_count;
+ /* Number of triangles; a triangle cannot be accounted for twice */
+ unsigned unique_triangle_count;
+ /* Number of vertices */
+ unsigned vertices_count;
+ /* The medium in the enclosure */
+ unsigned enclosed_medium;
+ /* Is the enclosure infinite? */
+ char is_infinite;
+};
+
+BEGIN_DECLS
+
+/*******************************************************************************
+ * StarEnclosures device. It is an handle toward the StarEnc library.
+ * It manages the lib resources.
+ ******************************************************************************/
+SENC_API res_T
+senc_device_create
+ (struct logger* logger, /* May be NULL <=> use default logger */
+ struct mem_allocator* allocator, /* May be NULL <=> use default allocator */
+ const unsigned nthreads_hint, /* Hint on the number of threads to use (UNUSED) */
+ const int verbose,
+ struct senc_device** device);
+
+SENC_API res_T
+senc_device_ref_get
+ (struct senc_device* device);
+
+SENC_API res_T
+senc_device_ref_put
+ (struct senc_device* device);
+
+/*******************************************************************************
+ * StarEnclosures scene. A scene is a collection of triangles. Each triangle is
+ * defined with a medium on each side.
+ ******************************************************************************/
+SENC_API res_T
+senc_scene_create
+ (struct senc_device* device,
+ const unsigned mediums_count,
+ struct senc_scene** scene);
+
+SENC_API res_T
+senc_scene_add_geometry
+ (struct senc_scene* scene,
+ const unsigned triangles_count,
+ void(*indices)(const unsigned itri, unsigned ids[3], void* context),
+ void(*mediums)(const unsigned itri, unsigned med[2], void* context),
+ const unsigned vertices_count,
+ void(*position)(const unsigned ivert, double pos[3], void* context),
+ void* context);
+
+SENC_API res_T
+senc_scene_analyze
+ (struct senc_scene* scene,
+ struct senc_descriptor** descriptor);
+
+SENC_API res_T
+senc_scene_ref_get
+ (struct senc_scene* scene);
+
+SENC_API res_T
+senc_scene_ref_put
+ (struct senc_scene* scene);
+
+/*******************************************************************************
+ * StarEnclosures descriptor. It is an handle toward an analyze result.
+ ******************************************************************************/
+SENC_API res_T
+senc_descriptor_get_enclosure_count
+ (const struct senc_descriptor* descriptor,
+ unsigned* count);
+
+SENC_API res_T
+senc_descriptor_get_enclosure
+ (struct senc_descriptor* descriptor,
+ const unsigned idx,
+ struct senc_enclosure** enclosure);
+
+SENC_API res_T
+senc_descriptor_get_global_triangle_count
+ (const struct senc_descriptor* descriptor,
+ unsigned* count);
+
+SENC_API res_T
+senc_descriptor_get_global_vertices_count
+ (const struct senc_descriptor* descriptor,
+ unsigned* count);
+
+SENC_API res_T
+senc_descriptor_get_global_indices
+ (const struct senc_descriptor* descriptor,
+ const unsigned itri,
+ unsigned indices[3]);
+
+SENC_API res_T
+senc_descriptor_get_global_vertices
+ (const struct senc_descriptor* descriptor,
+ const unsigned ivert,
+ double coord[3]);
+
+SENC_API res_T
+senc_descriptor_get_global_mediums
+ (const struct senc_descriptor* descriptor,
+ const unsigned itri,
+ unsigned mediums[2]);
+
+SENC_API res_T
+senc_descriptor_get_global_enclosures
+ (const struct senc_descriptor* descriptor,
+ const unsigned itri,
+ unsigned enclosures[2]);
+
+SENC_API res_T
+senc_descriptor_ref_get
+ (struct senc_descriptor* descriptor);
+
+SENC_API res_T
+senc_descriptor_ref_put
+ (struct senc_descriptor* descriptor);
+
+/*******************************************************************************
+* StarEnclosures enclosure. It is an handle toward an enclosure.
+******************************************************************************/
+SENC_API res_T
+senc_enclosure_get_header
+ (const struct senc_enclosure* enclosure,
+ const struct enclosure_header** header);
+
+SENC_API res_T
+senc_enclosure_get_indices
+ (const struct senc_enclosure* enclosure,
+ const unsigned itri,
+ unsigned indices[3]);
+
+SENC_API res_T
+senc_enclosure_get_vertices
+ (const struct senc_enclosure* enclosure,
+ const unsigned ivert,
+ double coord[3]);
+
+SENC_API res_T
+senc_enclosure_get_mediums
+ (const struct senc_enclosure* enclosure,
+ const unsigned itri,
+ unsigned medium[2]);
+
+SENC_API res_T
+senc_enclosure_ref_get
+ (struct senc_enclosure* enclosure);
+
+SENC_API res_T
+senc_enclosure_ref_put
+ (struct senc_enclosure* enclosure);
+
+END_DECLS
+
+#endif /* SENC_H */
diff --git a/src/senc_descriptor.c b/src/senc_descriptor.c
@@ -0,0 +1,224 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "senc_descriptor_c.h"
+#include "senc_device_c.h"
+#include "senc_enclosure_c.h"
+#include "senc_scene_c.h"
+#include "senc.h"
+
+#include <rsys/rsys.h>
+#include <rsys/double3.h>
+#include <rsys/mem_allocator.h>
+
+ /*******************************************************************************
+ * Helper function
+ ******************************************************************************/
+static void
+descriptor_release(ref_T * ref)
+{
+ struct senc_scene* scn = NULL;
+ struct senc_descriptor* desc = NULL;
+ ASSERT(ref);
+ desc = CONTAINER_OF(ref, struct senc_descriptor, ref);
+ scn = desc->scene;
+ darray_triangle_comp_release(&desc->triangles_comp);
+ darray_triangle_enc_release(&desc->triangles_enc);
+ darray_enclosure_release(&desc->enclosures);
+
+ MEM_RM(scn->dev->allocator, desc);
+ SENC(scene_ref_put(scn));
+}
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+struct senc_descriptor*
+descriptor_create(struct senc_scene* scn)
+{
+ struct senc_descriptor* desc;
+ ASSERT(scn);
+ desc = MEM_CALLOC(scn->dev->allocator, 1, sizeof(struct senc_descriptor));
+ if(desc) {
+ desc->scene = scn;
+ SENC(scene_ref_get(desc->scene));
+ darray_triangle_comp_init(scn->dev->allocator, &desc->triangles_comp);
+ darray_triangle_enc_init(scn->dev->allocator, &desc->triangles_enc);
+ /* Enclosure 0 is always defined for infinite */
+ darray_enclosure_init(scn->dev->allocator, &desc->enclosures);
+ darray_enclosure_resize(&desc->enclosures, 1);
+ desc->enclosures_count = 1;
+ desc->triangle_count = scn->nutris;
+ desc->vertices_count = scn->nuverts;
+ ref_init(&desc->ref);
+ }
+ return desc;
+}
+
+struct mem_allocator*
+ descriptor_get_allocator(struct senc_descriptor* desc)
+{
+ ASSERT(desc);
+ return desc->scene->dev->allocator;
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+senc_descriptor_get_enclosure_count
+ (const struct senc_descriptor* desc, unsigned* count)
+{
+ size_t tmp;
+ if(!desc || !count) return RES_BAD_ARG;
+ tmp = darray_enclosure_size_get(&desc->enclosures);
+ ASSERT(tmp < UINT_MAX); /* API type */
+ *count = (unsigned)tmp;
+ return RES_OK;
+}
+
+res_T
+senc_descriptor_get_enclosure
+ (struct senc_descriptor* desc,
+ const unsigned idx,
+ struct senc_enclosure** out_enc)
+{
+ struct senc_enclosure* enc;
+ if(!desc || idx >= darray_enclosure_size_get(&desc->enclosures) || !out_enc)
+ return RES_BAD_ARG;
+ enc =
+ enclosure_create(desc, darray_enclosure_data_get(&desc->enclosures) + idx);
+ if(!enc) return RES_MEM_ERR;
+ *out_enc = enc;
+ return RES_OK;
+}
+
+res_T
+senc_descriptor_get_global_triangle_count
+ (const struct senc_descriptor* desc,
+ unsigned* count)
+{
+ if(!desc || !count) return RES_BAD_ARG;
+ ASSERT(desc->triangle_count < UINT_MAX);
+ *count = (unsigned)desc->triangle_count; /* Back to API type */
+ return RES_OK;
+}
+
+res_T
+senc_descriptor_get_global_vertices_count
+ (const struct senc_descriptor* desc,
+ unsigned* count)
+{
+ if(!desc || !count) return RES_BAD_ARG;
+ if(desc->vertices_count >= UINT_MAX)
+ return RES_BAD_ARG;
+ *count = (unsigned)desc->vertices_count; /* Back to API type */
+ return RES_OK;
+}
+
+res_T
+senc_descriptor_get_global_indices
+ (const struct senc_descriptor* desc,
+ const unsigned itri,
+ unsigned indices[3])
+{
+ const struct triangle_in* trg;
+ int i;
+ if(!indices || ! desc
+ || itri >= darray_triangle_in_size_get(&desc->scene->triangles_in))
+ return RES_BAD_ARG;
+ trg = darray_triangle_in_cdata_get(&desc->scene->triangles_in) + itri;
+
+ FOR_EACH(i, 0, 3) {
+ ASSERT(trg->vertice_id[i] < UINT_MAX);
+ indices[i] = (unsigned)trg->vertice_id[i]; /* Back to API type */
+ }
+ return RES_OK;
+}
+
+res_T
+senc_descriptor_get_global_vertices
+ (const struct senc_descriptor* desc,
+ const unsigned ivert,
+ double vrtx[3])
+{
+ const union double3* v;
+ if(!vrtx || !desc
+ || ivert >= darray_triangle_in_size_get(&desc->scene->triangles_in))
+ return RES_BAD_ARG;
+
+ v = darray_position_cdata_get(&desc->scene->vertices) + ivert;
+ d3_set(vrtx, v->vec);
+ return RES_OK;
+}
+
+res_T
+senc_descriptor_get_global_mediums
+ (const struct senc_descriptor* desc,
+ const unsigned itri,
+ unsigned mediums[2])
+{
+ const struct triangle_in* trg;
+ int i;
+ if(!mediums || !desc
+ || itri >= darray_triangle_in_size_get(&desc->scene->triangles_in))
+ return RES_BAD_ARG;
+ trg = darray_triangle_in_cdata_get(&desc->scene->triangles_in) + itri;
+ FOR_EACH(i, 0, 2) {
+ ASSERT(trg->medium[i] < UINT_MAX);
+ mediums[i] = (unsigned)trg->medium[i]; /* Back to API type */
+ }
+ return RES_OK;
+}
+
+res_T
+senc_descriptor_get_global_enclosures
+ (const struct senc_descriptor* desc,
+ const unsigned itri,
+ unsigned enclosures[2])
+{
+ const struct triangle_comp* trg;
+ int i;
+ if(!enclosures || !desc
+ || itri >= darray_triangle_in_size_get(&desc->scene->triangles_in))
+ return RES_BAD_ARG;
+ trg = darray_triangle_comp_cdata_get(&desc->triangles_comp) + itri;
+
+ /* FIXME: trg->component is not what we need.
+ * We want an enclosure ID, not a component ID! */
+
+
+ FOR_EACH(i, 0, 2) {
+ ASSERT(trg->component[i] < UINT_MAX);
+ enclosures[i] = (unsigned)trg->component[i]; /* Back to API type */
+ }
+ return RES_OK;
+}
+
+res_T
+senc_descriptor_ref_get(struct senc_descriptor* desc)
+{
+ if(!desc) return RES_BAD_ARG;
+ ref_get(&desc->ref);
+ return RES_OK;
+}
+
+res_T
+senc_descriptor_ref_put(struct senc_descriptor* desc)
+{
+ if(!desc) return RES_BAD_ARG;
+ ref_put(&desc->ref, descriptor_release);
+ return RES_OK;
+}
diff --git a/src/senc_descriptor_c.h b/src/senc_descriptor_c.h
@@ -0,0 +1,95 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef SENC_DESCRIPTOR_C_H
+#define SENC_DESCRIPTOR_C_H
+
+#include <rsys/ref_count.h>
+#include <rsys/dynamic_array.h>
+
+#include "senc.h"
+#include "senc_enclosure_data.h"
+#include "senc_internal_types.h"
+
+struct senc_scene;
+struct mem_allocator;
+
+struct triangle_comp {
+ /* The connex component in which each side is. */
+ component_id_t component[2];
+};
+
+#ifndef NDEBUG
+static void
+triangle_comp_init(struct mem_allocator* alloc, struct triangle_comp* trg) {
+ int i;
+ (void)alloc;
+ ASSERT(trg);
+ FOR_EACH(i, 0, 2) trg->component[i] = COMPONENT_NULL__;
+}
+#define DARRAY_FUNCTOR_INIT triangle_comp_init
+#endif
+
+#define DARRAY_NAME triangle_comp
+#define DARRAY_DATA struct triangle_comp
+#include <rsys/dynamic_array.h>
+
+struct triangle_enc {
+ /* The connex component in which each side is. */
+ enclosure_id_t enclosure[2];
+};
+
+#ifndef NDEBUG
+static void
+triangle_enc_init(struct mem_allocator* alloc, struct triangle_enc* trg) {
+ int i;
+ (void) alloc;
+ ASSERT(trg);
+ FOR_EACH(i, 0, 2) trg->enclosure[i] = ENCLOSURE_NULL__;
+}
+#define DARRAY_FUNCTOR_INIT triangle_enc_init
+#endif
+
+#define DARRAY_NAME triangle_enc
+#define DARRAY_DATA struct triangle_enc
+#include <rsys/dynamic_array.h>
+
+#define DARRAY_NAME enclosure
+#define DARRAY_DATA struct enclosure_data
+#define DARRAY_FUNCTOR_INIT enclosure_data_init
+#define DARRAY_FUNCTOR_COPY enclosure_data_copy
+#define DARRAY_FUNCTOR_RELEASE enclosure_data_release
+#define DARRAY_FUNCTOR_COPY_AND_RELEASE enclosure_data_copy_and_release
+#include <rsys/dynamic_array.h>
+
+struct senc_descriptor {
+ struct senc_scene* scene;
+ enclosure_id_t enclosures_count;
+ struct darray_triangle_comp triangles_comp;
+ struct darray_triangle_enc triangles_enc;
+ struct darray_enclosure enclosures;
+ trg_id_t triangle_count;
+ vrtx_id_t vertices_count;
+
+ ref_T ref;
+};
+
+struct senc_descriptor*
+descriptor_create(struct senc_scene* scn);
+
+struct mem_allocator*
+descriptor_get_allocator(struct senc_descriptor* desc);
+
+#endif /* SENC_DESCRIPTOR_C_H */
diff --git a/src/senc_device.c b/src/senc_device.c
@@ -0,0 +1,134 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "senc.h"
+#include "senc_device_c.h"
+
+#include <rsys/logger.h>
+#include <rsys/mem_allocator.h>
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static void
+log_msg
+ (struct senc_device* dev,
+ const enum log_type stream,
+ const char* msg,
+ va_list vargs)
+{
+ ASSERT(dev && msg);
+ if(dev->verbose) {
+ res_T res; (void)res;
+ res = logger_vprint(dev->logger, stream, msg, vargs);
+ ASSERT(res == RES_OK);
+ }
+}
+
+static void
+device_release(ref_T* ref)
+{
+ struct senc_device* dev;
+ ASSERT(ref);
+ dev = CONTAINER_OF(ref, struct senc_device, ref);
+ MEM_RM(dev->allocator, dev);
+}
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+void
+log_err(struct senc_device* dev, const char* msg, ...)
+{
+ va_list vargs_list;
+ ASSERT(dev && msg);
+
+ va_start(vargs_list, msg);
+ log_msg(dev, LOG_ERROR, msg, vargs_list);
+ va_end(vargs_list);
+}
+
+void
+log_warn(struct senc_device* dev, const char* msg, ...)
+{
+ va_list vargs_list;
+ ASSERT(dev && msg);
+
+ va_start(vargs_list, msg);
+ log_msg(dev, LOG_WARNING, msg, vargs_list);
+ va_end(vargs_list);
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+senc_device_create
+ (struct logger* logger,
+ struct mem_allocator* mem_allocator,
+ const unsigned nthreads_hint,
+ const int verbose,
+ struct senc_device** out_dev)
+{
+ struct logger* log = NULL;
+ struct senc_device* dev = NULL;
+ struct mem_allocator* allocator = NULL;
+ res_T res = RES_OK;
+ (void)nthreads_hint; /* Unused */
+ if(nthreads_hint == 0 || !out_dev) return RES_BAD_ARG;
+
+ log = logger ? logger : LOGGER_DEFAULT;
+ allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
+ dev = MEM_CALLOC(allocator, 1, sizeof(struct senc_device));
+ if(!dev) {
+ if(verbose) {
+ /* Do not use helper log functions since dev is not initialised */
+ CHK(logger_print(log, LOG_ERROR,
+ "%s: could not allocate the StarEnclosures device.\n", FUNC_NAME) == RES_OK);
+ }
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ dev->logger = log;
+ dev->allocator = allocator;
+ dev->verbose = verbose;
+ ref_init(&dev->ref);
+
+exit:
+ if(dev) *out_dev = dev;
+ return res;
+error:
+ if(dev) {
+ SENC(device_ref_put(dev));
+ dev = NULL;
+ }
+ goto exit;
+}
+
+res_T
+senc_device_ref_get(struct senc_device* dev)
+{
+ if(!dev) return RES_BAD_ARG;
+ ref_get(&dev->ref);
+ return RES_OK;
+}
+
+res_T
+senc_device_ref_put(struct senc_device* dev)
+{
+ if(!dev) return RES_BAD_ARG;
+ ref_put(&dev->ref, device_release);
+ return RES_OK;
+}
diff --git a/src/senc_device_c.h b/src/senc_device_c.h
@@ -0,0 +1,58 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef SENC_DEVICE_C_H
+#define SENC_DEVICE_C_H
+
+#include <rsys/free_list.h>
+#include <rsys/ref_count.h>
+
+struct name { FITEM; };
+#define FITEM_TYPE name
+#include <rsys/free_list.h>
+
+struct senc_device {
+ struct logger* logger;
+ struct mem_allocator* allocator;
+ int verbose;
+
+ ref_T ref;
+};
+
+/* Conditionally log a message on the LOG_ERROR stream of the device logger,
+ * with respect to the device verbose flag */
+extern LOCAL_SYM void
+log_err
+ (struct senc_device* dev,
+ const char* msg,
+ ...)
+#ifdef COMPILER_GCC
+ __attribute((format(printf, 2, 3)))
+#endif
+;
+
+/* Conditionally log a message on the LOG_WARNING stream of the device logger,
+ * with respect to the device verbose flag */
+extern LOCAL_SYM void
+log_warn
+ (struct senc_device* dev,
+ const char* msg,
+ ...)
+#ifdef COMPILER_GCC
+ __attribute((format(printf, 2, 3)))
+#endif
+;
+
+#endif /* SENC_DEVICE_C_H */
diff --git a/src/senc_enclosure.c b/src/senc_enclosure.c
@@ -0,0 +1,145 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "senc_enclosure_c.h"
+#include "senc_descriptor_c.h"
+#include "senc_scene_c.h"
+#include "senc.h"
+
+#include <rsys/rsys.h>
+#include <rsys/double3.h>
+#include <rsys/mem_allocator.h>
+
+
+/*******************************************************************************
+ * Helper function
+ ******************************************************************************/
+static void
+enclosure_release(ref_T * ref)
+{
+ struct senc_enclosure* enclosure = NULL;
+ struct senc_descriptor* desc = NULL;
+ ASSERT(ref);
+ enclosure = CONTAINER_OF(ref, struct senc_enclosure, ref);
+ desc = enclosure->desc;
+
+ MEM_RM(descriptor_get_allocator(desc), enclosure);
+ SENC(descriptor_ref_put(desc));
+}
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+struct senc_enclosure*
+enclosure_create
+ (struct senc_descriptor* desc,
+ const struct enclosure_data* data)
+{
+ struct senc_enclosure* enc;
+ ASSERT(desc);
+ enc = MEM_CALLOC(descriptor_get_allocator(desc),
+ 1, sizeof(struct senc_enclosure));
+ if(enc) {
+ SENC(descriptor_ref_get(desc));
+ enc->desc = desc;
+ enc->data = data;
+ ref_init(&enc->ref);
+ }
+ return enc;
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+senc_enclosure_get_header
+ (const struct senc_enclosure* enclosure,
+ const struct enclosure_header** header)
+{
+ if(!enclosure || !header) return RES_BAD_ARG;
+ *header = &enclosure->data->header;
+ return RES_OK;
+}
+
+res_T
+senc_enclosure_get_indices
+ (const struct senc_enclosure* enclosure,
+ const unsigned itri,
+ unsigned indices[3])
+{
+ const struct triangle_in* triangle;
+ int i;
+ if(!enclosure || !indices
+ || itri >= enclosure->data->header.triangle_count)
+ return RES_BAD_ARG;
+ triangle = darray_triangle_in_cdata_get(&enclosure->data->sides) + itri;
+ FOR_EACH(i, 0, 3) {
+ ASSERT(triangle->vertice_id[i] < UINT_MAX);
+ indices[i] = (unsigned)triangle->vertice_id[i]; /* Back to API type */
+ }
+ return RES_OK;
+}
+
+res_T
+senc_enclosure_get_vertices
+ (const struct senc_enclosure* enclosure,
+ const unsigned ivert,
+ double coord[3])
+{
+ const struct senc_scene* scene;
+ if(!enclosure || !coord
+ || ivert >= enclosure->data->header.vertices_count)
+ return RES_BAD_ARG;
+ scene = enclosure->desc->scene;
+ ASSERT(darray_position_size_get(&scene->vertices)
+ == enclosure->data->header.vertices_count);
+ d3_set(coord, darray_position_cdata_get(&scene->vertices)[ivert].vec);
+ return RES_OK;
+}
+
+res_T
+senc_enclosure_get_mediums
+ (const struct senc_enclosure* enclosure,
+ const unsigned itri,
+ unsigned medium[2])
+{
+ const struct triangle_in* triangle;
+ int i;
+ if(!enclosure || !medium
+ || itri >= enclosure->data->header.triangle_count)
+ return RES_BAD_ARG;
+ triangle = darray_triangle_in_cdata_get(&enclosure->data->sides) + itri;
+ FOR_EACH(i, 0, 2) {
+ ASSERT(triangle->medium[i] < UINT_MAX);
+ medium[i] = (unsigned)triangle->medium[i]; /* Back to API type */
+ }
+ return RES_OK;
+}
+
+res_T
+senc_enclosure_ref_get(struct senc_enclosure* enc)
+{
+ if(!enc) return RES_BAD_ARG;
+ ref_get(&enc->ref);
+ return RES_OK;
+}
+
+res_T
+senc_enclosure_ref_put(struct senc_enclosure* enc)
+{
+ if(!enc) return RES_BAD_ARG;
+ ref_put(&enc->ref, enclosure_release);
+ return RES_OK;
+}
diff --git a/src/senc_enclosure_c.h b/src/senc_enclosure_c.h
@@ -0,0 +1,37 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef SENC_ENCLOSURE_C_H
+#define SENC_ENCLOSURE_C_H
+
+#include <rsys/ref_count.h>
+
+#include "senc.h"
+
+struct enclosure_data;
+struct senc_descriptor;
+
+struct senc_enclosure {
+ const struct enclosure_data* data;
+ struct senc_descriptor* desc;
+ ref_T ref;
+};
+
+struct senc_enclosure*
+enclosure_create
+ (struct senc_descriptor* desc,
+ const struct enclosure_data* data);
+
+#endif /* SENC_ENCLOSURE_C_H */
diff --git a/src/senc_enclosure_data.h b/src/senc_enclosure_data.h
@@ -0,0 +1,85 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef SENC_ENCLOSURE_DATA_H
+#define SENC_ENCLOSURE_DATA_H
+
+#include <rsys/rsys.h>
+#include <rsys/ref_count.h>
+
+#include "senc.h"
+#include "senc_scene_c.h"
+
+static void
+init_header(struct enclosure_header* header)
+{
+ ASSERT(header);
+ header->enclosure_id = ENCLOSURE_NULL__;
+ header->triangle_count = 0;
+ header->unique_triangle_count = 0;
+ header->vertices_count = 0;
+ header->enclosed_medium = MEDIUM_NULL__;
+ header->is_infinite = CHAR_MAX;
+}
+
+struct enclosure_data {
+ struct enclosure_header header;
+ /* Same triangle can appear twice if both sides */
+ struct darray_triangle_in sides;
+ struct darray_position vertices;
+};
+
+static FINLINE void
+enclosure_data_init(struct mem_allocator* alloc, struct enclosure_data* enc) {
+ ASSERT(enc);
+ init_header(&enc->header);
+ darray_triangle_in_init(alloc, &enc->sides);
+ darray_position_init(alloc, &enc->vertices);
+}
+
+static FINLINE res_T
+enclosure_data_copy
+(struct enclosure_data* dst,
+ const struct enclosure_data* src)
+{
+ res_T res = RES_OK;
+ ASSERT(src && dst);
+ dst->header = src->header;
+ res = darray_triangle_in_copy(&dst->sides, &src->sides);
+ if(res != RES_OK) return res;
+ return darray_position_copy(&dst->vertices, &src->vertices);
+}
+
+static FINLINE void
+enclosure_data_release(struct enclosure_data* n) {
+ ASSERT(n);
+ darray_triangle_in_release(&n->sides);
+ darray_position_release(&n->vertices);
+}
+
+static FINLINE res_T
+enclosure_data_copy_and_release
+(struct enclosure_data* dst,
+ struct enclosure_data* src)
+{
+ res_T res = RES_OK;
+ ASSERT(src && dst);
+ dst->header = src->header;
+ res = darray_triangle_in_copy_and_release(&dst->sides, &src->sides);
+ if(res != RES_OK) return res;
+ return darray_position_copy_and_release(&dst->vertices, &src->vertices);
+}
+
+#endif /* SENC_ENCLOSURE_DATA_H */
diff --git a/src/senc_internal_types.h b/src/senc_internal_types.h
@@ -0,0 +1,64 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef SENC_INTERNAL_TYPES_H
+#define SENC_INTERNAL_TYPES_H
+
+#include <rsys/math.h>
+
+#include <stdint.h>
+
+/* Side IDs are uint32_t */
+typedef uint32_t side_id_t;
+#define SIDE_MAX__ (UINT32_MAX-1)
+#define SIDE_NULL__ UINT32_MAX
+
+/* Trg IDs use internally side_id_t */
+/* Cannot be larger than unsigned, as the API uses it. */
+typedef side_id_t trg_id_t;
+/* TRG_MAX__ is limited to allow to count sides */
+#define TRG_MAX__ (SIDE_MAX__ / 2)
+#define TRG_NULL__ UINT32_MAX
+
+/* Vertex IDs are internally uint32_t */
+/* Cannot be larger than unsigned, as the API uses it. */
+typedef uint32_t vrtx_id_t;
+#define VRTX_MAX__ (UINT32_MAX-1)
+#define VRTX_NULL__ UINT32_MAX
+
+/* Edge IDs use the same type than vertex IDs */
+/* Cannot be larger than unsigned, as the API uses it. */
+typedef vrtx_id_t edge_id_t;
+#define EDGE_MAX__ VRTX_MAX__
+#define EDGE_NULL__ VRTX_NULL__
+
+/* Medium IDs are internally uint16_t */
+/* Should nnot be larger than unsigned, as the API uses it. */
+typedef uint16_t medium_id_t;
+#define MEDIUM_MAX__ (UINT16_MAX-1)
+#define MEDIUM_NULL__ UINT16_MAX
+
+/* Enclosure IDs are internally uint16_t */
+/* Cannot be larger than unsigned, as the API uses it. */
+typedef uint16_t enclosure_id_t;
+#define ENCLOSURE_MAX__ (UINT16_MAX-1)
+#define ENCLOSURE_NULL__ UINT16_MAX
+
+/* Component IDs use the same type than enclosure IDs */
+typedef enclosure_id_t component_id_t;
+#define COMPONENT_MAX__ ENCLOSURE_MAX__
+#define COMPONENT_NULL__ ENCLOSURE_NULL__
+
+#endif /* SENC_INTERNAL_TYPES_H */
diff --git a/src/senc_s3d_wrapper.h b/src/senc_s3d_wrapper.h
@@ -0,0 +1,80 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef SENC_S3D_WRAPPER_H
+#define SENC_S3D_WRAPPER_H
+
+#include "senc.h"
+
+#include <rsys/rsys.h>
+#include <rsys/float3.h>
+
+void FINLINE
+senc_descriptor_get_global_indices__
+ (const unsigned itri,
+ unsigned indices[3],
+ void* ctx)
+{
+ const struct senc_descriptor* descriptor = ctx;
+ res_T r;
+ ASSERT(indices && ctx);
+ r = senc_descriptor_get_global_indices(descriptor, itri, indices);
+ ASSERT(r == RES_OK);
+}
+
+void FINLINE
+senc_descriptor_get_global_vertices__
+ (const unsigned ivert,
+ float coord[3],
+ void* ctx)
+{
+ const struct senc_descriptor* descriptor = ctx;
+ double tmp[3];
+ res_T r;
+ ASSERT(coord && ctx);
+ r = senc_descriptor_get_global_vertices(descriptor, ivert, tmp);
+ ASSERT(r == RES_OK);
+ f3_set_d3(coord, tmp);
+}
+
+void FINLINE
+senc_enclosure_get_indices__
+ (const unsigned itri,
+ unsigned indices[3],
+ void* ctx)
+{
+ const struct senc_enclosure* enclosure = ctx;
+ res_T r;
+ ASSERT(indices && ctx);
+ r = senc_enclosure_get_indices(enclosure, itri, indices);
+ ASSERT(r == RES_OK);
+}
+
+void FINLINE
+senc_enclosure_get_vertices__
+ (const unsigned ivert,
+ float coord[3],
+ void* ctx)
+{
+ const struct senc_enclosure* enclosure = ctx;
+ double tmp[3];
+ res_T r;
+ ASSERT(coord && ctx);
+ r = senc_enclosure_get_vertices(enclosure, ivert, tmp);
+ ASSERT(r == RES_OK);
+ f3_set_d3(coord, tmp);
+}
+
+#endif /* SENC_S3D_WRAPPER_H */
diff --git a/src/senc_scene.c b/src/senc_scene.c
@@ -0,0 +1,289 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "senc.h"
+#include "senc_device_c.h"
+#include "senc_scene_c.h"
+
+#include <rsys/rsys.h>
+#include <rsys/mem_allocator.h>
+
+#include <limits.h>
+
+/*******************************************************************************
+ * Helper function
+ ******************************************************************************/
+static void
+scene_release(ref_T * ref)
+{
+ struct senc_device* dev = NULL;
+ struct senc_scene* scn = NULL;
+ ASSERT(ref);
+ scn = CONTAINER_OF(ref, struct senc_scene, ref);
+ dev = scn->dev;
+ darray_triangle_in_release(&scn->triangles_in);
+ darray_position_release(&scn->vertices);
+ htable_vrtx_release(&scn->unique_vertices);
+ darray_vrtx_id_release(&scn->unique_vertice_ids);
+ htable_trg_release(&scn->unique_triangles);
+ darray_trg_id_release(&scn->unique_triangle_ids);
+ darray_char_release(&scn->triangle_reversed);
+ MEM_RM(dev->allocator, scn);
+ SENC(device_ref_put(dev));
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+senc_scene_create
+ (struct senc_device* dev,
+ const unsigned nmeds,
+ struct senc_scene** out_scn)
+{
+ struct senc_scene* scn = NULL;
+ res_T res = RES_OK;
+
+ if(!dev || !out_scn || !nmeds || nmeds > MEDIUM_MAX__)
+ return RES_BAD_ARG;
+
+ scn = MEM_CALLOC(dev->allocator, 1, sizeof(struct senc_scene));
+ if(!scn) {
+ log_err(dev, "%s: could not allocate the StarEnclosures scene.\n", FUNC_NAME);
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ ref_init(&scn->ref);
+ SENC(device_ref_get(dev));
+ scn->dev = dev;
+ scn->ngeoms = 0;
+ scn->ntris = 0;
+ scn->nutris = 0;
+ scn->nmeds = (medium_id_t)nmeds;
+ scn->nverts = 0;
+ scn->nuverts = 0;
+ darray_triangle_in_init(dev->allocator, &scn->triangles_in);
+ darray_position_init(dev->allocator, &scn->vertices);
+ htable_vrtx_init(dev->allocator, &scn->unique_vertices);
+ darray_vrtx_id_init(dev->allocator, &scn->unique_vertice_ids);
+ htable_trg_init(dev->allocator, &scn->unique_triangles);
+ darray_trg_id_init(dev->allocator, &scn->unique_triangle_ids);
+ darray_char_init(dev->allocator, &scn->triangle_reversed);
+
+exit:
+ if(scn) *out_scn = scn;
+ return res;
+error:
+ if(scn) {
+ SENC(scene_ref_put(scn));
+ scn = NULL;
+ }
+ goto exit;
+}
+
+res_T
+senc_scene_add_geometry
+ (struct senc_scene* scn,
+ const unsigned ntris,
+ void(*indices)(const unsigned itri, unsigned ids[3], void*),
+ void(*mediums)(const unsigned itri, unsigned medium[2], void*),
+ const unsigned nverts,
+ void(*position)(const unsigned ivert, double pos[3], void* ctx),
+ void* ctx)
+{
+ unsigned i;
+ vrtx_id_t actual_nverts = 0;
+ vrtx_id_t actual_nuverts = 0;
+ trg_id_t actual_ntris = 0;
+ trg_id_t actual_nutris = 0;
+ const struct triangle_in* trg;
+ res_T res = RES_OK;
+
+ if(!scn
+ || !indices || !mediums || !position
+ || !nverts || ((size_t)scn->nverts + (size_t)nverts) > VRTX_MAX__
+ || !ntris || ((size_t)scn->ntris + (size_t)ntris) > TRG_MAX__)
+ return RES_BAD_ARG;
+
+ /* Make room for new geometry; suppose no more duplicates. */
+ res = darray_position_reserve(&scn->vertices, scn->nuverts + nverts);
+ if(res != RES_OK) goto error;
+ res = darray_triangle_in_reserve(&scn->triangles_in, scn->nutris + ntris);
+ if(res != RES_OK) goto error;
+ res = htable_vrtx_reserve(&scn->unique_vertices, scn->nuverts + nverts);
+ if(res != RES_OK) goto error;
+ res = darray_vrtx_id_reserve(&scn->unique_vertice_ids, scn->nverts + nverts);
+ if(res != RES_OK) goto error;
+ res = htable_trg_reserve(&scn->unique_triangles, scn->nutris + ntris);
+ if(res != RES_OK) goto error;
+ res = darray_trg_id_reserve(&scn->unique_triangle_ids, scn->ntris + ntris);
+ if (res != RES_OK) goto error;
+ res = darray_char_reserve(&scn->triangle_reversed, scn->ntris + ntris);
+ if (res != RES_OK) goto error;
+
+ /* Get geometry */
+ trg = darray_triangle_in_cdata_get(&scn->triangles_in);
+
+ FOR_EACH(i, 0, nverts) {
+ vrtx_id_t* p_vrtx;
+ union double3 tmp;
+ vrtx_id_t unique_v;
+ /* API: position needs an unsigned */
+ position(i, tmp.vec, ctx);
+ p_vrtx = htable_vrtx_find(&scn->unique_vertices, &tmp);
+ if(p_vrtx) {
+ /* Duplicate vertex */
+ unique_v = darray_vrtx_id_cdata_get(&scn->unique_vertice_ids)[*p_vrtx];
+ } else {
+ /* New vertex */
+ unique_v = scn->nuverts + actual_nuverts;
+ res = darray_position_push_back(&scn->vertices, &tmp);
+ if (res != RES_OK) goto error;
+ ASSERT(unique_v == htable_vrtx_size_get(&scn->unique_vertices));
+ res = htable_vrtx_set(&scn->unique_vertices, &tmp, &unique_v);
+ ++actual_nuverts;
+ if(res != RES_OK) goto error;
+ }
+ /* The unique ID for vertex v is u */
+ ASSERT(scn->nverts + i
+ == darray_vrtx_id_size_get(&scn->unique_vertice_ids));
+ res = darray_vrtx_id_push_back(&scn->unique_vertice_ids, &unique_v);
+ if(res != RES_OK) goto error;
+ ++actual_nverts;
+ }
+
+ FOR_EACH(i, 0, ntris) {
+ int j;
+ unsigned med[2];
+ unsigned ind[3];
+ vrtx_id_t vrtx_key;
+ union vrtx_id3 trg_key;
+ struct triangle_in tmp;
+ trg_id_t* p_trg;
+ trg_id_t tr;
+ char reversed;
+ indices(i, ind, ctx); /* API: indices needs an unsigned */
+ FOR_EACH(j, 0, 3) {
+ if(ind[j] >= nverts) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ vrtx_key = ind[j] + scn->nverts;
+ ASSERT(vrtx_key < darray_vrtx_id_size_get(&scn->unique_vertice_ids));
+ /* Find the unique ID for this vertex */
+ tmp.vertice_id[j] =
+ darray_vrtx_id_cdata_get(&scn->unique_vertice_ids)[vrtx_key];
+ }
+ if(tmp.vertice_id[0] == tmp.vertice_id[1]
+ || tmp.vertice_id[0] == tmp.vertice_id[2]
+ || tmp.vertice_id[1] == tmp.vertice_id[2]) {
+ log_err(scn->dev, "%s: triangle %lu is degenerate.\n",
+ FUNC_NAME, (unsigned long)i);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ /* Get mediums */
+ mediums(i, med, ctx); /* API: mediums needs an unsigned */
+ ASSERT(scn->nmeds <= MEDIUM_MAX__);
+ FOR_EACH(j, 0, 2) {
+ if(med[j] >= scn->nmeds) {
+ log_err(scn->dev, "%s: triangle %lu references invalid medium.\n",
+ FUNC_NAME, (unsigned long)i);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ tmp.medium[j] = (medium_id_t)med[j];
+ }
+ /* Find duplicate triangles */
+ reversed = trg_make_key(&trg_key, tmp.vertice_id);
+ p_trg = htable_trg_find(&scn->unique_triangles, &trg_key);
+ if(p_trg) {
+ union vrtx_id3 utrg_key;
+ char ureversed = trg_make_key(&utrg_key, trg[*p_trg].vertice_id);
+ int same = (reversed == ureversed);
+ const medium_id_t* umed;
+ /* Duplicate triangle. Need to check duplicate validity */
+ ASSERT(trg_key_eq(&trg_key, &utrg_key));
+ umed = trg[*p_trg].medium;
+ if(umed[0] != (same ? med[0] : med[1])
+ || umed[1] != (same ? med[1] : med[0])) {
+ /* Same triangles with different mediums: invalid! */
+ log_err(scn->dev, "%s: triangle %lu is a duplicate with incoherent mediums.\n",
+ FUNC_NAME, (unsigned long)*p_trg);
+ res = RES_BAD_ARG;
+ goto error;
+ } else {
+ if(!same) {
+ FOR_EACH(j, 0, 2) {
+ tmp.medium[j] = (medium_id_t) med[1-j];
+ }
+ }
+ }
+ }
+ else {
+ /* New triangle */
+ trg_id_t u = scn->nutris + actual_nutris;
+ ASSERT(u == htable_trg_size_get(&scn->unique_triangles));
+ res = htable_trg_set(&scn->unique_triangles, &trg_key, &u);
+ if(res != RES_OK) goto error;
+ res = darray_triangle_in_push_back(&scn->triangles_in, &tmp);
+ if (res != RES_OK) goto error;
+ ++actual_nutris;
+ }
+ ASSERT(darray_trg_id_size_get(&scn->unique_triangle_ids)
+ == darray_char_size_get(&scn->triangle_reversed));
+ ASSERT(scn->ntris + i
+ == darray_trg_id_size_get(&scn->unique_triangle_ids));
+ tr = scn->nutris + i;
+ res = darray_trg_id_push_back(&scn->unique_triangle_ids, &tr);
+ if (res != RES_OK) goto error;
+ res = darray_char_push_back(&scn->triangle_reversed, &reversed);
+ if (res != RES_OK) goto error;
+ ++actual_ntris;
+ }
+
+exit:
+ /* Update sizes */
+ scn->nuverts += actual_nuverts;
+ scn->nverts += actual_nverts;
+ scn->nutris += actual_nutris;
+ scn->ntris += actual_ntris;
+ ASSERT(scn->nverts == darray_vrtx_id_size_get(&scn->unique_vertice_ids));
+ ASSERT(scn->nuverts == htable_vrtx_size_get(&scn->unique_vertices));
+ ASSERT(scn->nutris == htable_trg_size_get(&scn->unique_triangles));
+ ASSERT(scn->ntris == darray_trg_id_size_get(&scn->unique_triangle_ids));
+ ASSERT(darray_trg_id_size_get(&scn->unique_triangle_ids)
+ == darray_char_size_get(&scn->triangle_reversed));
+ ++scn->ngeoms;
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+senc_scene_ref_get(struct senc_scene* scn)
+{
+ if(!scn) return RES_BAD_ARG;
+ ref_get(&scn->ref);
+ return RES_OK;
+}
+
+res_T
+senc_scene_ref_put(struct senc_scene* scn)
+{
+ if(!scn) return RES_BAD_ARG;
+ ref_put(&scn->ref, scene_release);
+ return RES_OK;
+}
diff --git a/src/senc_scene_analyze.c b/src/senc_scene_analyze.c
@@ -0,0 +1,1097 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "senc.h"
+#include "senc_descriptor_c.h"
+#include "senc_device_c.h"
+#include "senc_scene_c.h"
+#include "senc_scene_analyze_c.h"
+#include "senc_internal_types.h"
+
+#include <rsys/rsys.h>
+#include <rsys/float3.h>
+#include <rsys/double33.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/hash_table.h>
+#include <rsys/dynamic_array.h>
+#include <rsys/dynamic_array_uint.h>
+
+#include <star/s3d.h>
+
+#include <limits.h>
+#include <stdlib.h>
+
+#define CC_DESCRIPTOR_NULL__ {\
+ {0,0,-DBL_MAX}, -1, SIDE_NULL__, VRTX_NULL__, 0, MEDIUM_NULL__,\
+ CC_ID_NONE, CC_GROUP_ROOT_NONE, CC_GROUP_ID_NONE\
+}
+const struct cc_descriptor CC_DESCRIPTOR_NULL = CC_DESCRIPTOR_NULL__;
+
+#define DARRAY_NAME component_id
+#define DARRAY_DATA component_id_t
+#include <rsys/dynamic_array.h>
+
+#define DARRAY_NAME side_id
+#define DARRAY_DATA side_id_t
+#include <rsys/dynamic_array.h>
+
+#define HTABLE_NAME vrtx_id
+#define HTABLE_KEY vrtx_id_t
+#define HTABLE_DATA vrtx_id_t
+#include <rsys/hash_table.h>
+
+/*******************************************************************************
+ * Helper function
+ ******************************************************************************/
+static void
+dumplist
+ (const struct trgside* trgsides,
+ const struct darray_side_id* side_ids,
+ const enum list_id list_id)
+{
+ side_id_t i;
+ size_t tmp;
+ (void)list_id;
+ ASSERT(trgsides && side_ids);
+ printf("\n");
+ tmp = darray_side_id_size_get(side_ids);
+ ASSERT(tmp <= SIDE_MAX__);
+ FOR_EACH(i, 0, (side_id_t)tmp) {
+ const side_id_t id = darray_side_id_cdata_get(side_ids)[i];
+ const struct trgside* const side = trgsides + id;
+ printf("Side %lu (%lu %lu %lu)\n",
+ (unsigned long)id,
+ (unsigned long)side->facing_side_id[0],
+ (unsigned long)side->facing_side_id[1],
+ (unsigned long)side->facing_side_id[2]);
+ ASSERT(side->list_id & list_id);
+ }
+}
+
+static int
+find_side_in_list
+ (const struct trgside* trgsides,
+ const struct darray_side_id* side_ids,
+ const side_id_t side_id,
+ const enum list_id list_id)
+{
+ side_id_t i;
+ size_t tmp;
+ (void)list_id;
+ (void)trgsides;
+ ASSERT(trgsides && side_ids);
+ tmp = darray_side_id_size_get(side_ids);
+ ASSERT(tmp <= SIDE_MAX__);
+ FOR_EACH(i, 0, (side_id_t)tmp) {
+ const side_id_t id = darray_side_id_cdata_get(side_ids)[i];
+ ASSERT(trgsides[id].list_id & list_id);
+ if(id == side_id) return 1;
+ }
+ return 0;
+}
+
+static INLINE int
+neighbour_cmp(const void* w1, const void* w2)
+{
+ const double a1 = ((struct neighbour_info*)w1)->angle;
+ const double a2 = ((struct neighbour_info*)w2)->angle;
+ return (a1 > a2) - (a1 < a2);
+}
+
+static void
+find_component_Zmax
+ (struct senc_scene* scn,
+ struct darray_triangle_tmp* triangles_tmp_array,
+ struct cc_descriptor* cc)
+{
+ trg_id_t trid;
+ double edge0[3], edge1[3], normal[3], norm, side_nz;
+ struct triangle_in* triangles_in;
+ struct triangle_tmp* triangles_tmp;
+ const union double3* vertices;
+ const char* side_membership;
+ ASSERT(scn && triangles_tmp_array && cc);
+
+ vertices = darray_position_cdata_get(&scn->vertices);
+ triangles_in = darray_triangle_in_data_get(&scn->triangles_in);
+ triangles_tmp = darray_triangle_tmp_data_get(triangles_tmp_array);
+ side_membership = darray_char_cdata_get(&cc->side_membership);
+
+ /* Build the sorted list of side ids */
+ FOR_EACH(trid, 0, scn->ntris) {
+ const char member = side_membership[trid];
+ struct triangle_in* const trg_in = triangles_in + trid;
+ struct triangle_tmp* const trg_tmp = triangles_tmp + trid;
+ enum side_id side;
+ int change = 0;
+
+ if(!member) continue;
+
+ /* Keep track of the appropriate vertex/side of the connex component
+ * in order to cast a ray at the component grouping step of the algorithm.
+ * The most appropriate vertex is (the) one with the greater Z coordinate.
+ * If more than one vertex/side has the same Z, we want the side that most
+ * faces Z (that is the one with the greater nz).
+ * This is mandatory to select the correct side when both sides of a triangle
+ * are candidate. */
+ if(cc->max_vrtx[2] > trg_tmp->max_z)
+ return;
+
+ d3_sub(edge0, vertices[trg_in->vertice_id[1]].vec,
+ vertices[trg_in->vertice_id[0]].vec);
+ d3_sub(edge1, vertices[trg_in->vertice_id[2]].vec,
+ vertices[trg_in->vertice_id[0]].vec);
+ d3_cross(normal, edge0, edge1);
+ norm = d3_normalize(normal, normal);
+ ASSERT(norm);
+
+ if((member & FLAG_FRONT) && (member & FLAG_BACK)) {
+ /* Select the side with nz>0 */
+ side_nz = fabs(normal[2]);
+ side = (normal[2] > 0) ? SIDE_FRONT : SIDE_BACK;
+ } else if(member & FLAG_FRONT) {
+ side_nz = normal[2];
+ side = SIDE_FRONT;
+ } else {
+ ASSERT(member & FLAG_BACK);
+ side_nz = -normal[2];
+ side = SIDE_BACK;
+ }
+
+ /* TODO: keep the list of triangle sharing the selected vertex
+ * to improve self-hit filter. */
+ if(cc->max_vrtx[2] < trg_tmp->max_z) {
+ change = 1; /* Try first to improve z */
+ }
+ else if(cc->max_z_nz <= 0 && fabs(cc->max_z_nz) < fabs(side_nz)) {
+ change = 1; /* If nz <= 0, the more negative the better */
+ }
+ else if(cc->max_z_nz > 0 && cc->max_z_nz < side_nz) {
+ change = 1; /* If nz > 0, the more positive the better */
+ }
+ if(change) {
+ cc->max_z_nz = side_nz;
+ cc->max_z_side_id = TRGIDxSIDE_2_TRGSIDE(trid, side);
+ ASSERT(trg_tmp->max_z_vrtx_id < 3);
+ ASSERT(trg_in->vertice_id[trg_tmp->max_z_vrtx_id] < scn->nverts);
+ cc->max_z_vrtx_id = trg_in->vertice_id[trg_tmp->max_z_vrtx_id];
+ d3_set(cc->max_vrtx, vertices[cc->max_z_vrtx_id].vec);
+ }
+ }
+}
+
+static FINLINE void
+add_side_to_stack
+ (struct senc_scene* scn,
+ struct darray_side_id* stack,
+ struct trgside* trgsides,
+ const side_id_t side_id)
+{
+ (void)scn;
+ ASSERT(scn && trgsides && stack
+ && side_id < SIDE_MAX__ && side_id < 2 * scn->ntris);
+ ASSERT(!find_side_in_list(trgsides, stack, side_id, FLAG_WAITING_STACK));
+ darray_side_id_push_back(stack, &side_id);
+ trgsides[side_id].list_id = FLAG_WAITING_STACK;
+}
+
+static FINLINE void
+add_side_to_medium_list
+ (struct senc_scene* scn,
+ struct darray_side_id* side_ids_by_medium,
+ struct trgside* trgsides,
+ const side_id_t side_id)
+{
+ (void)scn;
+ ASSERT(scn && side_ids_by_medium && trgsides
+ && side_id < 2 * scn->ntris);
+ if(trgsides[side_id].list_id == FLAG_LIST_BY_MEDIUM) {
+ ASSERT(find_side_in_list(trgsides, side_ids_by_medium, side_id,
+ FLAG_LIST_BY_MEDIUM));
+ return;
+ }
+ ASSERT(!find_side_in_list(trgsides, side_ids_by_medium, side_id,
+ FLAG_LIST_BY_MEDIUM));
+ darray_side_id_push_back(side_ids_by_medium, &side_id);
+ trgsides[side_id].list_id = FLAG_LIST_BY_MEDIUM;
+}
+
+static side_id_t
+get_side_from_medium_list_not_in_connex_component
+ (struct senc_scene* scn,
+ struct trgside* trgsides,
+ side_id_t* first_side_by_medium_not_in_component,
+ const struct darray_side_id* side_ids_by_medium)
+{
+ side_id_t i, sz;
+ size_t tmp;
+ const side_id_t* ids;
+ (void)scn;
+ ASSERT(scn && trgsides && first_side_by_medium_not_in_component
+ && side_ids_by_medium);
+ tmp = darray_side_id_size_get(side_ids_by_medium);
+ ids = darray_side_id_cdata_get(side_ids_by_medium);
+ ASSERT(tmp <= SIDE_MAX__);
+ sz = (side_id_t)tmp;
+ i = *first_side_by_medium_not_in_component;
+ while(i < sz && trgsides[ids[i]].list_id != FLAG_LIST_BY_MEDIUM) ++i;
+
+ *first_side_by_medium_not_in_component = i+1;
+ if(i == sz) return SIDE_NULL__;
+ ASSERT(trgsides[ids[i]].list_id == FLAG_LIST_BY_MEDIUM);
+ return ids[i];
+}
+
+static FINLINE side_id_t
+get_side_from_stack
+ (struct trgside* trgsides,
+ struct darray_side_id* stack)
+{
+ side_id_t id;
+ size_t sz;
+ (void)trgsides;
+ ASSERT(trgsides && stack);
+ sz = darray_side_id_size_get(stack);
+ id = darray_side_id_cdata_get(stack)[sz - 1];
+ ASSERT(trgsides[id].list_id == FLAG_WAITING_STACK);
+ darray_side_id_pop_back(stack);
+ return id;
+}
+
+static res_T
+extract_connex_components
+ (struct senc_descriptor* desc,
+ struct trgside* trgsides,
+ struct darray_cc_descriptor* connex_components,
+ struct darray_triangle_tmp* triangles_tmp_array,
+ struct darray_side_id* side_ids_by_medium)
+{
+ res_T res = RES_OK;
+ medium_id_t m;
+ size_t tmp;
+ struct senc_scene* scn;
+ struct mem_allocator* alloc;
+ struct darray_side_id stack;
+ struct triangle_comp* triangles_comp;
+ /* Arrays of cc_descriptor ids, organized by medium. */
+ struct darray_component_id* component_ids_by_medium = NULL;
+
+ ASSERT(trgsides && desc && connex_components && triangles_tmp_array
+ && side_ids_by_medium);
+
+ alloc = descriptor_get_allocator(desc);
+ scn = desc->scene;
+
+ res = darray_triangle_comp_resize(&desc->triangles_comp, scn->ntris);
+ if(res != RES_OK) goto error;
+ triangles_comp = darray_triangle_comp_data_get(&desc->triangles_comp);
+
+ /* Init data structures */
+ darray_side_id_init(alloc, &stack);
+ component_ids_by_medium
+ = MEM_ALLOC(alloc, scn->nmeds * sizeof(struct darray_uint));
+ if(!component_ids_by_medium) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ FOR_EACH(m, 0, scn->nmeds) {
+ struct darray_component_id* const cc = component_ids_by_medium + m;
+ darray_component_id_init(alloc, cc);
+ }
+
+ /* For each medium extract connex components */
+ FOR_EACH(m, 0, scn->nmeds) {
+ struct darray_component_id* const cc_ids_by_medium
+ = component_ids_by_medium + m;
+ struct cc_descriptor current_cc;
+ /* Id of the first trgside not already in a connex component */
+ side_id_t first_side_by_medium_not_in_component = 0;
+ /* Init current component */
+ cc_descriptor_init(alloc, ¤t_cc);
+
+ for(;;) {
+ /* Any not-already-used side by medium is used as a starting point */
+ const side_id_t start_side_id =
+ get_side_from_medium_list_not_in_connex_component(scn, trgsides,
+ &first_side_by_medium_not_in_component, side_ids_by_medium + m);
+ side_id_t crt_side_id = start_side_id;
+ char* side_membership;
+
+ ASSERT(start_side_id == SIDE_NULL__ || start_side_id < 2 * scn->ntris);
+ if(start_side_id == SIDE_NULL__)
+ break; /* start_side_id=SIDE_NULL__ => medium done! */
+ ASSERT(trgsides[start_side_id].list_id == FLAG_LIST_BY_MEDIUM);
+
+ /* Reset CC data for a new CC */
+ tmp = darray_cc_descriptor_size_get(connex_components);
+ ASSERT(tmp <= COMPONENT_MAX__);
+ cc_descriptor_clear(¤t_cc);
+ /* 1 char per triangle (2 sides) */
+ darray_char_resize(¤t_cc.side_membership, scn->ntris);
+ side_membership = darray_char_data_get(¤t_cc.side_membership);
+ memset(side_membership, 0, scn->ntris * sizeof(char));
+ current_cc.cc_id = (component_id_t)tmp;
+ current_cc.medium = m;
+
+ for(;;) {
+ int i;
+ /* Pop waiting stack to feed crt_side */
+ struct trgside* crt_side = trgsides + crt_side_id;
+ const trg_id_t crt_trg_id = TRGSIDE_2_TRG(crt_side_id);
+ struct triangle_comp* trg_comp = triangles_comp + crt_trg_id;
+ ASSERT(crt_trg_id < scn->ntris);
+ ASSERT(trgsides[crt_side_id].medium == m);
+
+ /* Record crt_side both as component and triangle level */
+ current_cc.triangle_count++;
+ trgsides[crt_side_id].list_id = FLAG_LIST_COMPONENT;
+ if(TRGSIDE_IS_FRONT(crt_side_id)) {
+ ASSERT(trg_comp->component[SIDE_FRONT] == USHRT_MAX);
+ trg_comp->component[SIDE_FRONT] = current_cc.cc_id;
+ ASSERT(!(side_membership[crt_trg_id] & FLAG_FRONT));
+ side_membership[crt_trg_id] |= FLAG_FRONT;
+ } else {
+ ASSERT(trg_comp->component[SIDE_BACK] == USHRT_MAX);
+ trg_comp->component[SIDE_BACK] = current_cc.cc_id;
+ ASSERT(!(side_membership[crt_trg_id] & FLAG_BACK));
+ side_membership[crt_trg_id] |= FLAG_BACK;
+ }
+#ifndef NDEBUG
+ crt_side->member_of_cc = current_cc.cc_id;
+#endif
+ /* Store neighbour sides in a waiting stack */
+ FOR_EACH(i, 0, 3) {
+ side_id_t neighbour_id = crt_side->facing_side_id[i];
+ struct trgside* neighbour = trgsides + neighbour_id;
+ if(neighbour->medium != crt_side->medium) {
+ /* Found medium discontinuity!
+ * Model topology is broken. */
+ /* TODO: clean problem reporting. */
+ log_err(scn->dev,
+ "Medium mismatch found between triangle %u %s"
+ " side and triangle %u %s side.\n",
+ crt_trg_id, TRGSIDE_IS_FRONT(crt_side_id) ? "front": "back",
+ TRGSIDE_2_TRG(neighbour_id),
+ TRGSIDE_IS_FRONT(neighbour_id) ? "front" : "back");
+ res = RES_BAD_ARG;
+ cc_descriptor_release(¤t_cc);
+ goto error;
+ }
+ if(neighbour->list_id == FLAG_LIST_COMPONENT) {
+ /* Already processed */
+#ifndef NDEBUG
+ ASSERT(neighbour->member_of_cc == current_cc.cc_id);
+#endif
+ continue;
+ }
+ if(neighbour->list_id == FLAG_WAITING_STACK) {
+ continue; /* Already processed */
+ }
+ add_side_to_stack(scn, &stack, trgsides, neighbour_id);
+ }
+ if(darray_side_id_size_get(&stack) == 0)
+ break; /* Empty stack => connex component is done! */
+ crt_side_id = get_side_from_stack(trgsides, &stack);
+ }
+ /* Keep track of this new connex component */
+ darray_component_id_push_back(cc_ids_by_medium, ¤t_cc.cc_id);
+ find_component_Zmax(scn, triangles_tmp_array, ¤t_cc);
+ darray_cc_descriptor_push_back(connex_components, ¤t_cc);
+ }
+ cc_descriptor_release(¤t_cc);
+ }
+
+exit:
+ if(component_ids_by_medium) {
+ FOR_EACH(m, 0, scn->nmeds) {
+ struct darray_component_id* cc = component_ids_by_medium + m;
+ darray_component_id_release(cc);
+ }
+ MEM_RM(scn->dev->allocator, component_ids_by_medium);
+ }
+ darray_side_id_release(&stack);
+ return res;
+error:
+ goto exit;
+}
+
+void
+get_scn_indices(const unsigned itri, unsigned ids[3], void* ctx) {
+ int i;
+ const struct senc_scene* scene = ctx;
+ const struct triangle_in* trg =
+ darray_triangle_in_cdata_get(&scene->triangles_in) + itri;
+ FOR_EACH(i, 0, 3) {
+ ASSERT(trg->vertice_id[i] < scene->nverts);
+ ids[i] = (unsigned)trg->vertice_id[i]; /* Back to API type */
+ }
+}
+
+void
+get_scn_position(const unsigned ivert, float pos[3], void* ctx) {
+ const struct senc_scene* scene = ctx;
+ const union double3* pt =
+ darray_position_cdata_get(&scene->vertices) + ivert;
+ f3_set_d3(pos, pt->vec);
+}
+
+static res_T
+group_connex_components
+ (struct senc_descriptor* desc,
+ struct trgside* trgsides,
+ struct darray_cc_descriptor* connex_components)
+{
+ res_T res = RES_OK;
+ struct mem_allocator* alloc;
+ struct cc_descriptor* descriptors;
+ struct s3d_device* s3d = NULL;
+ struct s3d_scene* s3d_scn = NULL;
+ struct s3d_shape* s3d_shp = NULL;
+ struct s3d_scene_view* s3d_view = NULL;
+ struct s3d_vertex_data attribs;
+ size_t tmp;
+ component_id_t cc_count, c;
+ medium_id_t infinite_medium = MEDIUM_NULL__;
+ side_id_t infinite_medium_first_side = SIDE_MAX__;
+ char infinite_medium_is_known = 0;
+ (void)trgsides;
+
+ alloc = descriptor_get_allocator(desc);
+ descriptors = darray_cc_descriptor_data_get(connex_components);
+ tmp = darray_cc_descriptor_size_get(connex_components);
+ ASSERT(tmp <= COMPONENT_MAX__);
+ cc_count = (component_id_t)tmp;
+
+ if(!cc_count) return RES_OK; /* No component to group */
+
+ attribs.type = S3D_FLOAT3;
+ attribs.usage = S3D_POSITION;
+ attribs.get = get_scn_position;
+
+ res = s3d_device_create(desc->scene->dev->logger, alloc, 0, &s3d);
+ if(res != RES_OK) goto error;
+
+ res = s3d_scene_create(s3d, &s3d_scn);
+ if(res != RES_OK) goto error;
+
+ /* Put geometry in a 3D view */
+ res = s3d_shape_create_mesh(s3d, &s3d_shp);
+ if(res != RES_OK) goto error;
+
+ /* Back to API type for ntris and nverts */
+ ASSERT(desc->scene->nutris < UINT_MAX);
+ ASSERT(desc->scene->nuverts < UINT_MAX);
+ res = s3d_mesh_setup_indexed_vertices(s3d_shp, (unsigned)desc->scene->nutris,
+ get_scn_indices, (unsigned)desc->scene->nuverts, &attribs, 1, desc->scene);
+ if(res != RES_OK) goto error;
+
+ res = s3d_scene_attach_shape(s3d_scn, s3d_shp);
+ if(res != RES_OK) goto error;
+
+ res = s3d_scene_view_create(s3d_scn, S3D_TRACE, &s3d_view);
+ if(res != RES_OK) goto error;
+
+ /* Cast rays to find links between connex components */
+ FOR_EACH(c, 0, cc_count) {
+ struct s3d_hit hit = S3D_HIT_NULL;
+ float origin[3];
+ const float direction[3] = { 0, 0, 1 };
+ const float range[2] = { 0, FLT_MAX };
+ struct cc_descriptor* const cc = descriptors + c;
+
+ ASSERT(cc->cc_group_root == CC_GROUP_ID_NONE);
+
+ if(cc->max_z_nz < 0) {
+ /* Don't need to cast a ray */
+ cc->cc_group_root = c; /* New group with self as root */
+ ASSERT(desc->enclosures_count < USHRT_MAX);
+ cc->enclosure_id = desc->enclosures_count++;
+ continue;
+ }
+
+ ASSERT(cc->max_z_nz != 0
+ /* The only situation with nz==0 we can think of is this one: */
+ || (trgsides[cc->max_z_side_id].medium
+ == trgsides[TRGSIDE_OPPOSITE(cc->max_z_side_id)].medium
+ && (trgsides[cc->max_z_side_id].facing_side_id[0]
+ == TRGSIDE_OPPOSITE(cc->max_z_side_id)
+ || trgsides[cc->max_z_side_id].facing_side_id[1]
+ == TRGSIDE_OPPOSITE(cc->max_z_side_id)
+ || trgsides[cc->max_z_side_id].facing_side_id[2]
+ == TRGSIDE_OPPOSITE(cc->max_z_side_id))));
+ f3_set_d3(origin, cc->max_vrtx);
+ res = s3d_scene_view_trace_ray(
+ s3d_view, origin, direction, range, NULL, &hit);
+ if(res != RES_OK) goto error;
+ /* If no hit, the component is facing an infinite medium */
+ if(S3D_HIT_NONE(&hit)) {
+ cc->cc_group_root = CC_GROUP_ROOT_INFINITE;
+ cc->enclosure_id = 0;
+ if(!infinite_medium_is_known) {
+ infinite_medium_is_known = 1;
+ infinite_medium = cc->medium;
+ infinite_medium_first_side = cc->max_z_side_id;
+ continue;
+ }
+ if(infinite_medium != cc->medium) {
+ /* Medium mismatch!
+ * Model topology is broken. */
+ /* TODO: clean problem reporting. */
+ log_err(desc->scene->dev,
+ "Medium mismatch found between triangle %u %s side and triangle"
+ " %u %s side, both facing infinite.\n",
+ TRGSIDE_2_TRG(infinite_medium_first_side),
+ TRGSIDE_IS_FRONT(infinite_medium_first_side) ? "front" : "back",
+ TRGSIDE_2_TRG(cc->max_z_side_id),
+ TRGSIDE_IS_FRONT(cc->max_z_side_id) ? "front" : "back");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ /* Same medium as previous members of the group: OK */
+ continue;
+ } else {
+ /* If hit, group this component */
+ const trg_id_t hit_trg_id = (trg_id_t) hit.prim.prim_id;
+ const struct triangle_in* hit_trg_in =
+ darray_triangle_in_cdata_get(&desc->scene->triangles_in) + hit_trg_id;
+ const struct triangle_comp* hit_trg_comp =
+ darray_triangle_comp_cdata_get(&desc->triangles_comp) + hit_trg_id;
+ enum side_id hit_side = (hit.normal[2] > 0) ? SIDE_FRONT : SIDE_BACK;
+ const side_id_t hit_side_id = TRGIDxSIDE_2_TRGSIDE(hit_trg_id, hit_side);
+
+ ASSERT(hit.prim.prim_id < desc->scene->ntris);
+ ASSERT(hit.prim.prim_id <= TRG_MAX__);
+
+ /* Not really the root until following links */
+ cc->cc_group_root = hit_trg_comp->component[hit_side];
+#ifndef NDEBUG
+ {
+ const struct cc_descriptor* hit_desc;
+ ASSERT(cc->cc_group_root
+ < darray_cc_descriptor_size_get(connex_components));
+ hit_desc = darray_cc_descriptor_cdata_get(connex_components)
+ + cc->cc_group_root;
+ ASSERT(hit_desc->medium == hit_trg_in->medium[hit_side]);
+ ASSERT(darray_char_cdata_get(&hit_desc->side_membership)[hit_trg_id]
+ & TRGSIDE_IS_FRONT(hit_side) ? FLAG_FRONT : FLAG_BACK);
+ }
+#endif
+ if(hit_trg_in->medium[hit_side] != cc->medium) {
+ /* Medium mismatch!
+ * Model topology is broken. */
+ /* TODO: clean problem reporting. */
+ log_err(desc->scene->dev,
+ "Medium mismatch found between triangle %u %s side and triangle"
+ "%u %s side facing each other.\n",
+ TRGSIDE_2_TRG(hit_side_id),
+ TRGSIDE_IS_FRONT(hit_side) ? "front" : "back",
+ TRGSIDE_2_TRG(cc->max_z_side_id),
+ TRGSIDE_IS_FRONT(cc->max_z_side_id) ? "front" : "back");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ }
+ }
+
+ if(s3d) S3D(device_ref_put(s3d));
+ s3d = NULL;
+ if(s3d_view) S3D(scene_view_ref_put(s3d_view));
+ s3d_view = NULL;
+
+ /* Post-process links to group connex components */
+ printf("\n");
+ FOR_EACH(c, 0, cc_count) {
+ struct cc_descriptor* const cc = descriptors + c;
+ const struct cc_descriptor* other_desc = cc;
+
+ while(other_desc->enclosure_id == CC_GROUP_ID_NONE) {
+ other_desc = darray_cc_descriptor_cdata_get(connex_components)
+ + other_desc->cc_group_root;
+ }
+ ASSERT(other_desc->cc_group_root != CC_GROUP_ROOT_NONE);
+ ASSERT(other_desc->enclosure_id != CC_GROUP_ID_NONE);
+ cc->cc_group_root = other_desc->cc_group_root;
+ cc->enclosure_id = other_desc->enclosure_id;
+ }
+
+exit:
+ /* Local Star3D stuff is no longer useful */
+ if(s3d) S3D(device_ref_put(s3d));
+ if(s3d_scn) S3D(scene_ref_put(s3d_scn));
+ if(s3d_shp) S3D(shape_ref_put(s3d_shp));
+ if(s3d_view) S3D(scene_view_ref_put(s3d_view));
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+scan_edges
+ (struct senc_scene* scn,
+ struct darray_neighbourhood* neighbourhood_by_edge,
+ struct darray_triangle_tmp* triangles_tmp_array)
+{
+ trg_id_t t;
+ struct triangle_in *triangles_in;
+ struct triangle_tmp *triangles_tmp;
+ edge_id_t max_nbedges;
+ /* Htable used to give an id to edges */
+ struct htable_edge_id edge_ids;
+ res_T res = RES_OK;
+
+ ASSERT(scn && neighbourhood_by_edge && triangles_tmp_array);
+ ASSERT((size_t)scn->nuverts + (size_t)scn->nutris + 2 <= EDGE_MAX__);
+
+ /* Make some room for edges. */
+ max_nbedges = (edge_id_t)(scn->nuverts + scn->nutris + 2);
+ htable_edge_id_init(scn->dev->allocator, &edge_ids);
+ res = htable_edge_id_reserve(&edge_ids, max_nbedges);
+ if(res != RES_OK) goto error;
+ res = darray_neighbourhood_reserve(neighbourhood_by_edge, max_nbedges);
+ if(res != RES_OK) goto error;
+ res = darray_triangle_tmp_resize(triangles_tmp_array, scn->nutris);
+ if(res != RES_OK) goto error;
+
+ /* Loop on triangles to register edges. */
+ triangles_in = darray_triangle_in_data_get(&scn->triangles_in);
+ triangles_tmp = darray_triangle_tmp_data_get(triangles_tmp_array);
+ FOR_EACH(t, 0, scn->nutris) {
+ struct darray_neighbour* neighbour_list;
+ struct neighbour_info neighbour;
+ struct edge_neighbourhood neighbourhood;
+ char e;
+ FOR_EACH(e, 0, 3) {
+ edge_id_t* p_id;
+ edge_id_t id;
+
+ /* Create edge. */
+ set_edge(triangles_in[t].vertice_id[e],
+ triangles_in[t].vertice_id[(e + 1) % 3],
+ &neighbourhood.edge, &triangles_tmp[t].reversed_edge[e]);
+ /* Find edge id; create it if not already done. */
+ p_id = htable_edge_id_find(&edge_ids, &neighbourhood.edge);
+
+ if(p_id) {
+ id = *p_id;
+ } else {
+ /* Create id */
+ size_t tmp;
+ tmp = htable_edge_id_size_get(&edge_ids);
+ ASSERT(tmp <= EDGE_MAX__);
+ id = (edge_id_t)tmp;
+ res = htable_edge_id_set(&edge_ids, &neighbourhood.edge, &id);
+ if(res != RES_OK) goto error;
+ darray_neighbour_init(scn->dev->allocator, &neighbourhood.neighbours);
+ res = darray_neighbourhood_push_back(
+ neighbourhood_by_edge, &neighbourhood);
+ if(res != RES_OK) goto error;
+ }
+ /* Add neighbour info to edge's neighbour list */
+ neighbour_list
+ = &darray_neighbourhood_data_get(neighbourhood_by_edge)[id].neighbours;
+ neighbour.trg_id = t;
+ neighbour.edge_rank = e;
+ darray_neighbour_push_back(neighbour_list, &neighbour);
+ }
+ }
+
+exit:
+ htable_edge_id_release(&edge_ids);
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+link_neighbours
+ (struct senc_scene* scn,
+ struct trgside* trgsides,
+ struct darray_neighbourhood* neighbourhood_by_edge,
+ struct darray_triangle_tmp* triangles_tmp_array,
+ struct darray_side_id* side_ids_by_medium)
+{
+ edge_id_t e, edge_count;
+ struct triangle_in *triangles_in;
+ struct triangle_tmp *triangles_tmp;
+ const union double3* vertices;
+ size_t tmp;
+ res_T res = RES_OK;
+
+ ASSERT(scn && neighbourhood_by_edge && triangles_tmp_array
+ && side_ids_by_medium);
+
+ /* Loop on edges.
+ * For each edge sort triangle sides by rotation angle
+ * and connect neighbours. */
+ triangles_in = darray_triangle_in_data_get(&scn->triangles_in);
+ triangles_tmp = darray_triangle_tmp_data_get(triangles_tmp_array);
+ vertices = darray_position_cdata_get(&scn->vertices);
+ tmp = darray_neighbourhood_size_get(neighbourhood_by_edge);
+ ASSERT(tmp <= EDGE_MAX__);
+ edge_count = (edge_id_t)tmp;
+
+ FOR_EACH(e, 0, edge_count) {
+ double edge[3], common_edge[3], basis[9], norm, mz;
+ vrtx_id_t v0, v1, v2;
+ struct edge_neighbourhood* neighbourhood
+ = darray_neighbourhood_data_get(neighbourhood_by_edge) + e;
+ struct darray_neighbour* neighbour_list = &neighbourhood->neighbours;
+ side_id_t i, neighbour_count;
+ char mz_vid;
+ tmp = darray_neighbour_size_get(neighbour_list);
+ ASSERT(tmp <= SIDE_MAX__);
+ neighbour_count = (side_id_t)tmp;
+ ASSERT(neighbour_count);
+ v0 = neighbourhood->edge.vrtx0;
+ v1 = neighbourhood->edge.vrtx1;
+ d3_sub(common_edge, vertices[v1].vec, vertices[v0].vec);
+ if(vertices[v0].pos.z > vertices[v1].pos.z) {
+ mz = vertices[v0].pos.z;
+ mz_vid = 0;
+ } else {
+ mz = vertices[v1].pos.z;
+ mz_vid = 1;
+ }
+ norm = d3_normalize(common_edge, common_edge);
+ ASSERT(norm);
+ d33_basis(basis, common_edge);
+ d33_inverse(basis, basis);
+ FOR_EACH(i, 0, neighbour_count) {
+ struct neighbour_info* neighbour_info
+ = darray_neighbour_data_get(neighbour_list) + i;
+ struct triangle_in *trg_in = triangles_in + neighbour_info->trg_id;
+ struct triangle_tmp *neighbour = triangles_tmp + neighbour_info->trg_id;
+ v2 = trg_in->vertice_id[(neighbour_info->edge_rank + 2) % 3];
+ if(vertices[v2].pos.z > mz) {
+ mz = vertices[v2].pos.z;
+ mz_vid = 2;
+ }
+ neighbour->max_z = mz;
+ neighbour->max_z_vrtx_id = mz_vid;
+ /* Compute rotation angle around common edge */
+ d3_sub(edge, vertices[v2].vec, vertices[v0].vec);
+ d33_muld3(edge, basis, edge);
+ /* TODO: where are invalid triangles detected? */
+ ASSERT(d3_len(edge) && (edge[0] || edge[1]));
+ neighbour_info->angle = atan2(edge[1], edge[0]);
+ if(neighbour_info->angle < 0) neighbour_info->angle += 2 * PI;
+ ASSERT(0 <= neighbour_info->angle && neighbour_info->angle < 2 * PI);
+ }
+ /* Sort triangles by rotation angle */
+ qsort(darray_neighbour_data_get(neighbour_list), neighbour_count,
+ sizeof(struct neighbour_info), neighbour_cmp);
+ /* Link sides.
+ * Create cycles of sides by neighbourhood around common edge. */
+ FOR_EACH(i, 0, neighbour_count) {
+ /* Neighbourhood info for current pair of triangles */
+ const struct neighbour_info* current
+ = darray_neighbour_cdata_get(neighbour_list) + i;
+ const struct neighbour_info* ccw_neighbour
+ = darray_neighbour_cdata_get(neighbour_list) + (i + 1) % neighbour_count;
+ /* Rank of the edge of interest in triangles */
+ const char crt_edge = current->edge_rank;
+ const char ccw_edge = ccw_neighbour->edge_rank;
+ /* User id of current triangles */
+ const trg_id_t crt_id = current->trg_id;
+ const trg_id_t ccw_id = ccw_neighbour->trg_id;
+ /* Facing sides of triangles */
+ const enum side_id crt_side
+ = triangles_tmp[crt_id].reversed_edge[crt_edge] ? SIDE_BACK : SIDE_FRONT;
+ const enum side_id ccw_side
+ = triangles_tmp[ccw_id].reversed_edge[ccw_edge] ? SIDE_FRONT : SIDE_BACK;
+ /* Index of sides in trgsides */
+ const side_id_t crt_side_idx = TRGIDxSIDE_2_TRGSIDE(crt_id, crt_side);
+ const side_id_t ccw_side_idx = TRGIDxSIDE_2_TRGSIDE(ccw_id, ccw_side);
+ /* Side ptrs */
+ struct trgside* const p_crt_side = trgsides + crt_side_idx;
+ struct trgside* const p_ccw_side = trgsides + ccw_side_idx;
+ /* Link sides */
+ p_crt_side->facing_side_id[crt_edge] = ccw_side_idx;
+ p_ccw_side->facing_side_id[ccw_edge] = crt_side_idx;
+ /* Record sides by medium */
+ p_crt_side->medium = triangles_in[crt_id].medium[crt_side];
+ p_ccw_side->medium = triangles_in[ccw_id].medium[ccw_side];
+ ASSERT(p_crt_side->medium < scn->nmeds);
+ ASSERT(p_ccw_side->medium < scn->nmeds);
+ add_side_to_medium_list(scn, side_ids_by_medium + p_crt_side->medium,
+ trgsides, crt_side_idx);
+ add_side_to_medium_list(scn, side_ids_by_medium + p_ccw_side->medium,
+ trgsides, ccw_side_idx);
+ }
+ }
+ return res;
+}
+
+static res_T
+build_result
+ (struct senc_descriptor* desc,
+ struct darray_cc_descriptor* connex_components)
+{
+ res_T res = RES_OK;
+ struct mem_allocator* alloc;
+ const struct cc_descriptor* cc_descriptors;
+ struct enclosure_data* enclosures;
+ char* side_membership = NULL;
+ const struct triangle_in* triangles_in;
+ const union double3* positions;
+ struct htable_vrtx_id vtable;
+ size_t tmp;
+ component_id_t cc_count, c;
+ enclosure_id_t e;
+ trg_id_t* approx_trg_counts;
+
+ ASSERT(desc && connex_components);
+
+ alloc = descriptor_get_allocator(desc);
+ tmp = darray_cc_descriptor_size_get(connex_components);
+ ASSERT(tmp < COMPONENT_MAX__);
+ cc_count = (component_id_t)tmp;
+ cc_descriptors = darray_cc_descriptor_cdata_get(connex_components);
+ darray_enclosure_resize(&desc->enclosures, desc->enclosures_count);
+ enclosures = darray_enclosure_data_get(&desc->enclosures);
+ triangles_in = darray_triangle_in_cdata_get(&desc->scene->triangles_in);
+ positions = darray_position_cdata_get(&desc->scene->vertices);
+ /* Set some enclosure data */
+ approx_trg_counts
+ = MEM_CALLOC(alloc, desc->enclosures_count, sizeof(trg_id_t));
+ if(!approx_trg_counts) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ FOR_EACH(e, 0, desc->enclosures_count) {
+ struct enclosure_data* enc = enclosures + e;
+ ASSERT(e < UINT_MAX);
+ enc->header.enclosure_id = (unsigned)e; /* Back to API type */
+ enc->header.is_infinite = (e == 0);
+ }
+ /* Process components to feed enclosures */
+ FOR_EACH(c, 0, cc_count) {
+ const struct cc_descriptor* cc = cc_descriptors + c;
+ const enclosure_id_t e_id = cc->enclosure_id;
+ struct enclosure_data* enc = enclosures + e_id;
+ ASSERT(enc->header.enclosed_medium == MEDIUM_NULL__ /* Unset */
+ || enc->header.enclosed_medium == cc->medium); /* Same medium */
+ ASSERT(enc->header.enclosed_medium < UINT_MAX);
+ enc->header.enclosed_medium = (unsigned)cc->medium; /* Back to API type */
+ approx_trg_counts[e_id] += cc->triangle_count;
+ }
+ side_membership
+ = MEM_ALLOC(alloc, desc->scene->ntris * sizeof(*side_membership));
+ if(!side_membership) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ htable_vrtx_id_init(alloc, &vtable);
+ FOR_EACH(e, 0, desc->enclosures_count) {
+ struct enclosure_data* enc = enclosures + e;
+ trg_id_t t;
+ memset(side_membership, 0, desc->scene->ntris * sizeof(*side_membership));
+ /* Process all CC enclosures_count times to limit memory footprint. */
+ FOR_EACH(c, 0, cc_count) {
+ const struct cc_descriptor* cc = cc_descriptors + c;
+ const char* cc_membership
+ = darray_char_cdata_get(&cc->side_membership);
+ if(cc->enclosure_id != e) continue;
+ FOR_EACH(t, 0, desc->scene->ntris) side_membership[t] |= cc_membership[t];
+ }
+ /* Translate membership into a side and vertex lists. */
+ res = darray_triangle_in_reserve(&enc->sides, approx_trg_counts[e]);
+ if(res != RES_OK) goto error;
+ res = darray_position_reserve(&enc->vertices, approx_trg_counts[e] / 2);
+ if(res != RES_OK) goto error;
+ /* New vertex numbering scheme local to the enclosure */
+ htable_vrtx_id_clear(&vtable);
+ if(res != RES_OK) goto error;
+ FOR_EACH(t, 0, desc->scene->ntris) {
+ const struct triangle_in* trg_in = triangles_in + t;
+ struct triangle_in trg;
+ int i;
+ if(!side_membership[t]) continue;
+ ++enc->header.unique_triangle_count;
+ FOR_EACH(i, 0, 3) {
+ vrtx_id_t* id = htable_vrtx_id_find(&vtable, trg_in->vertice_id + i);
+ if(id) {
+ trg.vertice_id[i] = *id; /* Known vertex */
+ } else {
+ /* Create new association */
+ tmp = htable_vrtx_id_size_get(&vtable);
+ ASSERT(tmp == darray_position_size_get(&enc->vertices));
+ ASSERT(tmp < VRTX_MAX__);
+ trg.vertice_id[i] = (vrtx_id_t)tmp;
+ res = htable_vrtx_id_set(
+ &vtable, trg_in->vertice_id + i, trg.vertice_id + i);
+ if(res != RES_OK) goto error;
+ res = darray_position_push_back(
+ &enc->vertices, positions + trg_in->vertice_id[i]);
+ if(res != RES_OK) goto error;
+ ++enc->header.vertices_count;
+ }
+ }
+ FOR_EACH(i, 0, 2) trg.medium[i] = trg_in->medium[i];
+ if(side_membership[t] & FLAG_FRONT) {
+ ++enc->header.triangle_count;
+ res = darray_triangle_in_push_back(&enc->sides, &trg);
+ if(res != RES_OK) goto error;
+ }
+ if(side_membership[t] & FLAG_BACK) {
+ ++enc->header.triangle_count;
+ triangle_in_flip(&trg);
+ res = darray_triangle_in_push_back(&enc->sides, &trg);
+ if(res != RES_OK) goto error;
+ }
+ }
+ }
+
+exit:
+ htable_vrtx_id_release(&vtable);
+ if(side_membership) MEM_RM(alloc, side_membership);
+ if(approx_trg_counts) MEM_RM(alloc, approx_trg_counts);
+ return res;
+error:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+senc_scene_analyze(struct senc_scene* scn, struct senc_descriptor** out_desc)
+{
+ res_T res = RES_OK;
+ unsigned m;
+ struct senc_descriptor* desc = NULL;
+ /* By triangle tmp data */
+ struct darray_triangle_tmp triangles_tmp;
+ char triangles_tmp_initialized = 0;
+ /* Arrays of trgside ids, organized by medium */
+ struct darray_side_id* side_ids_by_medium = NULL;
+ /* Array of connex components.
+ * They are refered to by arrays of ids. */
+ struct darray_cc_descriptor connex_components;
+ char connex_components_initialized = 0;
+ /* Triangle neighbourhood by edge. */
+ struct darray_neighbourhood neighbourhood_by_edge;
+ char neighbourhood_by_edge_initialized = 0;
+ /* Array of triangle sides. */
+ struct trgside* trgsides = NULL;
+
+ if(!scn || !out_desc) return RES_BAD_ARG;
+
+ desc = descriptor_create(scn);
+ if(!desc) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ if(!scn->nutris) goto exit;
+
+ darray_triangle_tmp_init(scn->dev->allocator, &triangles_tmp);
+ triangles_tmp_initialized = 1;
+ darray_neighbourhood_init(scn->dev->allocator, &neighbourhood_by_edge);
+ neighbourhood_by_edge_initialized = 1;
+
+ /* Step 1: */
+ res = scan_edges(scn, &neighbourhood_by_edge, &triangles_tmp);
+ if(res != RES_OK) {
+ log_err(scn->dev, "%s: could not scan edges.\n", FUNC_NAME);
+ goto error;
+ }
+
+ side_ids_by_medium
+ = MEM_ALLOC(scn->dev->allocator, scn->nmeds * sizeof(struct darray_uint));
+ if(!side_ids_by_medium) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ FOR_EACH(m, 0, scn->nmeds) {
+ struct darray_side_id* const s = side_ids_by_medium + m;
+ darray_side_id_init(scn->dev->allocator, s);
+ }
+ trgsides
+ = MEM_CALLOC(scn->dev->allocator, 2 * scn->nutris, sizeof(struct trgside));
+ if(!trgsides) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ res = link_neighbours(scn, trgsides, &neighbourhood_by_edge,
+ &triangles_tmp, side_ids_by_medium);
+ if(res != RES_OK) {
+ log_err(scn->dev, "%s: could not link neighbours.\n", FUNC_NAME);
+ goto error;
+ }
+
+ darray_neighbourhood_release(&neighbourhood_by_edge);
+ neighbourhood_by_edge_initialized = 0;
+ darray_cc_descriptor_init(scn->dev->allocator, &connex_components);
+ connex_components_initialized = 1;
+
+ res = extract_connex_components(desc, trgsides, &connex_components,
+ &triangles_tmp, side_ids_by_medium);
+ if(res != RES_OK) {
+ log_err(scn->dev,
+ "%s: could not extract connex components from scene.\n", FUNC_NAME);
+ goto error;
+ }
+
+ darray_triangle_tmp_release(&triangles_tmp);
+ triangles_tmp_initialized = 0;
+ if(side_ids_by_medium) {
+ FOR_EACH(m, 0, scn->nmeds) {
+ struct darray_side_id* cc = side_ids_by_medium + m;
+ darray_side_id_release(cc);
+ }
+ MEM_RM(scn->dev->allocator, side_ids_by_medium);
+ }
+ side_ids_by_medium = NULL;
+
+ res = group_connex_components(desc, trgsides, &connex_components);
+ if(res != RES_OK) {
+ log_err(scn->dev,
+ "%s: could not group connex components from scene.\n", FUNC_NAME);
+ goto error;
+ }
+
+ /* Build result. */
+ res = build_result(desc, &connex_components);
+ if(res != RES_OK) {
+ log_err(scn->dev, "%s: could not build result.\n", FUNC_NAME);
+ goto error;
+ }
+
+ darray_triangle_comp_purge(&desc->triangles_comp);
+
+
+exit:
+ if(connex_components_initialized)
+ darray_cc_descriptor_release(&connex_components);
+ if(neighbourhood_by_edge_initialized)
+ darray_neighbourhood_release(&neighbourhood_by_edge);
+ if(triangles_tmp_initialized) darray_triangle_tmp_release(&triangles_tmp);
+ if(side_ids_by_medium) {
+ FOR_EACH(m, 0, scn->nmeds) {
+ struct darray_side_id* cc = side_ids_by_medium + m;
+ darray_side_id_release(cc);
+ }
+ MEM_RM(scn->dev->allocator, side_ids_by_medium);
+ }
+ if(trgsides) MEM_RM(scn->dev->allocator, trgsides);
+ if(desc) *out_desc = desc;
+ return res;
+error:
+ if(desc) SENC(descriptor_ref_put(desc));
+ desc = NULL;
+ goto exit;
+}
diff --git a/src/senc_scene_analyze_c.h b/src/senc_scene_analyze_c.h
@@ -0,0 +1,319 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef SENC_SCNENE_ANALYZE_C_H
+#define SENC_SCNENE_ANALYZE_C_H
+
+#include "senc_scene_c.h"
+#include "senc_internal_types.h"
+
+#include <rsys/mem_allocator.h>
+#include <rsys/dynamic_array_char.h>
+#include <rsys/hash_table.h>
+#include <rsys/double3.h>
+
+
+/* This one is used as an index to arrays */
+enum side_id {
+ SIDE_FRONT = 0,
+ SIDE_BACK = 1
+};
+
+/* This one is used as flag */
+enum side_flag {
+ FLAG_FRONT = BIT(0),
+ FLAG_BACK = BIT(1)
+};
+
+enum list_id {
+ FLAG_NO_LIST = 0,
+ FLAG_LIST_BY_MEDIUM = BIT(1),
+ FLAG_LIST_COMPONENT = BIT(2),
+ FLAG_WAITING_STACK = BIT(3),
+ FLAG_ANY_LIST = 0xFF
+};
+
+/* Triangle edge struct and basic functions */
+struct trg_edge {
+ vrtx_id_t vrtx0, vrtx1;
+};
+
+FINLINE int
+edge_ok(const struct trg_edge* edge) {
+ return(edge
+ && edge->vrtx0 <= VRTX_MAX__
+ && edge->vrtx1 <= VRTX_MAX__
+ && edge->vrtx0 < edge->vrtx1);
+}
+
+FINLINE void
+set_edge
+ (const vrtx_id_t vrtx0,
+ const vrtx_id_t vrtx1,
+ struct trg_edge* edge,
+ char* reversed)
+{
+ ASSERT(edge && reversed && vrtx0 != vrtx1);
+ if(vrtx0 < vrtx1) {
+ edge->vrtx0 = vrtx0;
+ edge->vrtx1 = vrtx1;
+ *reversed = 0; /* Non reversed edge */
+ } else {
+ edge->vrtx0 = vrtx1;
+ edge->vrtx1 = vrtx0;
+ *reversed = 1; /* Reversed edge */
+ }
+ ASSERT(edge_ok(edge));
+}
+
+FINLINE int
+edge_eq(const struct trg_edge* e1, const struct trg_edge* e2)
+{
+ ASSERT(edge_ok(e1) && edge_ok(e2));
+ return e1->vrtx0 == e2->vrtx0 && e1->vrtx1 == e2->vrtx1;
+}
+
+/* Information kept during the building of side groups. */
+struct trgside {
+ /* Rank of the trgside facing this trgside through its edges */
+ side_id_t facing_side_id[3];
+ /* Id of this trgside's medium */
+ medium_id_t medium;
+ /* The list containing the trgside; made of enum list_id flags */
+ char list_id;
+
+ /* Implicit information that we don't need to store:
+ * - triangle_id
+ * - side
+ * This is due to the memory layout of the elt darray:
+ * front(trg_0), back(trg_0), front(trg_1), back(trg_1), ... */
+
+#ifndef NDEBUG
+ component_id_t member_of_cc;
+#endif
+};
+
+static FINLINE trg_id_t
+TRGSIDE_2_TRG(side_id_t s) {
+ ASSERT(((size_t)s >> 1) <= TRG_MAX__);
+ return s >> 1; }
+
+static FINLINE int
+TRGSIDE_IS_FRONT(side_id_t s) {
+ return (s & 1) == 0;
+}
+
+static FINLINE side_id_t
+TRGSIDE_OPPOSITE(side_id_t s) {
+ return s ^ (~1);
+}
+
+static FINLINE enum side_id
+TRGSIDE_2_SIDE(side_id_t s) {
+ return (s & 1) ? SIDE_BACK : SIDE_FRONT;
+}
+
+static FINLINE side_id_t
+TRGIDxSIDE_2_TRGSIDE(trg_id_t t, enum side_id i) {
+ ASSERT((((size_t)t << 1) | (i == SIDE_BACK)) < SIDE_MAX__);
+ return (side_id_t)((t << 1) | (i == SIDE_BACK));
+}
+
+/* Descriptors for connex component.
+ * Define lists of trg sides starting from a given head.
+ * Also keeps the maximum z info of the component
+ * along with the associated triangle id,
+ * a star3D id */
+#define CC_ID_NONE USHRT_MAX
+#define CC_GROUP_ROOT_NONE USHRT_MAX
+#define CC_GROUP_ROOT_INFINITE (USHRT_MAX-1)
+#define CC_GROUP_ID_NONE USHRT_MAX
+struct cc_descriptor {
+ double max_vrtx[3];
+ double max_z_nz;
+ side_id_t max_z_side_id;
+ vrtx_id_t max_z_vrtx_id;
+ trg_id_t triangle_count;
+ medium_id_t medium;
+ component_id_t cc_id;
+ component_id_t cc_group_root;
+ enclosure_id_t enclosure_id;
+ /* TODO: use only 1 bit per side (now 1 char per triangle) */
+ struct darray_char side_membership;
+};
+extern const struct cc_descriptor CC_DESCRIPTOR_NULL;
+
+static FINLINE void
+cc_descriptor_init(struct mem_allocator* alloc, struct cc_descriptor* data)
+{
+ ASSERT(data);
+ *data = CC_DESCRIPTOR_NULL;
+ darray_char_init(alloc, &data->side_membership);
+}
+
+static FINLINE void
+cc_descriptor_release(struct cc_descriptor* data)
+{
+ ASSERT(data);
+ darray_char_release(&data->side_membership);
+}
+
+static FINLINE void
+cc_descriptor_clear(struct cc_descriptor* data)
+{
+ d3_set(data->max_vrtx, CC_DESCRIPTOR_NULL.max_vrtx);
+ data->max_z_nz = CC_DESCRIPTOR_NULL.max_z_nz;
+ data->max_z_side_id = CC_DESCRIPTOR_NULL.max_z_side_id;
+ data->max_z_vrtx_id = CC_DESCRIPTOR_NULL.max_z_vrtx_id;
+ data->triangle_count = CC_DESCRIPTOR_NULL.triangle_count;
+ data->medium = CC_DESCRIPTOR_NULL.medium;
+ data->cc_id = CC_DESCRIPTOR_NULL.cc_id;
+ data->cc_group_root = CC_DESCRIPTOR_NULL.cc_group_root;
+ data->enclosure_id = CC_DESCRIPTOR_NULL.enclosure_id;
+ darray_char_clear(&data->side_membership);
+}
+
+static FINLINE void
+cc_descriptor_purge(struct cc_descriptor* data)
+{
+ d3_set(data->max_vrtx, CC_DESCRIPTOR_NULL.max_vrtx);
+ data->max_z_nz = CC_DESCRIPTOR_NULL.max_z_nz;
+ data->max_z_side_id = CC_DESCRIPTOR_NULL.max_z_side_id;
+ data->max_z_vrtx_id = CC_DESCRIPTOR_NULL.max_z_vrtx_id;
+ data->triangle_count = CC_DESCRIPTOR_NULL.triangle_count;
+ data->medium = CC_DESCRIPTOR_NULL.medium;
+ data->cc_id = CC_DESCRIPTOR_NULL.cc_id;
+ data->cc_group_root = CC_DESCRIPTOR_NULL.cc_group_root;
+ data->enclosure_id = CC_DESCRIPTOR_NULL.enclosure_id;
+ darray_char_purge(&data->side_membership);
+}
+
+static FINLINE res_T
+cc_descriptor_copy(struct cc_descriptor* dst, const struct cc_descriptor* src)
+{
+ ASSERT(dst && src);
+ d3_set(dst->max_vrtx, src->max_vrtx);
+ dst->max_z_nz = src->max_z_nz;
+ dst->max_z_side_id = src->max_z_side_id;
+ dst->max_z_vrtx_id = src->max_z_vrtx_id;
+ dst->triangle_count = src->triangle_count;
+ dst->medium = src->medium;
+ dst->cc_id = src->cc_id;
+ dst->cc_group_root = src->cc_group_root;
+ dst->enclosure_id = src->enclosure_id;
+ return darray_char_copy(&dst->side_membership, &src->side_membership);
+}
+
+static FINLINE res_T
+cc_descriptor_copy_and_release(struct cc_descriptor* dst, struct cc_descriptor* src)
+{
+ ASSERT(dst && src);
+ d3_set(dst->max_vrtx, src->max_vrtx);
+ dst->max_z_nz = src->max_z_nz;
+ dst->max_z_side_id = src->max_z_side_id;
+ dst->max_z_vrtx_id = src->max_z_vrtx_id;
+ dst->triangle_count = src->triangle_count;
+ dst->medium = src->medium;
+ dst->cc_id = src->cc_id;
+ dst->cc_group_root = src->cc_group_root;
+ dst->enclosure_id = src->enclosure_id;
+ return darray_char_copy_and_release(&dst->side_membership, &src->side_membership);
+}
+
+#define DARRAY_NAME cc_descriptor
+#define DARRAY_DATA struct cc_descriptor
+#define DARRAY_FUNCTOR_INIT cc_descriptor_init
+#define DARRAY_FUNCTOR_RELEASE cc_descriptor_release
+#define DARRAY_FUNCTOR_COPY cc_descriptor_copy
+#define DARRAY_FUNCTOR_COPY_AND_RELEASE cc_descriptor_copy_and_release
+#include <rsys/dynamic_array.h>
+
+/* Triangle information.
+ * Depending on lifespan, information is kept in different places:
+ * - triangle_in for user provided information (kept in scene)
+ * - triangle_comp for information describing components (kept in senc_descriptor)
+ * - triangle_tmp for tmp information (kept until triangle_comp is ready) */
+struct triangle_tmp {
+ /* Are the edges of the triangle defined in the same order than
+ * the edges they are linked to? */
+ char reversed_edge[3];
+ /* tmp data used to find the +Z-most vertex of components */
+ char max_z_vrtx_id;
+ double max_z;
+};
+#define DARRAY_NAME triangle_tmp
+#define DARRAY_DATA struct triangle_tmp
+#include <rsys/dynamic_array.h>
+
+#define HTABLE_NAME edge_id
+#define HTABLE_KEY struct trg_edge
+#define HTABLE_DATA edge_id_t
+#define HTABLE_KEY_FUNCTOR_EQ edge_eq
+#include <rsys/hash_table.h>
+
+struct neighbour_info {
+ trg_id_t trg_id;
+ /* Rank of the vrtx in the triangle (in [0 2]) */
+ char edge_rank;
+ double angle;
+};
+#define DARRAY_NAME neighbour
+#define DARRAY_DATA struct neighbour_info
+#include <rsys/dynamic_array.h>
+
+struct edge_neighbourhood {
+ struct trg_edge edge;
+ struct darray_neighbour neighbours;
+};
+static void
+neighbourhood_init
+ (struct mem_allocator* alloc,
+ struct edge_neighbourhood* n)
+{
+ ASSERT(n);
+ darray_neighbour_init(alloc, &n->neighbours);
+}
+static res_T
+neighbourhood_copy
+ (struct edge_neighbourhood* dst,
+ const struct edge_neighbourhood* src)
+{
+ ASSERT(src && dst);
+ dst->edge = src->edge;
+ return darray_neighbour_copy(&dst->neighbours, &src->neighbours);
+}
+static void
+neighbourhood_release(struct edge_neighbourhood* n) {
+ ASSERT(n);
+ darray_neighbour_release(&n->neighbours);
+}
+static res_T
+neighbourhood_copy_and_release
+ (struct edge_neighbourhood* dst,
+ struct edge_neighbourhood* src)
+{
+ ASSERT(src && dst);
+ dst->edge = src->edge;
+ return darray_neighbour_copy_and_release(&dst->neighbours, &src->neighbours);
+}
+#define DARRAY_NAME neighbourhood
+#define DARRAY_DATA struct edge_neighbourhood
+#define DARRAY_FUNCTOR_INIT neighbourhood_init
+#define DARRAY_FUNCTOR_COPY neighbourhood_copy
+#define DARRAY_FUNCTOR_RELEASE neighbourhood_release
+#define DARRAY_FUNCTOR_COPY_AND_RELEASE neighbourhood_copy_and_release
+#include <rsys/dynamic_array.h>
+
+#endif /* SENC_SCNENE_ANALYZE_C_H */
diff --git a/src/senc_scene_c.h b/src/senc_scene_c.h
@@ -0,0 +1,215 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef SENC_SCNENE_C_H
+#define SENC_SCNENE_C_H
+
+#include "senc_internal_types.h"
+
+#include <rsys/ref_count.h>
+#include <rsys/dynamic_array.h>
+#include <rsys/hash_table.h>
+#include <rsys/dynamic_array_char.h>
+
+struct mem_allocator;
+
+union double3 {
+ struct {
+ double x, y, z;
+ } pos;
+ double vec[3];
+};
+#define DARRAY_NAME position
+#define DARRAY_DATA union double3
+#include <rsys/dynamic_array.h>
+/* Triangle information.
+ * Depending on lifespan, information is kept in different places:
+ * - triangle_in for user provided information (kept in scene)
+ * - triangle_comp for information describing components (kept in senc_descriptor)
+ * - triangle_tmp for tmp information (kept until triangle_comp is ready) */
+struct triangle_in {
+ /* Ids of the triangle's vertices */
+ vrtx_id_t vertice_id[3];
+ /* Ids of this triangle's mediums */
+ medium_id_t medium[2];
+};
+
+#ifndef NDEBUG
+static FINLINE void
+triangle_in_init(struct mem_allocator* alloc, struct triangle_in* trg) {
+ int i;
+ (void) alloc;
+ ASSERT(trg);
+ FOR_EACH(i, 0, 3) trg->vertice_id[i] = VRTX_NULL__;
+ FOR_EACH(i, 0, 2) trg->medium[i] = MEDIUM_NULL__;
+}
+#define DARRAY_FUNCTOR_INIT triangle_in_init
+#endif
+
+#define DARRAY_NAME triangle_in
+#define DARRAY_DATA struct triangle_in
+#include <rsys/dynamic_array.h>
+
+static FINLINE void
+triangle_in_flip(struct triangle_in* trg) {
+ vrtx_id_t v;
+ medium_id_t m;
+ ASSERT(trg);
+ v = trg->vertice_id[1];
+ trg->vertice_id[1] = trg->vertice_id[2];
+ trg->vertice_id[2] = v;
+ m = trg->medium[0];
+ trg->medium[0] = trg->medium[1];
+ trg->medium[1] = m;
+}
+
+FINLINE int
+vrtx_eq(const union double3* v1, const union double3* v2)
+{
+ ASSERT(v1 && v2);
+ return (v1->pos.x == v2->pos.x
+ && v1->pos.y == v2->pos.y
+ && v1->pos.z == v2->pos.z);
+}
+
+#define HTABLE_NAME vrtx
+#define HTABLE_KEY union double3
+#define HTABLE_DATA vrtx_id_t
+#define HTABLE_KEY_FUNCTOR_EQ vrtx_eq
+#include <rsys/hash_table.h>
+
+#define DARRAY_NAME vrtx_id
+#define DARRAY_DATA vrtx_id_t
+#include <rsys/dynamic_array.h>
+
+union vrtx_id3 {
+ struct {
+ vrtx_id_t v0, v1, v2;
+ } pos;
+ vrtx_id_t vec[3];
+};
+
+FINLINE char /* Return 1 if reversed */
+trg_make_key(union vrtx_id3* k, const vrtx_id_t t[3])
+{
+ ASSERT(t);
+ ASSERT(t[0] != t[1] && t[0] != t[2] && t[1] != t[2]);
+ if(t[0] < t[2]) {
+ if(t[0] < t[1]) {
+ k->vec[0] = t[0];
+ if(t[1] < t[2]) {
+ k->vec[1] = t[1];
+ k->vec[2] = t[2];
+ return 0;
+ } else {
+ k->vec[1] = t[2];
+ k->vec[2] = t[1];
+ return 1;
+ }
+ } else {
+ k->vec[0] = t[1];
+ if(t[0] < t[2]) {
+ k->vec[1] = t[0];
+ k->vec[2] = t[2];
+ return 1;
+ } else {
+ k->vec[1] = t[2];
+ k->vec[2] = t[0];
+ return 0;
+ }
+ }
+ } else if(t[2] < t[1]) {
+ k->vec[0] = t[2];
+ if(t[0] < t[1]) {
+ k->vec[1] = t[0];
+ k->vec[2] = t[1];
+ return 0;
+ }
+ else {
+ k->vec[1] = t[1];
+ k->vec[2] = t[0];
+ return 1;
+ }
+ }
+ else {
+ k->vec[0] = t[1];
+ if(t[0] < t[2]) {
+ k->vec[1] = t[0];
+ k->vec[2] = t[2];
+ return 1;
+ }
+ else {
+ k->vec[1] = t[2];
+ k->vec[2] = t[0];
+ return 0;
+ }
+ }
+}
+
+FINLINE int
+trg_key_eq(const union vrtx_id3* k1, const union vrtx_id3* k2)
+{
+ ASSERT(k1 && k2);
+ ASSERT(k1->vec[0] < k1->vec[1] && k1->vec[1] < k1->vec[2]);
+ ASSERT(k2->vec[0] < k2->vec[1] && k2->vec[1] < k2->vec[2]);
+ return (k1->vec[0] == k2->vec[0])
+ && (k1->vec[1] == k2->vec[1])
+ && (k1->vec[2] == k2->vec[2]);
+}
+
+#define HTABLE_NAME trg
+#define HTABLE_KEY union vrtx_id3
+#define HTABLE_DATA trg_id_t
+#define HTABLE_KEY_FUNCTOR_EQ trg_key_eq
+#include <rsys/hash_table.h>
+
+#define DARRAY_NAME trg_id
+#define DARRAY_DATA trg_id_t
+#include <rsys/dynamic_array.h>
+
+struct senc_scene {
+ /* Triangle information as given by user; no duplicates here */
+ struct darray_triangle_in triangles_in;
+
+ /* Vertex information as given by user; no duplicates here */
+ struct darray_position vertices;
+
+ /* Htables used to detect duplicate vertices.
+ * As we rely on edges (i.e. vertice IDs) to build
+ * neighbourhoods, we need vertice unicity. */
+ /* Keep each unique vertex; no duplicates here. */
+ struct htable_vrtx unique_vertices;
+ /* Associate each vertex with its rank in unique_vertices. */
+ struct darray_vrtx_id unique_vertice_ids;
+
+ /* Htables used to detect duplicate triangles. */
+ /* Keep each unique triangle; no duplicates here. */
+ struct htable_trg unique_triangles;
+ /* Associate each triangle with its rank in unique_triangles... */
+ struct darray_trg_id unique_triangle_ids;
+ /* ... and a bool indicating reverse vertices order. */
+ struct darray_char triangle_reversed;
+
+ /* Keep sizes */
+ unsigned ngeoms; /* Not used yet (just counted). */
+ trg_id_t ntris, nutris; /* Trg count, unique trg count */
+ vrtx_id_t nverts, nuverts; /* Vrtx count, unique vrtx count */
+ medium_id_t nmeds;
+
+ ref_T ref;
+ struct senc_device* dev;
+};
+
+#endif /* SENC_SCNENE_C_H */
diff --git a/src/test_senc_cube_behind_cube.c b/src/test_senc_cube_behind_cube.c
@@ -0,0 +1,84 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "senc.h"
+#include "test_senc_utils.h"
+
+#include <rsys/double3.h>
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc_descriptor* desc = NULL;
+ struct senc_device* dev = NULL;
+ struct senc_scene* scn = NULL;
+ struct context ctx;
+ (void)argc, (void)argv;
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK);
+ CHK(senc_device_create
+ (NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) == RES_OK);
+
+ /* Create the scene */
+ CHK(senc_scene_create(dev, 2, &scn) == RES_OK);
+
+ ctx.positions = box_vertices;
+ ctx.indices = box_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d3(ctx.offset, 0, 0, 0);
+ ctx.front_medium = 0;
+ ctx.back_medium = 1;
+
+ /* First cube */
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ /* +Z from the first cube,
+ * big enough to prevent rays from the first cube to miss this one */
+ d3(ctx.offset, -2, -2, 20);
+ ctx.scale = 5;
+
+ /* Second cube */
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc_scene_analyze(scn, &desc) == RES_OK);
+
+ /* Even further in +Z, even bigger */
+ d3(ctx.offset, -3, -3, 30);
+ ctx.scale = 7;
+ ctx.front_medium = 1;
+ ctx.back_medium = 0;
+
+ /* Third cube */
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ if(desc) CHK(senc_descriptor_ref_put(desc) == RES_OK);
+ desc = NULL;
+ CHK(senc_scene_analyze(scn, &desc) == RES_BAD_ARG);
+
+ CHK(senc_scene_ref_put(scn) == RES_OK);
+ CHK(senc_device_ref_put(dev) == RES_OK);
+ if(desc) CHK(senc_descriptor_ref_put(desc) == RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+ return 0;
+}
diff --git a/src/test_senc_cube_in_cube.c b/src/test_senc_cube_in_cube.c
@@ -0,0 +1,90 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "senc.h"
+#include "test_senc_utils.h"
+
+#include <rsys/double3.h>
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc_descriptor* desc = NULL;
+ struct senc_device* dev = NULL;
+ struct senc_scene* scn = NULL;
+ struct context ctx;
+ (void)argc, (void)argv;
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK);
+ CHK(senc_device_create
+ (NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) == RES_OK);
+
+ /* Create the scene */
+ CHK(senc_scene_create(dev, 2, &scn) == RES_OK);
+
+ ctx.positions = box_vertices;
+ ctx.indices = box_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d3(ctx.offset, 0, 0, 0);
+ /* Smallest cube exterior is medium 0 */
+ ctx.front_medium = 0;
+ /* Smallest cube interior is medium 1 */
+ ctx.back_medium = 1;
+
+ /* First cube */
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ d3(ctx.offset, -1, -1, -1);
+ ctx.scale = 3;
+ /* Bigger cube exterior is medium 1 */
+ /* Bigger cube interior is medium 0 */
+ ctx.reverse_vrtx = 1;
+
+ /* Second cube */
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc_scene_analyze(scn, &desc) == RES_OK);
+
+ d3(ctx.offset, -4, -4, -4);
+ ctx.scale = 10;
+ ctx.reverse_vrtx = 1;
+ ctx.reverse_med = 1;
+ /* Biggest cube exterior is medium 1 */
+ ctx.front_medium = 1;
+ /* Biggest cube interior is medium 0 */
+ ctx.back_medium = 0; /* mismatch with cube 2 */
+
+ /* Third cube */
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ if(desc) CHK(senc_descriptor_ref_put(desc) == RES_OK);
+ desc = NULL;
+ CHK(senc_scene_analyze(scn, &desc) == RES_BAD_ARG);
+
+ CHK(senc_scene_ref_put(scn) == RES_OK);
+ CHK(senc_device_ref_put(dev) == RES_OK);
+ if(desc) CHK(senc_descriptor_ref_put(desc) == RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+ return 0;
+}
diff --git a/src/test_senc_descriptor.c b/src/test_senc_descriptor.c
@@ -0,0 +1,121 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "senc.h"
+#include "test_senc_utils.h"
+
+#include <rsys/float3.h>
+#include <rsys/double3.h>
+
+#include <star/s3d.h>
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc_device* dev = NULL;
+ struct senc_scene* scn = NULL;
+ struct senc_descriptor* desc = NULL;
+ struct senc_enclosure* enc = NULL;
+ struct context ctx;
+ unsigned count;
+ (void) argc, (void) argv;
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK);
+ CHK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev)
+ == RES_OK);
+
+ CHK(senc_scene_create(dev, 2, &scn) == RES_OK);
+
+ /* A 3D cube */
+ ctx.positions = box_vertices;
+ ctx.indices = box_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d3(ctx.offset, 0, 0, 0);
+ ctx.front_medium = 0;
+ ctx.back_medium = 1;
+
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc_scene_analyze(scn, &desc) == RES_OK);
+
+ CHK(senc_descriptor_ref_get(desc) == RES_OK);
+ CHK(senc_descriptor_ref_get(NULL) == RES_BAD_ARG);
+ CHK(senc_descriptor_ref_put(NULL) == RES_BAD_ARG);
+ CHK(senc_descriptor_ref_put(desc) == RES_OK);
+
+ CHK(senc_descriptor_get_enclosure_count(NULL, &count) == RES_BAD_ARG);
+ CHK(senc_descriptor_get_enclosure_count(desc, NULL) == RES_BAD_ARG);
+ CHK(senc_descriptor_get_enclosure_count(NULL, NULL) == RES_BAD_ARG);
+ CHK(senc_descriptor_get_enclosure_count(desc, &count) == RES_OK);
+
+ CHK(count == 2);
+
+ CHK(senc_descriptor_get_enclosure(NULL, 0, &enc) == RES_BAD_ARG);
+ CHK(senc_descriptor_get_enclosure(desc, count, &enc) == RES_BAD_ARG);
+ CHK(senc_descriptor_get_enclosure(desc, 0, NULL) == RES_BAD_ARG);
+ CHK(senc_descriptor_get_enclosure(NULL, count, &enc) == RES_BAD_ARG);
+ CHK(senc_descriptor_get_enclosure(NULL, 0, NULL) == RES_BAD_ARG);
+ CHK(senc_descriptor_get_enclosure(desc, count, NULL) == RES_BAD_ARG);
+ CHK(senc_descriptor_get_enclosure(NULL, count, NULL) == RES_BAD_ARG);
+ CHK(senc_descriptor_get_enclosure(desc, 0, &enc) == RES_OK);
+
+ CHK(senc_descriptor_get_global_vertices_count(NULL, &count) == RES_BAD_ARG);
+ CHK(senc_descriptor_get_global_vertices_count(desc, NULL) == RES_BAD_ARG);
+ CHK(senc_descriptor_get_global_vertices_count(desc, &count) == RES_OK);
+ CHK(count == box_nvertices);
+
+ CHK(senc_descriptor_get_global_triangle_count(NULL, &count) == RES_BAD_ARG);
+ CHK(senc_descriptor_get_global_triangle_count(desc, NULL) == RES_BAD_ARG);
+ CHK(senc_descriptor_get_global_triangle_count(desc, &count) == RES_OK);
+ CHK(count == box_ntriangles);
+
+ /* Add valid duplicate geometry */
+ CHK(senc_descriptor_ref_put(desc) == RES_OK);
+ desc = NULL;
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+ CHK(senc_scene_analyze(scn, &desc) == RES_OK);
+
+ CHK(senc_descriptor_get_global_vertices_count(desc, &count) == RES_OK);
+ /* Duplicate vertices have been replaced */
+ CHK(count == box_nvertices);
+
+ CHK(senc_descriptor_get_global_triangle_count(desc, &count) == RES_OK);
+ /* Duplicate triangles have been replaced */
+ CHK(count == box_ntriangles);
+
+ /* Add invalid duplicate geometry */
+ CHK(senc_descriptor_ref_put(desc) == RES_OK);
+ desc = NULL;
+ ctx.front_medium = 1;
+ ctx.back_medium = 0;
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_BAD_ARG);
+
+ CHK(senc_scene_ref_put(scn) == RES_OK);
+ CHK(senc_device_ref_put(dev) == RES_OK);
+ if(desc) CHK(senc_descriptor_ref_put(desc) == RES_OK);
+ CHK(senc_enclosure_ref_put(enc) == RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+
+ return 0;
+}
diff --git a/src/test_senc_device.c b/src/test_senc_device.c
@@ -0,0 +1,77 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "senc.h"
+#include "test_senc_utils.h"
+
+#include <rsys/logger.h>
+
+static INLINE void
+log_stream(const char* msg, void* ctx)
+{
+ ASSERT(msg);
+ (void) msg, (void) ctx;
+ printf("%s\n", msg);
+}
+
+int
+main(int argc, char** argv)
+{
+ struct logger logger;
+ struct mem_allocator allocator;
+ struct senc_device* dev;
+ (void)argc, (void)argv;
+
+ CHK(senc_device_create(NULL, NULL, 0, 0, NULL) == RES_BAD_ARG);
+ CHK(senc_device_create(NULL, NULL, 0, 0, &dev) == RES_BAD_ARG);
+ CHK(senc_device_create(NULL, NULL, 1, 0, &dev) == RES_OK);
+ CHK(senc_device_ref_get(NULL) == RES_BAD_ARG);
+ CHK(senc_device_ref_get(dev) == RES_OK);
+ CHK(senc_device_ref_put(NULL) == RES_BAD_ARG);
+ CHK(senc_device_ref_put(dev) == RES_OK);
+ CHK(senc_device_ref_put(dev) == RES_OK);
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)
+ == RES_OK);
+
+ CHK(MEM_ALLOCATED_SIZE(&allocator) == 0);
+ CHK(senc_device_create(NULL, &allocator, 1, 0, NULL) == RES_BAD_ARG);
+ CHK(senc_device_create(NULL, &allocator, 1, 0, &dev) == RES_OK);
+ CHK(senc_device_ref_put(dev) == RES_OK);
+ CHK(MEM_ALLOCATED_SIZE(&allocator) == 0);
+
+ CHK(logger_init(&allocator, &logger) == RES_OK);
+ logger_set_stream(&logger, LOG_OUTPUT, log_stream, NULL);
+ logger_set_stream(&logger, LOG_ERROR, log_stream, NULL);
+ logger_set_stream(&logger, LOG_WARNING, log_stream, NULL);
+
+ CHK(senc_device_create(&logger, NULL, 1, 0, NULL) == RES_BAD_ARG);
+ CHK(senc_device_create(&logger, NULL, 1, 0, &dev) == RES_OK);
+ CHK(senc_device_ref_put(dev) == RES_OK);
+
+ CHK(senc_device_create(&logger, &allocator, 1, 0, NULL) == RES_BAD_ARG);
+ CHK(senc_device_create(&logger, &allocator, 1, 0, &dev) == RES_OK);
+ CHK(senc_device_ref_put(dev) == RES_OK);
+
+ CHK(senc_device_create
+ (&logger, &allocator, SENC_NTHREADS_DEFAULT, 0, &dev) == RES_OK);
+ CHK(senc_device_ref_put(dev) == RES_OK);
+
+ logger_release(&logger);
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+ return 0;
+}
diff --git a/src/test_senc_enclosure.c b/src/test_senc_enclosure.c
@@ -0,0 +1,181 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "senc.h"
+#include "senc_s3d_wrapper.h"
+#include "test_senc_utils.h"
+
+#include <rsys/float3.h>
+#include <rsys/double3.h>
+
+#include <star/s3d.h>
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc_descriptor* desc = NULL;
+ struct senc_device* dev = NULL;
+ struct senc_scene* scn = NULL;
+ struct senc_enclosure* enclosures[2] = { NULL, NULL };
+ struct senc_enclosure* enclosure;
+ const struct enclosure_header* header;
+ struct s3d_device* s3d = NULL;
+ struct s3d_scene* s3d_scn = NULL;
+ struct s3d_shape* s3d_shp = NULL;
+ struct s3d_vertex_data s3d_attribs;
+ struct context ctx;
+ unsigned i, n, count;
+ (void) argc, (void) argv;
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK);
+ CHK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev)
+ == RES_OK);
+
+ CHK(senc_scene_create(dev, 2, &scn) == RES_OK);
+
+ s3d_attribs.type = S3D_FLOAT3;
+ s3d_attribs.usage = S3D_POSITION;
+ s3d_attribs.get = senc_enclosure_get_vertices__;
+
+ CHK(s3d_device_create(NULL, &allocator, 0, &s3d) == RES_OK);
+
+ CHK(s3d_scene_create(s3d, &s3d_scn) == RES_OK);
+
+ /* A 3D cube.
+ * 2 enclosures (inside, outside) sharing the same triangles, but opposite sides */
+ ctx.positions = box_vertices;
+ ctx.indices = box_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d3(ctx.offset, 0, 0, 0);
+ ctx.front_medium = 0;
+ ctx.back_medium = 1;
+
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc_scene_analyze(scn, &desc) == RES_OK);
+
+ CHK(senc_descriptor_get_enclosure_count(desc, &count) == RES_OK);
+ CHK(count == 2);
+
+ FOR_EACH(i, 0, count) {
+ CHK(senc_descriptor_get_enclosure(desc, i, &enclosure) == RES_OK);
+
+ CHK(senc_enclosure_get_header(NULL, &header) == RES_BAD_ARG);
+ CHK(senc_enclosure_get_header(enclosure, NULL) == RES_BAD_ARG);
+ CHK(senc_enclosure_get_header(NULL, NULL) == RES_BAD_ARG);
+ CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK);
+
+ CHK(header->enclosure_id == i);
+ CHK(header->enclosed_medium == (i == 0 ? 0U : 1U));
+ CHK(header->triangle_count == box_ntriangles);
+ CHK(header->unique_triangle_count == box_ntriangles);
+ CHK(header->vertices_count == box_nvertices);
+ CHK(header->is_infinite == (i == 0));
+
+ CHK(senc_enclosure_ref_put(enclosure) == RES_OK);
+ }
+
+ FOR_EACH(i, 0, 2)
+ CHK(senc_descriptor_get_enclosure(desc, i, enclosures + i) == RES_OK);
+ FOR_EACH(n, 0, box_ntriangles) {
+ unsigned indices[2][3];
+ /* Read same triangles in both enclosures */
+ FOR_EACH(i, 0, 2)
+ senc_enclosure_get_indices(enclosures[i], n, indices[i]);
+ /* Same triangles, opposite sides */
+ CHK(indices[0][0] == indices[1][0]);
+ CHK(indices[0][1] == indices[1][2]);
+ CHK(indices[0][2] == indices[1][1]);
+ }
+ FOR_EACH(i, 0, 2)
+ CHK(senc_enclosure_ref_put(enclosures[i]) == RES_OK);
+
+ CHK(senc_scene_ref_put(scn) == RES_OK);
+ CHK(senc_descriptor_ref_put(desc) == RES_OK);
+
+ /* Same 3D cube, but with a hole (incomplete).
+ * 1 single enclosure including both sides of triangles */
+
+ CHK(senc_scene_create(dev, 2, &scn) == RES_OK);
+
+ ctx.positions = box_vertices;
+ ctx.indices = box_indices;
+ ctx.scale = 1;
+ d3(ctx.offset, 0, 0, 0);
+ ctx.front_medium = 0;
+ ctx.back_medium = 0;
+
+ CHK(senc_scene_add_geometry(scn, box_ntriangles - 1, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc_scene_analyze(scn, &desc) == RES_OK);
+
+ CHK(senc_descriptor_get_enclosure_count(desc, &count) == RES_OK);
+ CHK(count == 1);
+
+ CHK(senc_descriptor_get_enclosure(desc, 0, &enclosure) == RES_OK);
+
+ CHK(senc_enclosure_get_header(NULL, &header) == RES_BAD_ARG);
+ CHK(senc_enclosure_get_header(enclosure, NULL) == RES_BAD_ARG);
+ CHK(senc_enclosure_get_header(NULL, NULL) == RES_BAD_ARG);
+ CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK);
+
+ CHK(header->enclosure_id == 0);
+ CHK(header->enclosed_medium == 0);
+ CHK(header->triangle_count == 2 * header->unique_triangle_count);
+ CHK(header->unique_triangle_count == box_ntriangles - 1);
+ CHK(header->vertices_count == box_nvertices);
+ CHK(header->is_infinite == 1);
+
+ FOR_EACH(n, 0, header->unique_triangle_count) {
+ unsigned indices[2][3];
+ /* Read 2 consecutive triangles in the enclosure */
+ FOR_EACH(i, 0, 2)
+ senc_enclosure_get_indices(enclosure, 2 * n + i, indices[i]);
+ /* Same triangles, opposite sides */
+ CHK(indices[0][0] == indices[1][0]);
+ CHK(indices[0][1] == indices[1][2]);
+ CHK(indices[0][2] == indices[1][1]);
+
+ /* Put geometry in a 3D view */
+ CHK(s3d_shape_create_mesh(s3d, &s3d_shp) == RES_OK);
+
+ CHK(s3d_mesh_setup_indexed_vertices(s3d_shp, header->triangle_count,
+ senc_enclosure_get_indices__, header->vertices_count, &s3d_attribs,
+ 1, enclosure)
+ == RES_OK);
+
+ CHK(s3d_scene_attach_shape(s3d_scn, s3d_shp) == RES_OK);
+ CHK(s3d_shape_ref_put(s3d_shp) == RES_OK);
+ }
+
+ CHK(s3d_device_ref_put(s3d) == RES_OK);
+ CHK(s3d_scene_ref_put(s3d_scn) == RES_OK);
+
+ CHK(senc_scene_ref_put(scn) == RES_OK);
+ CHK(senc_device_ref_put(dev) == RES_OK);
+ CHK(senc_descriptor_ref_put(desc) == RES_OK);
+ CHK(senc_enclosure_ref_put(enclosure) == RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+
+ return 0;
+}
diff --git a/src/test_senc_sample_enclosure.c b/src/test_senc_sample_enclosure.c
@@ -0,0 +1,139 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "senc.h"
+#include "senc_s3d_wrapper.h"
+#include "test_senc_utils.h"
+
+#include <rsys/float3.h>
+#include <rsys/double3.h>
+
+#include <star/s3d.h>
+#include <star/ssp.h>
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc_descriptor* desc = NULL;
+ struct senc_device* dev = NULL;
+ struct senc_scene* scn = NULL;
+ struct senc_enclosure* enclosure = NULL;
+ const struct enclosure_header* header = NULL;
+ struct s3d_device* s3d = NULL;
+ struct s3d_scene* s3d_scn = NULL;
+ struct s3d_scene_view* s3d_view = NULL;
+ struct s3d_shape* s3d_shp = NULL;
+ struct s3d_primitive prim;
+ struct s3d_vertex_data vrtx_get;
+ struct ssp_rng* rng;
+ struct context ctx;
+ int i;
+ float st[2];
+ (void) argc, (void) argv;
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK);
+ CHK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev)
+ == RES_OK);
+
+ CHK(senc_scene_create(dev, 2, &scn) == RES_OK);
+
+ vrtx_get.type = S3D_FLOAT3;
+ vrtx_get.usage = S3D_POSITION;
+ vrtx_get.get = senc_enclosure_get_vertices__;
+
+ CHK(s3d_device_create(NULL, &allocator, 0, &s3d) == RES_OK);
+
+ CHK(s3d_scene_create(s3d, &s3d_scn) == RES_OK);
+
+ /* A 3D cube, but with a hole (incomplete).
+ * 1 single enclosure including both sides of triangles */
+
+ ctx.positions = box_vertices;
+ ctx.indices = box_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d3(ctx.offset, 0, 0, 0);
+ ctx.front_medium = 0;
+ ctx.back_medium = 0;
+
+ CHK(senc_scene_add_geometry(scn, box_ntriangles - 1, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc_scene_analyze(scn, &desc) == RES_OK);
+
+ CHK(senc_descriptor_get_enclosure(desc, 0, &enclosure) == RES_OK);
+ CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK);
+
+ /* Put enclosure in a 3D view... */
+ CHK(s3d_shape_create_mesh(s3d, &s3d_shp) == RES_OK);
+ CHK(s3d_mesh_setup_indexed_vertices(s3d_shp, header->triangle_count,
+ senc_enclosure_get_indices__, header->vertices_count, &vrtx_get, 1, enclosure)
+ == RES_OK);
+
+ CHK(s3d_scene_attach_shape(s3d_scn, s3d_shp) == RES_OK);
+
+ CHK(s3d_scene_view_create(s3d_scn, S3D_SAMPLE, &s3d_view) == RES_OK);
+
+ /* ... and sample it. */
+ ssp_rng_create(&allocator, &ssp_rng_threefry, &rng);
+ FOR_EACH(i, 0, 10000) {
+ struct s3d_attrib attrib;
+ int n, c;
+ CHK(s3d_scene_view_sample(s3d_view,
+ ssp_rng_canonical_float(rng),
+ ssp_rng_canonical_float(rng),
+ ssp_rng_canonical_float(rng),
+ &prim, st) == RES_OK);
+ s3d_primitive_get_attrib(&prim, S3D_POSITION, st, &attrib);
+ c = 0;
+ FOR_EACH(n, 0, 3)
+ if(eq_eps(attrib.value[n], 0, FLT_EPSILON)
+ || eq_eps(attrib.value[n], 1, FLT_EPSILON))
+ c++;
+ ASSERT(c == 1);
+ s3d_primitive_get_attrib(&prim, S3D_GEOMETRY_NORMAL, st, &attrib);
+ c = 0;
+ FOR_EACH(n, 0, 3)
+ if(eq_eps(attrib.value[n], -1, FLT_EPSILON)
+ || eq_eps(attrib.value[n], 1, FLT_EPSILON))
+ c++;
+ ASSERT(c == 1);
+ c = 0;
+ FOR_EACH(n, 0, 3)
+ if(eq_eps(attrib.value[n], 0, FLT_EPSILON))
+ c++;
+ ASSERT(c == 2);
+ }
+
+ CHK(senc_enclosure_ref_put(enclosure) == RES_OK);
+ CHK(senc_scene_ref_put(scn) == RES_OK);
+ CHK(senc_device_ref_put(dev) == RES_OK);
+ CHK(senc_descriptor_ref_put(desc) == RES_OK);
+
+ CHK(ssp_rng_ref_put(rng) == RES_OK);
+
+ CHK(s3d_shape_ref_put(s3d_shp) == RES_OK);
+ CHK(s3d_scene_view_ref_put(s3d_view) == RES_OK);
+ CHK(s3d_device_ref_put(s3d) == RES_OK);
+ CHK(s3d_scene_ref_put(s3d_scn) == RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+
+ return 0;
+}
diff --git a/src/test_senc_scene.c b/src/test_senc_scene.c
@@ -0,0 +1,131 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "senc.h"
+#include "test_senc_utils.h"
+
+#include <rsys/float3.h>
+#include <rsys/double3.h>
+
+#include <star/s3d.h>
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc_device* dev = NULL;
+ struct senc_scene* scn = NULL;
+ struct senc_descriptor* desc = NULL;
+ struct context ctx;
+ (void)argc, (void)argv;
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK);
+ CHK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev)
+ == RES_OK);
+
+ CHK(senc_scene_create(NULL, 2, &scn) == RES_BAD_ARG);
+ CHK(senc_scene_create(dev, 0, &scn) == RES_BAD_ARG);
+ CHK(senc_scene_create(dev, 2, NULL) == RES_BAD_ARG);
+ CHK(senc_scene_create(NULL, 0, &scn) == RES_BAD_ARG);
+ CHK(senc_scene_create(NULL, 2, NULL) == RES_BAD_ARG);
+ CHK(senc_scene_create(dev, 0, NULL) == RES_BAD_ARG);
+ CHK(senc_scene_create(NULL, 0, NULL) == RES_BAD_ARG);
+ /* It is valid to have unused mediums */
+ CHK(senc_scene_create(dev, 4, &scn) == RES_OK);
+
+ /* A 3D cube */
+ ctx.positions = box_vertices;
+ ctx.indices = box_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d3(ctx.offset, 0, 0, 0);
+ ctx.front_medium = 0;
+ ctx.back_medium = 1;
+
+ CHK(senc_scene_add_geometry(NULL, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_BAD_ARG);
+ CHK(senc_scene_add_geometry(scn, 0, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_BAD_ARG);
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, NULL, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_BAD_ARG);
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, NULL,
+ box_nvertices, get_position, &ctx) == RES_BAD_ARG);
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ 0, get_position, &ctx) == RES_BAD_ARG);
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, NULL, &ctx) == RES_BAD_ARG);
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc_scene_analyze(NULL, NULL) == RES_BAD_ARG);
+ CHK(senc_scene_analyze(scn, NULL) == RES_BAD_ARG);
+ CHK(senc_scene_analyze(NULL, &desc) == RES_BAD_ARG);
+ CHK(senc_scene_analyze(NULL, NULL) == RES_BAD_ARG);
+ CHK(senc_scene_analyze(scn, &desc) == RES_OK);
+
+ CHK(senc_scene_ref_get(NULL) == RES_BAD_ARG);
+ CHK(senc_scene_ref_get(scn) == RES_OK);
+ CHK(senc_scene_ref_put(NULL) == RES_BAD_ARG);
+ CHK(senc_scene_ref_put(scn) == RES_OK);
+
+ /* Invalid medium ID */
+ ctx.back_medium = 12;
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_BAD_ARG);
+ ctx.back_medium = 1;
+
+ /* Invalid vertex ID */
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices - 1, get_position, &ctx) == RES_BAD_ARG);
+
+ /* Incoherent medium on duplicate triangle */
+ ctx.back_medium = 3;
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_BAD_ARG);
+
+ /* It is OK dd geometry after a failed add */
+ ctx.back_medium = 1;
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ /* Coherent medium on duplicate triangle */
+ ctx.back_medium = 1;
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ /* Coherent medium on duplicate triangle V2 */
+ ctx.reverse_med = 1;
+ ctx.front_medium = 1;
+ ctx.back_medium = 0;
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ /* Coherent medium on duplicate triangle V3 */
+ ctx.reverse_med = 0;
+ ctx.reverse_vrtx = 1;
+ CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums,
+ box_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc_scene_ref_put(scn) == RES_OK);
+ CHK(senc_device_ref_put(dev) == RES_OK);
+ CHK(senc_descriptor_ref_put(desc) == RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+
+ return 0;
+}
diff --git a/src/test_senc_utils.h b/src/test_senc_utils.h
@@ -0,0 +1,135 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef TEST_UTILS_H
+#define TEST_UTILS_H
+
+#include <rsys/mem_allocator.h>
+#include <stdio.h>
+
+/*******************************************************************************
+ * Geometry
+ ******************************************************************************/
+static const double box_vertices[8/*#vertices*/*3/*#coords per vertex*/] = {
+ 0.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0,
+ 1.0, 1.0, 0.0,
+ 0.0, 0.0, 1.0,
+ 1.0, 0.0, 1.0,
+ 0.0, 1.0, 1.0,
+ 1.0, 1.0, 1.0
+};
+static const unsigned box_nvertices = sizeof(box_vertices) / sizeof(double[3]);
+
+/* The following array lists the indices toward the 3D vertices of each
+ * triangle.
+ * ,6---,7 ,6----7
+ * ,' | ,'/| ,' | \ |
+ * 2----3' / | 2', | \ |
+ * |', | / ,5 | ',4---,5
+ * | ',|/,' | ,' | ,'
+ * 0----1' 0----1'
+ * Front, right Back, left and
+ * and Top faces bottom faces */
+static const unsigned box_indices[12/*#triangles*/*3/*#indices per triangle*/] = {
+ 0, 2, 1, 1, 2, 3, /* Front face */
+ 0, 4, 2, 2, 4, 6, /* Left face*/
+ 4, 5, 6, 6, 5, 7, /* Back face */
+ 3, 7, 1, 1, 7, 5, /* Right face */
+ 2, 6, 3, 3, 6, 7, /* Top face */
+ 0, 1, 4, 4, 1, 5 /* Bottom face */
+};
+static const unsigned box_ntriangles = sizeof(box_indices) / (3 * sizeof(*box_indices));
+
+struct context {
+ const double* positions;
+ const unsigned* indices;
+ struct cube_specifics* specifics;
+ unsigned front_medium;
+ unsigned back_medium;
+ double offset[3];
+ double scale;
+ char reverse_vrtx, reverse_med;
+};
+
+static void
+get_indices(const unsigned itri, unsigned ids[3], void* context)
+{
+ struct context* ctx = context;
+ ASSERT(ids && ctx);
+ ids[0] = ctx->indices[itri * 3 + 0];
+ ids[ctx->reverse_vrtx ? 2 : 1] = ctx->indices[itri * 3 + 1];
+ ids[ctx->reverse_vrtx ? 1 : 2] = ctx->indices[itri * 3 + 2];
+}
+
+static void
+get_position(const unsigned ivert, double pos[3], void* context)
+{
+ struct context* ctx = context;
+ ASSERT(pos && ctx);
+ pos[0] = ctx->positions[ivert * 3 + 0] * ctx->scale + ctx->offset[0];
+ pos[1] = ctx->positions[ivert * 3 + 1] * ctx->scale + ctx->offset[1];
+ pos[2] = ctx->positions[ivert * 3 + 2] * ctx->scale + ctx->offset[2];
+}
+
+static void
+get_mediums(const unsigned itri, unsigned medium[2], void* context)
+{
+ struct context* ctx = context;
+ ASSERT(medium && ctx);
+ (void) itri;
+ medium[ctx->reverse_med ? 1 : 0] = ctx->front_medium;
+ medium[ctx->reverse_med ? 0 : 1] = ctx->back_medium;
+}
+
+/*******************************************************************************
+ * Miscellaneous
+ ******************************************************************************/
+static INLINE void
+dump_mesh
+ (FILE* stream,
+ const double* pos,
+ const size_t npos,
+ const size_t* ids,
+ const size_t nids)
+{
+ size_t i;
+ CHK(pos != NULL && npos != 0);
+ CHK(ids != NULL && nids != 0);
+ FOR_EACH(i, 0, npos) {
+ fprintf(stream, "v %g %g %g\n", SPLIT3(pos+i*3));
+ }
+ FOR_EACH(i, 0, nids) {
+ fprintf(stream, "f %lu %lu %lu\n",
+ (unsigned long)(ids[i*3+0] + 1),
+ (unsigned long)(ids[i*3+1] + 1),
+ (unsigned long)(ids[i*3+2] + 1));
+ }
+}
+
+static INLINE void
+check_memory_allocator(struct mem_allocator* allocator)
+{
+ if(MEM_ALLOCATED_SIZE(allocator)) {
+ char dump[128];
+ MEM_DUMP(allocator, dump, sizeof(dump));
+ fprintf(stderr, "%s\n", dump);
+ FATAL("Memory leaks.\n");
+ }
+}
+
+#endif /* TEST_UTILS_H */
+