commit 6b87ccb8faebed4ecff1b332b74cfb2f5ca756fd
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Fri, 30 Mar 2018 15:02:00 +0200
First commit
Supposed to be a fully working 2D translation of star-enclosures
Diffstat:
28 files changed, 5658 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,31 @@
+# StarEnclosures 2D
+
+The purpose of this library is to extract enclosures from raw 2D geometry. An
+enclosure is a set of segments enclosing a given area. The library manages
+vertices and segments duplicates, easing the scene definition process. It also
+checks some coherency properties, most noticeably the enclosed medium unicity.
+
+## How to build
+
+StarEnclosures 2D 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/) libraries. Additionaly, two
+more libraries are needed to build tests:
+[Star-SP](https://gitlab.com/meso-star/star-sp/) and
+[Star-3DUT](https://gitlab.com/meso-star/star-3dut/).
+
+First ensure that CMake and a C compiler that implements the OpenMP 1.2 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 2D 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,139 @@
+# 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-Enclosures2D C)
+enable_testing()
+
+set(SENC2D_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src)
+option(NO_TEST "Do not build tests" OFF)
+
+################################################################################
+# Check dependencies
+################################################################################
+find_package(RCMake 0.4 REQUIRED)
+find_package(Star2D 0.1 REQUIRED)
+find_package(RSys 0.6.1 REQUIRED)
+find_package(OpenMP 1.2 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(SENC2D_FILES_SRC
+ senc2d_descriptor.c
+ senc2d_device.c
+ senc2d_enclosure.c
+ senc2d_scene.c
+ senc2d_scene_analyze.c)
+
+set(SENC2D_FILES_INC_API
+ senc2d.h
+ senc2d_s2d_wrapper.h)
+
+set(SENC2D_FILES_INC
+ senc2d_descriptor_c.h
+ senc2d_device_c.h
+ senc2d_enclosure_c.h
+ senc2d_enclosure_data.h
+ senc2d_internal_types.h
+ senc2d_scene_c.h
+ senc2d_scene_analyze_c.h)
+
+set(SENC2D_FILES_DOC COPYING README.md)
+
+# Prepend each file by `SENC2D_SOURCE_DIR'
+rcmake_prepend_path(SENC2D_FILES_SRC ${SENC2D_SOURCE_DIR})
+rcmake_prepend_path(SENC2D_FILES_INC ${SENC2D_SOURCE_DIR})
+rcmake_prepend_path(SENC2D_FILES_INC_API ${SENC2D_SOURCE_DIR})
+rcmake_prepend_path(SENC2D_FILES_DOC ${PROJECT_SOURCE_DIR}/../)
+
+add_library(senc2d SHARED
+ ${SENC2D_FILES_SRC}
+ ${SENC2D_FILES_INC}
+ ${SENC2D_FILES_INC_API})
+target_link_libraries(senc2d RSys Star2D)
+
+set_target_properties(senc2d PROPERTIES
+ DEFINE_SYMBOL SENC2D_SHARED_BUILD
+ VERSION ${VERSION}
+ COMPILE_FLAGS ${OpenMP_C_FLAGS}
+ SOVERSION ${VERSION_MAJOR})
+rcmake_copy_runtime_libraries(senc2d)
+
+if(CMAKE_COMPILER_IS_GNUCC)
+ set_target_properties(senc PROPERTIES LINK_FLAGS "${OpenMP_C_FLAGS}")
+ target_link_libraries(senc m)
+endif()
+
+rcmake_setup_devel(senc2d StarEnc2D ${VERSION} senc2d_version.h)
+
+################################################################################
+# Add tests
+################################################################################
+if(NOT NO_TEST)
+ function(build_test _name)
+ add_executable(${_name}
+ ${SENC2D_SOURCE_DIR}/test_senc2d_utils.h
+ ${SENC2D_SOURCE_DIR}/${_name}.c)
+ target_link_libraries(${_name} RSys senc2d)
+ 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_senc2d_square_behind_square)
+ new_test(test_senc2d_square_in_square)
+ new_test(test_senc2d_square_on_square)
+ new_test(test_senc2d_descriptor)
+ new_test(test_senc2d_device)
+ new_test(test_senc2d_enclosure)
+ new_test(test_senc2d_many_enclosures)
+ new_test(test_senc2d_many_segments)
+ new_test(test_senc2d_sample_enclosure)
+ new_test(test_senc2d_scene)
+
+ target_link_libraries(test_senc2d_sample_enclosure StarSP)
+ rcmake_copy_runtime_libraries(test_senc2d_sample_enclosure)
+endif()
+
+################################################################################
+# Define output & install directories
+################################################################################
+install(TARGETS senc2d
+ ARCHIVE DESTINATION bin
+ LIBRARY DESTINATION lib
+ RUNTIME DESTINATION bin)
+install(FILES ${SENC2D_FILES_INC_API} DESTINATION include/)
+install(FILES ${SENC2D_FILES_DOC} DESTINATION share/doc/star-enc2d)
diff --git a/src/senc2d.h b/src/senc2d.h
@@ -0,0 +1,273 @@
+/* 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 SENC2D_H
+#define SENC2D_H
+
+#include <rsys/rsys.h>
+
+/* Library symbol management */
+#if defined(SENC2D_SHARED_BUILD)
+ #define SENC2D_API extern EXPORT_SYM
+#elif defined(SENC2D_STATIC_BUILD)
+ #define SENC2D_API extern LOCAL_SYM
+#else /* Use shared library */
+ #define SENC2D_API extern IMPORT_SYM
+#endif
+
+/* Helper macro that asserts if the invocation of the StarEnc2D 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 SENC2D(Func) ASSERT(senc2d_ ## Func == RES_OK)
+#else
+ #define SENC2D(Func) senc2d_ ## Func
+#endif
+
+/* Syntactic sugar used to inform the library that it can use as many threads
+ * as CPU cores */
+#define SENC2D_NTHREADS_DEFAULT (~0u)
+
+/* Forward declaration of external opaque data types */
+struct logger;
+struct mem_allocator;
+
+/* Forward declaration of StarEnclosures2D opaque data types. These data types
+ * are ref counted. Once created with the appropriated `senc2d_<TYPE>_create'
+ * function, the caller implicitly owns the created data, i.e. its reference
+ * counter is set to 1. The senc2d_<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 senc2d_descriptor;
+struct senc2d_device;
+struct senc2d_scene;
+struct senc2d_enclosure;
+
+/* Enclosure2D header type */
+struct enclosure2d_header {
+ /* The ID of the enclosure; 0, 1, ... */
+ unsigned enclosure_id;
+ /* Number of segments; a segment can be accounted for twice, once by side */
+ unsigned segment_count;
+ /* Number of segments; a segment cannot be accounted for twice */
+ unsigned unique_segment_count;
+ /* Number of vertices */
+ unsigned vertices_count;
+ /* The medium inside the enclosure */
+ unsigned enclosed_medium;
+ /* Is the enclosure infinite?
+ * Only the outermost enclosure is infinite. */
+ char is_infinite;
+};
+
+BEGIN_DECLS
+
+/*******************************************************************************
+ * StarEnclosures2D device. It is an handle toward the StarEnc2d library.
+ * It manages the lib resources.
+ * If provided, the allocator has to be suitable for parallel high frequency
+ * allocations. As a consequence, a rsys proxy allocator should be avoided.
+ ******************************************************************************/
+SENC2D_API res_T
+senc2d_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,
+ const int verbose,
+ struct senc2d_device** device);
+
+SENC2D_API res_T
+senc2d_device_ref_get
+ (struct senc2d_device* device);
+
+SENC2D_API res_T
+senc2d_device_ref_put
+ (struct senc2d_device* device);
+
+/*******************************************************************************
+ * StarEnclosures2D scene. A scene is a collection of segments. Each segment is
+ * defined with a medium on each side.
+ ******************************************************************************/
+SENC2D_API res_T
+senc2d_scene_create
+ (struct senc2d_device* device,
+ const unsigned media_count,
+ struct senc2d_scene** scene);
+
+/* Add a new set of vertices and segments to the scene.
+ * Vertices can be duplicates and are deduplicated on the fly.
+ * Segments can be duplicates as long as they constantly define the same
+ * medium on both sides (or an error will be reported) and are deduplicated.
+ * When deduplicating segments, the first occurence is kept (with it original
+ * global_id). */
+SENC2D_API res_T
+senc2d_scene_add_geometry
+ (struct senc2d_scene* scene,
+ const unsigned segments_count,
+ void(*indices)(const unsigned iseg, unsigned ids[2], void* context),
+ void(*media)(const unsigned iseg, unsigned med[2], void* context),
+ void(*global_id) /* May be NULL <=> use segment rank */
+ (const unsigned iseg, unsigned* gid, void* context),
+ const unsigned vertices_count,
+ void(*position)(const unsigned ivert, double pos[2], void* context),
+ void* context);
+
+SENC2D_API res_T
+senc2d_scene_analyze
+ (struct senc2d_scene* scene,
+ struct senc2d_descriptor** descriptor);
+
+SENC2D_API res_T
+senc2d_scene_get_segments_count
+ (const struct senc2d_scene* scene,
+ unsigned* count);
+
+SENC2D_API res_T
+senc2d_scene_get_unique_segments_count
+ (const struct senc2d_scene* scene,
+ unsigned* count);
+
+SENC2D_API res_T
+senc2d_scene_get_vertices_count
+ (const struct senc2d_scene* scene,
+ unsigned* count);
+
+SENC2D_API res_T
+senc2d_scene_get_unique_vertices_count
+ (const struct senc2d_scene* scene,
+ unsigned* count);
+
+SENC2D_API res_T
+senc2d_scene_ref_get
+ (struct senc2d_scene* scene);
+
+SENC2D_API res_T
+senc2d_scene_ref_put
+ (struct senc2d_scene* scene);
+
+/*******************************************************************************
+ * StarEnclosures2D descriptor. It is an handle toward an analyze result.
+ ******************************************************************************/
+SENC2D_API res_T
+senc2d_descriptor_get_enclosure_count
+ (const struct senc2d_descriptor* descriptor,
+ unsigned* count);
+
+SENC2D_API res_T
+senc2d_descriptor_get_enclosure
+ (struct senc2d_descriptor* descriptor,
+ const unsigned idx,
+ struct senc2d_enclosure** enclosure);
+
+SENC2D_API res_T
+senc2d_descriptor_get_global_segments_count
+ (const struct senc2d_descriptor* descriptor,
+ unsigned* count); /* Number of unique segments. */
+
+SENC2D_API res_T
+senc2d_descriptor_get_global_vertices_count
+ (const struct senc2d_descriptor* descriptor,
+ unsigned* count); /* Number of unique vertices. */
+
+SENC2D_API res_T
+senc2d_descriptor_get_global_segment
+ (const struct senc2d_descriptor* descriptor,
+ const unsigned iseg,
+ unsigned indices[2]);
+
+SENC2D_API res_T
+senc2d_descriptor_get_global_vertex
+ (const struct senc2d_descriptor* descriptor,
+ const unsigned ivert,
+ double coord[2]);
+
+SENC2D_API res_T
+senc2d_descriptor_get_global_segment_media
+ (const struct senc2d_descriptor* descriptor,
+ const unsigned iseg,
+ unsigned media[2]);
+
+SENC2D_API res_T
+senc2d_descriptor_get_global_segment_enclosures
+ (const struct senc2d_descriptor* descriptor,
+ const unsigned iseg,
+ unsigned enclosures[2]);
+
+SENC2D_API res_T
+senc2d_descriptor_get_global_segment_global_id
+ (const struct senc2d_descriptor* descriptor,
+ const unsigned iseg,
+ unsigned* gid);
+
+SENC2D_API res_T
+senc2d_descriptor_ref_get
+ (struct senc2d_descriptor* descriptor);
+
+SENC2D_API res_T
+senc2d_descriptor_ref_put
+ (struct senc2d_descriptor* descriptor);
+
+/*******************************************************************************
+ * StarEnclosures2D enclosure. It is an handle toward an enclosure.
+ * Counts and other information on enclosures are not individually accessible,
+ * but as a whole through header access.
+ * An enclosure can list the "same" segment twice if both sides are in. In this
+ * case the 2 occurences of the segment have reversed vertices order and
+ * unique_segment_count and segment_count differ.
+ * By-index API accesses of segments (or properties) visit unique segments
+ * for indexes in the [0 unique_segment_count[ and back-faces of the
+ * doubly-listed segments in the [unique_segment_count segment_count[ range.
+ ******************************************************************************/
+SENC2D_API res_T
+senc2d_enclosure_get_header
+ (const struct senc2d_enclosure* enclosure,
+ const struct enclosure2d_header** header);
+
+SENC2D_API res_T
+senc2d_enclosure_get_segment
+ (const struct senc2d_enclosure* enclosure,
+ const unsigned iseg,
+ unsigned indices[2]);
+
+SENC2D_API res_T
+senc2d_enclosure_get_vertex
+ (const struct senc2d_enclosure* enclosure,
+ const unsigned ivert,
+ double coord[2]);
+
+SENC2D_API res_T
+senc2d_enclosure_get_segment_media
+ (const struct senc2d_enclosure* enclosure,
+ const unsigned iseg,
+ unsigned medium[2]);
+
+SENC2D_API res_T
+senc2d_enclosure_get_segment_global_id
+ (const struct senc2d_enclosure* enclosure,
+ const unsigned iseg,
+ unsigned* gid);
+
+SENC2D_API res_T
+senc2d_enclosure_ref_get
+ (struct senc2d_enclosure* enclosure);
+
+SENC2D_API res_T
+senc2d_enclosure_ref_put
+ (struct senc2d_enclosure* enclosure);
+
+END_DECLS
+
+#endif /* SENC2D_H */
diff --git a/src/senc2d_descriptor.c b/src/senc2d_descriptor.c
@@ -0,0 +1,242 @@
+/* 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 "senc2d_descriptor_c.h"
+#include "senc2d_device_c.h"
+#include "senc2d_enclosure_c.h"
+#include "senc2d_scene_c.h"
+#include "senc2d.h"
+
+#include <rsys/rsys.h>
+#include <rsys/double2.h>
+#include <rsys/mem_allocator.h>
+
+ /*******************************************************************************
+ * Helper function
+ ******************************************************************************/
+static void
+descriptor_release(ref_T * ref)
+{
+ struct senc2d_scene* scn = NULL;
+ struct senc2d_descriptor* desc = NULL;
+ ASSERT(ref);
+ desc = CONTAINER_OF(ref, struct senc2d_descriptor, ref);
+ scn = desc->scene;
+ darray_segment_enc_release(&desc->segments_enc);
+ darray_enclosure_release(&desc->enclosures);
+
+ MEM_RM(scn->dev->allocator, desc);
+ SENC2D(scene_ref_put(scn));
+}
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+struct senc2d_descriptor*
+descriptor_create(struct senc2d_scene* scn)
+{
+ struct senc2d_descriptor* desc;
+ res_T res = RES_OK;
+ ASSERT(scn);
+ desc = MEM_CALLOC(scn->dev->allocator, 1, sizeof(struct senc2d_descriptor));
+ if(desc) {
+ desc->scene = scn;
+ SENC2D(scene_ref_get(desc->scene));
+ ref_init(&desc->ref);
+ darray_segment_enc_init(scn->dev->allocator, &desc->segments_enc);
+ /* Enclosure 0 is always defined for infinite */
+ darray_enclosure_init(scn->dev->allocator, &desc->enclosures);
+ OK(darray_enclosure_resize(&desc->enclosures, 1));
+ desc->enclosures_count = 1;
+ desc->segment_count = scn->nusegs;
+ desc->vertices_count = scn->nuverts;
+ }
+end:
+ return desc;
+error:
+ if(desc) SENC2D(descriptor_ref_put(desc));
+ goto end;
+}
+
+struct mem_allocator*
+ descriptor_get_allocator(struct senc2d_descriptor* desc)
+{
+ ASSERT(desc);
+ return desc->scene->dev->allocator;
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+senc2d_descriptor_get_enclosure_count
+ (const struct senc2d_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 */
+ ASSERT(desc->enclosures_count == tmp);
+ *count = (unsigned)tmp;
+ return RES_OK;
+}
+
+res_T
+senc2d_descriptor_get_enclosure
+ (struct senc2d_descriptor* desc,
+ const unsigned idx,
+ struct senc2d_enclosure** out_enc)
+{
+ struct senc2d_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
+senc2d_descriptor_get_global_segments_count
+ (const struct senc2d_descriptor* desc,
+ unsigned* count)
+{
+ if(!desc || !count) return RES_BAD_ARG;
+ ASSERT(desc->segment_count < UINT_MAX);
+ *count = (unsigned)desc->segment_count; /* Back to API type */
+ return RES_OK;
+}
+
+res_T
+senc2d_descriptor_get_global_vertices_count
+ (const struct senc2d_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
+senc2d_descriptor_get_global_segment
+ (const struct senc2d_descriptor* desc,
+ const unsigned iseg,
+ unsigned indices[2])
+{
+ const struct segment_in* seg;
+ int i;
+ if(!indices || ! desc
+ || iseg >= darray_segment_in_size_get(&desc->scene->segments_in))
+ return RES_BAD_ARG;
+ seg = darray_segment_in_cdata_get(&desc->scene->segments_in) + iseg;
+
+ FOR_EACH(i, 0, 2) {
+ ASSERT(seg->vertice_id[i] < UINT_MAX);
+ indices[i] = (unsigned)seg->vertice_id[i]; /* Back to API type */
+ }
+ return RES_OK;
+}
+
+res_T
+senc2d_descriptor_get_global_vertex
+ (const struct senc2d_descriptor* desc,
+ const unsigned ivert,
+ double vrtx[2])
+{
+ const union double2* v;
+ if(!vrtx || !desc
+ || ivert >= darray_position_size_get(&desc->scene->vertices))
+ return RES_BAD_ARG;
+
+ v = darray_position_cdata_get(&desc->scene->vertices) + ivert;
+ d2_set(vrtx, v->vec);
+ return RES_OK;
+}
+
+res_T
+senc2d_descriptor_get_global_segment_media
+ (const struct senc2d_descriptor* desc,
+ const unsigned isef,
+ unsigned media[2])
+{
+ const struct segment_in* seg;
+ int i;
+ if(!media || !desc
+ || isef >= darray_segment_in_size_get(&desc->scene->segments_in))
+ return RES_BAD_ARG;
+ seg = darray_segment_in_cdata_get(&desc->scene->segments_in) + isef;
+ FOR_EACH(i, 0, 2) {
+#if (UINT_MAX < MEDIUM_MAX__)
+ ASSERT(seg->medium[i] < UINT_MAX);
+#endif
+ media[i] = (unsigned)seg->medium[i]; /* Back to API type */
+ }
+ return RES_OK;
+}
+
+res_T
+senc2d_descriptor_get_global_segment_enclosures
+ (const struct senc2d_descriptor* desc,
+ const unsigned iseg,
+ unsigned enclosures[2])
+{
+ const struct segment_enc* seg;
+ int i;
+ if(!enclosures || !desc
+ || iseg >= darray_segment_enc_size_get(&desc->segments_enc))
+ return RES_BAD_ARG;
+ seg = darray_segment_enc_cdata_get(&desc->segments_enc) + iseg;
+ FOR_EACH(i, 0, 2) {
+#if (UINT_MAX < ENCLOSURE_MAX__)
+ ASSERT(seg->enclosure[i] < UINT_MAX);
+#endif
+ enclosures[i] = (unsigned)seg->enclosure[i]; /* Back to API type */
+ }
+ return RES_OK;
+}
+
+res_T
+senc2d_descriptor_get_global_segment_global_id
+ (const struct senc2d_descriptor* desc,
+ const unsigned iseg,
+ unsigned* gid)
+{
+ const struct segment_in* seg;
+ if(!gid || !desc
+ || iseg >= darray_segment_in_size_get(&desc->scene->segments_in))
+ return RES_BAD_ARG;
+ seg = darray_segment_in_cdata_get(&desc->scene->segments_in) + iseg;
+ *gid = seg->global_id;
+ return RES_OK;
+}
+
+res_T
+senc2d_descriptor_ref_get(struct senc2d_descriptor* desc)
+{
+ if(!desc) return RES_BAD_ARG;
+ ref_get(&desc->ref);
+ return RES_OK;
+}
+
+res_T
+senc2d_descriptor_ref_put(struct senc2d_descriptor* desc)
+{
+ if(!desc) return RES_BAD_ARG;
+ ref_put(&desc->ref, descriptor_release);
+ return RES_OK;
+}
diff --git a/src/senc2d_descriptor_c.h b/src/senc2d_descriptor_c.h
@@ -0,0 +1,96 @@
+/* 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 SENC2D_DESCRIPTOR_C_H
+#define SENC2D_DESCRIPTOR_C_H
+
+#include <rsys/ref_count.h>
+#include <rsys/dynamic_array.h>
+
+#include "senc2d.h"
+#include "senc2d_enclosure_data.h"
+#include "senc2d_internal_types.h"
+
+struct senc2d_scene;
+struct mem_allocator;
+
+struct segment_comp {
+ /* The connex component in which each side is. */
+ component_id_t component[2];
+};
+
+#ifndef NDEBUG
+static void
+segment_comp_init(struct mem_allocator* alloc, struct segment_comp* seg) {
+ int i;
+ (void)alloc;
+ ASSERT(seg);
+ FOR_EACH(i, 0, 2) seg->component[i] = COMPONENT_NULL__;
+}
+#define DARRAY_FUNCTOR_INIT segment_comp_init
+#endif
+
+#define DARRAY_NAME segment_comp
+#define DARRAY_DATA struct segment_comp
+#include <rsys/dynamic_array.h>
+
+struct segment_enc {
+ /* The connex component in which each side is. */
+ enclosure_id_t enclosure[2];
+};
+
+#ifndef NDEBUG
+static void
+segment_enc_init(struct mem_allocator* alloc, struct segment_enc* seg) {
+ int i;
+ (void)alloc;
+ ASSERT(seg);
+ FOR_EACH(i, 0, 2) seg->enclosure[i] = ENCLOSURE_NULL__;
+}
+#define DARRAY_FUNCTOR_INIT segment_enc_init
+#endif
+
+#define DARRAY_NAME segment_enc
+#define DARRAY_DATA struct segment_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 senc2d_descriptor {
+ struct senc2d_scene* scene;
+ enclosure_id_t enclosures_count;
+ /* Store by-segment enclosures */
+ struct darray_segment_enc segments_enc;
+ /* Store enclosures */
+ struct darray_enclosure enclosures;
+ seg_id_t segment_count;
+ vrtx_id_t vertices_count;
+
+ ref_T ref;
+};
+
+struct senc2d_descriptor*
+descriptor_create(struct senc2d_scene* scn);
+
+struct mem_allocator*
+descriptor_get_allocator(struct senc2d_descriptor* desc);
+
+#endif /* SENC2D_DESCRIPTOR_C_H */
diff --git a/src/senc2d_device.c b/src/senc2d_device.c
@@ -0,0 +1,138 @@
+/* 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 "senc2d.h"
+#include "senc2d_device_c.h"
+
+#include <rsys/logger.h>
+#include <rsys/mem_allocator.h>
+
+#include <omp.h>
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static void
+log_msg
+ (struct senc2d_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 senc2d_device* dev;
+ ASSERT(ref);
+ dev = CONTAINER_OF(ref, struct senc2d_device, ref);
+ MEM_RM(dev->allocator, dev);
+}
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+void
+log_err(struct senc2d_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 senc2d_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
+senc2d_device_create
+ (struct logger* logger,
+ struct mem_allocator* mem_allocator,
+ const unsigned nthreads_hint,
+ const int verbose,
+ struct senc2d_device** out_dev)
+{
+ struct logger* log = NULL;
+ struct senc2d_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 senc2d_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 StarEnclosures2D device.\n", FUNC_NAME) == RES_OK);
+ }
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ dev->logger = log;
+ dev->allocator = allocator;
+ dev->verbose = verbose;
+ dev->nthreads = MMIN(nthreads_hint, (unsigned)omp_get_num_procs());
+ omp_set_num_threads((int)dev->nthreads);
+ ref_init(&dev->ref);
+
+exit:
+ if(dev) *out_dev = dev;
+ return res;
+error:
+ if(dev) {
+ SENC2D(device_ref_put(dev));
+ dev = NULL;
+ }
+ goto exit;
+}
+
+res_T
+senc2d_device_ref_get(struct senc2d_device* dev)
+{
+ if(!dev) return RES_BAD_ARG;
+ ref_get(&dev->ref);
+ return RES_OK;
+}
+
+res_T
+senc2d_device_ref_put(struct senc2d_device* dev)
+{
+ if(!dev) return RES_BAD_ARG;
+ ref_put(&dev->ref, device_release);
+ return RES_OK;
+}
diff --git a/src/senc2d_device_c.h b/src/senc2d_device_c.h
@@ -0,0 +1,59 @@
+/* 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 SENC2D_DEVICE_C_H
+#define SENC2D_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 senc2d_device {
+ struct logger* logger;
+ struct mem_allocator* allocator;
+ int verbose;
+ unsigned nthreads;
+
+ 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 senc2d_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 senc2d_device* dev,
+ const char* msg,
+ ...)
+#ifdef COMPILER_GCC
+ __attribute((format(printf, 2, 3)))
+#endif
+;
+
+#endif /* SENC2D_DEVICE_C_H */
diff --git a/src/senc2d_enclosure.c b/src/senc2d_enclosure.c
@@ -0,0 +1,172 @@
+/* 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 "senc2d_enclosure_c.h"
+#include "senc2d_descriptor_c.h"
+#include "senc2d_scene_c.h"
+#include "senc2d.h"
+
+#include <rsys/rsys.h>
+#include <rsys/double2.h>
+#include <rsys/mem_allocator.h>
+
+
+/*******************************************************************************
+ * Helper function
+ ******************************************************************************/
+static void
+enclosure_release(ref_T * ref)
+{
+ struct senc2d_enclosure* enclosure = NULL;
+ struct senc2d_descriptor* desc = NULL;
+ ASSERT(ref);
+ enclosure = CONTAINER_OF(ref, struct senc2d_enclosure, ref);
+ desc = enclosure->desc;
+
+ MEM_RM(descriptor_get_allocator(desc), enclosure);
+ SENC2D(descriptor_ref_put(desc));
+}
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+struct senc2d_enclosure*
+enclosure_create
+ (struct senc2d_descriptor* desc,
+ const struct enclosure_data* data)
+{
+ struct senc2d_enclosure* enc;
+ ASSERT(desc);
+ enc = MEM_CALLOC(descriptor_get_allocator(desc),
+ 1, sizeof(struct senc2d_enclosure));
+ if(enc) {
+ SENC2D(descriptor_ref_get(desc));
+ enc->desc = desc;
+ enc->data = data;
+ ref_init(&enc->ref);
+ }
+ return enc;
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+senc2d_enclosure_get_header
+ (const struct senc2d_enclosure* enclosure,
+ const struct enclosure2d_header** header)
+{
+ if(!enclosure || !header) return RES_BAD_ARG;
+ *header = &enclosure->data->header;
+ return RES_OK;
+}
+
+res_T
+senc2d_enclosure_get_segment
+ (const struct senc2d_enclosure* enclosure,
+ const unsigned iseg,
+ unsigned indices[2])
+{
+ const struct segment_in* segment;
+ int i;
+ if(!enclosure || !indices
+ || iseg >= enclosure->data->header.segment_count)
+ return RES_BAD_ARG;
+ ASSERT(darray_segment_in_size_get(&enclosure->data->sides)
+ == enclosure->data->header.segment_count);
+ segment = darray_segment_in_cdata_get(&enclosure->data->sides) + iseg;
+ FOR_EACH(i, 0, 2) {
+ ASSERT(segment->vertice_id[i] < UINT_MAX);
+ indices[i] = (unsigned)segment->vertice_id[i]; /* Back to API type */
+ }
+ return RES_OK;
+}
+
+res_T
+senc2d_enclosure_get_vertex
+ (const struct senc2d_enclosure* enclosure,
+ const unsigned ivert,
+ double coord[2])
+{
+ if(!enclosure || !coord
+ || ivert >= enclosure->data->header.vertices_count) {
+ return RES_BAD_ARG;
+ } else {
+ const vrtx_id_t idx
+ = darray_vrtx_id_cdata_get(&enclosure->data->vertices)[ivert];
+ const union double2* positions
+ = darray_position_cdata_get(&enclosure->desc->scene->vertices);
+ ASSERT(darray_vrtx_id_size_get(&enclosure->data->vertices)
+ == enclosure->data->header.vertices_count);
+ d2_set(coord, positions[idx].vec);
+ return RES_OK;
+ }
+}
+
+res_T
+senc2d_enclosure_get_segment_media
+ (const struct senc2d_enclosure* enclosure,
+ const unsigned iseg,
+ unsigned medium[2])
+{
+ const struct segment_in* segment;
+ int i;
+ if(!enclosure || !medium
+ || iseg >= enclosure->data->header.segment_count)
+ return RES_BAD_ARG;
+ ASSERT(darray_segment_in_size_get(&enclosure->data->sides)
+ == enclosure->data->header.segment_count);
+ segment = darray_segment_in_cdata_get(&enclosure->data->sides) + iseg;
+ FOR_EACH(i, 0, 2) {
+#if (UINT_MAX < MEDIUM_MAX__)
+ ASSERT(segment->medium[i] < UINT_MAX);
+#endif
+ medium[i] = (unsigned) segment->medium[i]; /* Back to API type */
+ }
+ return RES_OK;
+}
+
+res_T
+senc2d_enclosure_get_segment_global_id
+ (const struct senc2d_enclosure* enclosure,
+ const unsigned iseg,
+ unsigned* gid)
+{
+ const struct segment_in* segment;
+ if(!enclosure || !gid
+ || iseg >= enclosure->data->header.segment_count)
+ return RES_BAD_ARG;
+ ASSERT(darray_segment_in_size_get(&enclosure->data->sides)
+ == enclosure->data->header.segment_count);
+ segment = darray_segment_in_cdata_get(&enclosure->data->sides) + iseg;
+ *gid = segment->global_id;
+ return RES_OK;
+}
+
+res_T
+senc2d_enclosure_ref_get(struct senc2d_enclosure* enc)
+{
+ if(!enc) return RES_BAD_ARG;
+ ref_get(&enc->ref);
+ return RES_OK;
+}
+
+res_T
+senc2d_enclosure_ref_put(struct senc2d_enclosure* enc)
+{
+ if(!enc) return RES_BAD_ARG;
+ ref_put(&enc->ref, enclosure_release);
+ return RES_OK;
+}
diff --git a/src/senc2d_enclosure_c.h b/src/senc2d_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 SENC2D_ENCLOSURE_C_H
+#define SENC2D_ENCLOSURE_C_H
+
+#include <rsys/ref_count.h>
+
+#include "senc2d.h"
+
+struct enclosure_data;
+struct senc2d_descriptor;
+
+struct senc2d_enclosure {
+ const struct enclosure_data* data;
+ struct senc2d_descriptor* desc;
+ ref_T ref;
+};
+
+struct senc2d_enclosure*
+enclosure_create
+ (struct senc2d_descriptor* desc,
+ const struct enclosure_data* data);
+
+#endif /* SENC2D_ENCLOSURE_C_H */
diff --git a/src/senc2d_enclosure_data.h b/src/senc2d_enclosure_data.h
@@ -0,0 +1,112 @@
+/* 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 SENC2D_ENCLOSURE_DATA_H
+#define SENC2D_ENCLOSURE_DATA_H
+
+#include <rsys/rsys.h>
+#include <rsys/ref_count.h>
+
+#include "senc2d.h"
+#include "senc2d_scene_c.h"
+#include "senc2d_internal_types.h"
+
+#include <limits.h>
+
+static void
+init_header(struct enclosure2d_header* header)
+{
+ ASSERT(header);
+ header->enclosure_id = ENCLOSURE_NULL__;
+ header->segment_count = 0;
+ header->unique_segment_count = 0;
+ header->vertices_count = 0;
+ header->enclosed_medium = MEDIUM_NULL__;
+ header->is_infinite = CHAR_MAX;
+}
+
+struct enclosure_data {
+ struct enclosure2d_header header;
+ /* Same segment can appear twice if both sides */
+ struct darray_segment_in sides;
+ /* Index of vertices in scene's unique vertices */
+ struct darray_vrtx_id vertices;
+ /* Number of components involved in this enclosure */
+ component_id_t cc_count;
+ /* Linked list of the components */
+ component_id_t first_component;
+ /* Range of segments member of the enclosure */
+ struct side_range side_range;
+ /* Counts */
+ side_id_t side_count;
+};
+
+static FINLINE void
+enclosure_data_init(struct mem_allocator* alloc, struct enclosure_data* enc) {
+ ASSERT(enc);
+ init_header(&enc->header);
+ enc->cc_count = 0;
+ enc->first_component = COMPONENT_NULL__;
+ enc->side_range.first = SIDE_NULL__;
+ enc->side_range.last = 0;
+ enc->side_count = 0;
+ darray_segment_in_init(alloc, &enc->sides);
+ darray_vrtx_id_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;
+ dst->cc_count = src->cc_count;
+ dst->first_component = src->first_component;
+ dst->side_range = src->side_range;
+ dst->side_count = src->side_count;
+ OK(darray_segment_in_copy(&dst->sides, &src->sides));
+ OK(darray_vrtx_id_copy(&dst->vertices, &src->vertices));
+error:
+ return res;
+}
+
+static FINLINE void
+enclosure_data_release(struct enclosure_data* n) {
+ ASSERT(n);
+ darray_segment_in_release(&n->sides);
+ darray_vrtx_id_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;
+ dst->cc_count = src->cc_count;
+ dst->first_component = src->first_component;
+ dst->side_range = src->side_range;
+ dst->side_count = src->side_count;
+ OK(darray_segment_in_copy_and_release(&dst->sides, &src->sides));
+ OK(darray_vrtx_id_copy_and_release(&dst->vertices, &src->vertices));
+error:
+ return res;
+}
+
+#endif /* SENC2D_ENCLOSURE_DATA_H */
diff --git a/src/senc2d_internal_types.h b/src/senc2d_internal_types.h
@@ -0,0 +1,114 @@
+/* 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 SENC2D_INTERNAL_TYPES_H
+#define SENC2D_INTERNAL_TYPES_H
+
+#include <rsys/math.h>
+
+#include <stdint.h>
+
+/* Utility macros */
+#ifdef NDEBUG
+#define OK2(Expr, Label)\
+ if((res = (Expr)) != RES_OK) goto Label;
+#else
+#define OK2(Expr, Label)\
+ if((res = (Expr)) != RES_OK) {\
+ fprintf(stderr, "%s: error code set to %d at line %d\n", FUNC_NAME, res, __LINE__);\
+ goto Label;\
+ }
+#endif
+
+#define OK(Expr) OK2((Expr), error)
+
+/* Side IDs are uint32_t */
+typedef uint32_t side_id_t;
+#define SIDE_MAX__ (UINT32_MAX-1)
+#define SIDE_NULL__ UINT32_MAX
+
+/* Seg IDs use internally side_id_t */
+/* Cannot be larger than unsigned, as the API uses it. */
+typedef side_id_t seg_id_t;
+/* SEG_MAX__ is limited to allow to count sides */
+#define SEG_MAX__ (SIDE_MAX__ / 2)
+#define SEG_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 uint32_t */
+/* Should nnot be larger than unsigned, as the API uses it. */
+typedef uint32_t medium_id_t;
+#define MEDIUM_MAX__ INT32_MAX
+#define MEDIUM_NULL__ UINT32_MAX
+
+/* Enclosure IDs are internally uint32_t */
+/* Cannot be larger than unsigned, as the API uses it. */
+typedef uint32_t enclosure_id_t;
+#define ENCLOSURE_MAX__ UINT32_MAX
+#define ENCLOSURE_NULL__ UINT32_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__
+
+/* This one is used as an index to arrays */
+enum side_id {
+ SIDE_FRONT = 0,
+ SIDE_BACK = 1
+};
+
+/* Utility macros */
+static FINLINE seg_id_t
+SEGSIDE_2_SEG(side_id_t s) {
+ ASSERT(((size_t)s >> 1) <= SEG_MAX__);
+ return s >> 1;
+}
+
+static FINLINE int
+SEGSIDE_IS_FRONT(side_id_t s) {
+ return (s & 1) == 0;
+}
+
+static FINLINE enum side_id
+SEGSIDE_2_SIDE(side_id_t s) {
+ return (s & 1) ? SIDE_BACK : SIDE_FRONT;
+}
+
+static FINLINE side_id_t
+SEGIDxSIDE_2_SEGSIDE(seg_id_t s, enum side_id i) {
+ ASSERT((((size_t)s << 1) | (i == SIDE_BACK)) < SIDE_MAX__);
+ ASSERT(i == SIDE_FRONT || i == SIDE_BACK);
+ return (side_id_t)((s << 1) | (i == SIDE_BACK));
+}
+
+static FINLINE side_id_t
+SEGSIDE_OPPOSITE(side_id_t s) {
+ return SEGIDxSIDE_2_SEGSIDE(SEGSIDE_2_SEG(s),
+ SEGSIDE_IS_FRONT(s) ? SIDE_BACK : SIDE_FRONT);
+}
+
+#endif /* SENC2D_INTERNAL_TYPES_H */
diff --git a/src/senc2d_s2d_wrapper.h b/src/senc2d_s2d_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 SENC2D_S2D_WRAPPER_H
+#define SENC2D_S2D_WRAPPER_H
+
+#include "senc2d.h"
+
+#include <rsys/rsys.h>
+#include <rsys/float2.h>
+
+FINLINE void
+senc2d_descriptor_get_global_indices__
+ (const unsigned iseg,
+ unsigned indices[2],
+ void* ctx)
+{
+ const struct senc2d_descriptor* descriptor = ctx;
+ res_T r;
+ ASSERT(indices && ctx);
+ r = senc2d_descriptor_get_global_segment(descriptor, iseg, indices);
+ ASSERT(r == RES_OK); (void)r;
+}
+
+static FINLINE void
+senc2d_descriptor_get_global_vertices__
+ (const unsigned ivert,
+ float coord[2],
+ void* ctx)
+{
+ const struct senc2d_descriptor* descriptor = ctx;
+ double tmp[2];
+ res_T r;
+ ASSERT(coord && ctx);
+ r = senc2d_descriptor_get_global_vertex(descriptor, ivert, tmp);
+ ASSERT(r == RES_OK); (void)r;
+ f2_set_d2(coord, tmp);
+}
+
+FINLINE void
+senc2d_enclosure_get_segment__
+ (const unsigned iseg,
+ unsigned indices[2],
+ void* ctx)
+{
+ const struct senc2d_enclosure* enclosure = ctx;
+ res_T r;
+ ASSERT(indices && ctx);
+ r = senc2d_enclosure_get_segment(enclosure, iseg, indices);
+ ASSERT(r == RES_OK); (void)r;
+}
+
+static FINLINE void
+senc2d_enclosure_get_vertex__
+ (const unsigned ivert,
+ float coord[2],
+ void* ctx)
+{
+ const struct senc2d_enclosure* enclosure = ctx;
+ double tmp[2];
+ res_T r;
+ ASSERT(coord && ctx);
+ r = senc2d_enclosure_get_vertex(enclosure, ivert, tmp);
+ ASSERT(r == RES_OK); (void)r;
+ f2_set_d2(coord, tmp);
+}
+
+#endif /* SENC2D_S2D_WRAPPER_H */
diff --git a/src/senc2d_scene.c b/src/senc2d_scene.c
@@ -0,0 +1,341 @@
+/* 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 "senc2d.h"
+#include "senc2d_device_c.h"
+#include "senc2d_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 senc2d_device* dev = NULL;
+ struct senc2d_scene* scn = NULL;
+ ASSERT(ref);
+ scn = CONTAINER_OF(ref, struct senc2d_scene, ref);
+ dev = scn->dev;
+ darray_segment_in_release(&scn->segments_in);
+ darray_position_release(&scn->vertices);
+ htable_vrtx_release(&scn->unique_vertices);
+ htable_seg_release(&scn->unique_segments);
+ darray_side_range_release(&scn->media_use);
+ MEM_RM(dev->allocator, scn);
+ SENC2D(device_ref_put(dev));
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+senc2d_scene_create
+ (struct senc2d_device* dev,
+ const unsigned nmeds,
+ struct senc2d_scene** out_scn)
+{
+ struct senc2d_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 senc2d_scene));
+ if(!scn) {
+ log_err(dev, "%s: could not allocate the StarEnclosures2D scene.\n", FUNC_NAME);
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ ref_init(&scn->ref);
+ SENC2D(device_ref_get(dev));
+ scn->dev = dev;
+ scn->ngeoms = 0;
+ scn->nsegs = 0;
+ scn->nusegs = 0;
+ scn->nmeds = (medium_id_t)nmeds;
+ scn->nverts = 0;
+ scn->nuverts = 0;
+ darray_segment_in_init(dev->allocator, &scn->segments_in);
+ darray_position_init(dev->allocator, &scn->vertices);
+ htable_vrtx_init(dev->allocator, &scn->unique_vertices);
+ htable_seg_init(dev->allocator, &scn->unique_segments);
+ darray_side_range_init(dev->allocator, &scn->media_use);
+ darray_side_range_resize(&scn->media_use, nmeds);
+
+exit:
+ if(scn) *out_scn = scn;
+ return res;
+error:
+ if(scn) {
+ SENC2D(scene_ref_put(scn));
+ scn = NULL;
+ }
+ goto exit;
+}
+
+res_T
+senc2d_scene_add_geometry
+ (struct senc2d_scene* scn,
+ const unsigned nsegs,
+ void(*indices)(const unsigned iseg, unsigned ids[2], void* ctx),
+ void(*media)(const unsigned iseg, unsigned med[2], void* ctx),
+ void(*global_id)(const unsigned iseg, unsigned* gid, void* ctx),
+ const unsigned nverts,
+ void(*position)(const unsigned ivert, double pos[2], void* ctx),
+ void* ctx)
+{
+ struct darray_vrtx_id unique_vertice_ids;
+ unsigned i;
+ vrtx_id_t actual_nverts = 0;
+ vrtx_id_t actual_nuverts = 0;
+ seg_id_t actual_nsegs = 0;
+ seg_id_t actual_nusegs = 0;
+ const struct segment_in* seg;
+ res_T res = RES_OK;
+
+ if(!scn
+ || !indices || !media || !position
+ || !nverts || ((size_t)scn->nverts + (size_t)nverts) > VRTX_MAX__
+ || !nsegs || ((size_t)scn->nsegs + (size_t)nsegs) > SEG_MAX__)
+ return RES_BAD_ARG;
+
+ /* Make room for new geometry; suppose no more duplicates. */
+ darray_vrtx_id_init(scn->dev->allocator, &unique_vertice_ids);
+ OK(darray_vrtx_id_reserve(&unique_vertice_ids, nverts));
+ OK(darray_position_reserve(&scn->vertices, scn->nuverts + nverts));
+ OK(darray_segment_in_reserve(&scn->segments_in, scn->nusegs + nsegs));
+ OK(htable_vrtx_reserve(&scn->unique_vertices, scn->nuverts + nverts));
+ OK(htable_seg_reserve(&scn->unique_segments, scn->nusegs + nsegs));
+
+ seg = darray_segment_in_cdata_get(&scn->segments_in);
+
+ /* Get geometry */
+ FOR_EACH(i, 0, nverts) {
+ vrtx_id_t* p_vrtx;
+ union double2 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 */
+ log_warn(scn->dev, "%s: vertex %lu is a duplicate of unique vertex %lu.\n",
+ FUNC_NAME, (unsigned long)(scn->nverts + i), (unsigned long)*p_vrtx);
+ log_warn(scn->dev, "%s: vertex %lu: (%g %g).\n",
+ FUNC_NAME, (unsigned long)(scn->nverts + i), SPLIT2(tmp.vec));
+ unique_v = *p_vrtx;
+ } else {
+ /* New vertex */
+ unique_v = scn->nuverts + actual_nuverts;
+ OK(darray_position_push_back(&scn->vertices, &tmp));
+ ASSERT(unique_v == htable_vrtx_size_get(&scn->unique_vertices));
+ OK(htable_vrtx_set(&scn->unique_vertices, &tmp, &unique_v));
+ ++actual_nuverts;
+ }
+ /* The unique ID for vertex i is unique_v */
+ ASSERT(i == darray_vrtx_id_size_get(&unique_vertice_ids));
+ OK(darray_vrtx_id_push_back(&unique_vertice_ids, &unique_v));
+ ++actual_nverts;
+ }
+
+ FOR_EACH(i, 0, nsegs) {
+ int j;
+ unsigned med[2];
+ unsigned ind[2];
+ union vrtx_id2 seg_key;
+ struct segment_in tmp;
+ seg_id_t* p_seg;
+ char reversed;
+ if(global_id) {
+ global_id(i, &tmp.global_id, ctx);
+ } else {
+ tmp.global_id = (unsigned)(scn->nsegs + i);
+ }
+ indices(i, ind, ctx); /* API: indices needs an unsigned */
+ FOR_EACH(j, 0, 2) {
+ if(ind[j] >= nverts) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ ASSERT(ind[j] < darray_vrtx_id_size_get(&unique_vertice_ids));
+ /* Find the unique ID for this vertex */
+ tmp.vertice_id[j] = darray_vrtx_id_cdata_get(&unique_vertice_ids)[ind[j]];
+ }
+ if(tmp.vertice_id[0] == tmp.vertice_id[1]) {
+ const union double2* positions
+ = darray_position_cdata_get(&scn->vertices);
+ log_err(scn->dev, "%s: segment %lu is degenerate.\n",
+ FUNC_NAME, (unsigned long)tmp.global_id);
+ log_err(scn->dev, " (%g %g) (%g %g)\n",
+ SPLIT2(positions[seg[i].vertice_id[0]].vec),
+ SPLIT2(positions[seg[i].vertice_id[1]].vec));
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ /* Get media */
+ media(i, med, ctx); /* API: media needs an unsigned */
+ ASSERT(scn->nmeds <= MEDIUM_MAX__);
+ FOR_EACH(j, 0, 2) {
+ if(med[j] >= scn->nmeds) {
+ log_err(scn->dev,
+ "%s: segment %lu %s side references invalid medium: %lu.\n",
+ FUNC_NAME,
+ (unsigned long)tmp.global_id,
+ (j ? "back" : "front"),
+ (unsigned long)med[j]);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ tmp.medium[j] = (medium_id_t)med[j];
+ }
+ /* Find duplicate segments */
+ reversed = seg_make_key(&seg_key, tmp.vertice_id);
+ p_seg = htable_seg_find(&scn->unique_segments, &seg_key);
+ if(p_seg) {
+ union vrtx_id2 useg_key;
+ char ureversed = seg_make_key(&useg_key, seg[*p_seg].vertice_id);
+ int same = (reversed == ureversed);
+ const medium_id_t* umed;
+ /* Duplicate segment. Need to check duplicate validity */
+ ASSERT(seg_key_eq(&seg_key, &useg_key));
+ umed = seg[*p_seg].medium;
+ if(umed[0] != (same ? med[0] : med[1])
+ || umed[1] != (same ? med[1] : med[0])) {
+ /* Same segments with different media: invalid! */
+ const union double2* positions
+ = darray_position_cdata_get(&scn->vertices);
+ log_err(scn->dev, "%s: segment %lu is a duplicate"
+ " of segment %lu with incoherent media.\n",
+ FUNC_NAME, (unsigned long)tmp.global_id,
+ (unsigned long)seg[*p_seg].global_id);
+ log_err(scn->dev,
+ "Segment %lu:\n (%g %g ) (%g %g)\n",
+ (unsigned long)seg[*p_seg].global_id,
+ SPLIT2(positions[seg[*p_seg].vertice_id[0]].vec),
+ SPLIT2(positions[seg[*p_seg].vertice_id[1]].vec));
+ log_err(scn->dev, "Media: (%lu, %lu) VS (%lu, %lu)\n",
+ (unsigned long)umed[ureversed? 1 : 0],
+ (unsigned long)umed[ureversed ? 0 : 1],
+ (unsigned long)med[reversed ? 1 : 0],
+ (unsigned long)med[reversed ? 0 : 1]);
+ res = RES_BAD_ARG;
+ goto error;
+ } else {
+ /* Legit duplicate */
+ log_warn(scn->dev, "%s: segment %lu is a duplicate of segment %lu.\n",
+ FUNC_NAME, (unsigned long)tmp.global_id,
+ (unsigned long)seg[*p_seg].global_id);
+ if(!same) {
+ FOR_EACH(j, 0, 2) {
+ tmp.medium[j] = (medium_id_t)med[1-j];
+ }
+ }
+ }
+ } else {
+ /* New segment */
+ seg_id_t u = scn->nusegs + actual_nusegs;
+ struct side_range* media_use;
+ ASSERT(u == htable_seg_size_get(&scn->unique_segments));
+ OK(htable_seg_set(&scn->unique_segments, &seg_key, &u));
+ OK(darray_segment_in_push_back(&scn->segments_in, &tmp));
+ FOR_EACH(j, 0, 2) {
+ ASSERT(tmp.medium[j] < scn->nmeds);
+ media_use = darray_side_range_data_get(&scn->media_use) + tmp.medium[j];
+ media_use->first = MMIN(media_use->first, SEGIDxSIDE_2_SEGSIDE(u, j));
+ ASSERT(media_use->first < 2 * (scn->nusegs + actual_nusegs + 1));
+ media_use->last = MMAX(media_use->last, SEGIDxSIDE_2_SEGSIDE(u, j));
+ ASSERT(media_use->last < 2 * (scn->nusegs + actual_nusegs + 1));
+ ASSERT(media_use->first <= media_use->last);
+ }
+ ++actual_nusegs;
+ }
+ ++actual_nsegs;
+ }
+
+exit:
+ darray_vrtx_id_release(&unique_vertice_ids);
+ /* Update sizes */
+ scn->nuverts += actual_nuverts;
+ scn->nverts += actual_nverts;
+ scn->nusegs += actual_nusegs;
+ scn->nsegs += actual_nsegs;
+ ASSERT(scn->nuverts == htable_vrtx_size_get(&scn->unique_vertices));
+ ASSERT(scn->nusegs == htable_seg_size_get(&scn->unique_segments));
+ ++scn->ngeoms;
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+senc2d_scene_get_segments_count
+ (const struct senc2d_scene* scn,
+ unsigned* count)
+{
+ if(!scn || !count) return RES_BAD_ARG;
+ *count = scn->nsegs;
+ return RES_OK;
+}
+
+res_T
+senc2d_scene_get_unique_segments_count
+ (const struct senc2d_scene* scn,
+ unsigned* count)
+{
+ if(!scn || !count) return RES_BAD_ARG;
+ *count = scn->nusegs;
+ return RES_OK;
+}
+
+res_T
+senc2d_scene_get_vertices_count
+ (const struct senc2d_scene* scn,
+ unsigned* count)
+{
+ if(!scn || !count) return RES_BAD_ARG;
+ *count = scn->nverts;
+ return RES_OK;
+}
+
+res_T
+senc2d_scene_get_unique_vertices_count
+ (const struct senc2d_scene* scn,
+ unsigned* count)
+{
+ if(!scn || !count) return RES_BAD_ARG;
+ *count = scn->nuverts;
+ return RES_OK;
+}
+
+res_T
+senc2d_scene_ref_get(struct senc2d_scene* scn)
+{
+ if(!scn) return RES_BAD_ARG;
+ ref_get(&scn->ref);
+ return RES_OK;
+}
+
+res_T
+senc2d_scene_ref_put(struct senc2d_scene* scn)
+{
+ if(!scn) return RES_BAD_ARG;
+ ref_put(&scn->ref, scene_release);
+ return RES_OK;
+}
diff --git a/src/senc2d_scene_analyze.c b/src/senc2d_scene_analyze.c
@@ -0,0 +1,1165 @@
+/* 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 "senc2d.h"
+#include "senc2d_descriptor_c.h"
+#include "senc2d_device_c.h"
+#include "senc2d_scene_c.h"
+#include "senc2d_scene_analyze_c.h"
+#include "senc2d_internal_types.h"
+
+#include <rsys/rsys.h>
+#include <rsys/float2.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/hash_table.h>
+#include <rsys/dynamic_array.h>
+
+#if defined(COMPILER_GCC)
+#define ATOMIC_CAS_PTR(Atom, NewVal, Comparand) /* Return the initial value */ \
+ ATOMIC_CAS((Atom), (NewVal), (Comparand))
+#elif defined(COMPILER_CL)
+#define ATOMIC_CAS_PTR(Atom, NewVal, Comparand) /* Return the initial value */ \
+ (InterlockedCompareExchangePointer(Atom, NewVal, Comparand))
+#else
+#error "Undefined atomic operations"
+#endif
+
+#include <star/s2d.h>
+
+#include <omp.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#define CC_DESCRIPTOR_NULL__ {\
+ {0,-DBL_MAX}, -1, SIDE_NULL__, VRTX_NULL__, 0, MEDIUM_NULL__,\
+ CC_ID_NONE, CC_GROUP_ROOT_NONE, CC_GROUP_ID_NONE, CC_ID_NONE,\
+ { SEG_NULL__, 0}\
+}
+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>
+
+/*******************************************************************************
+ * Helper function
+ ******************************************************************************/
+static INLINE int
+find_side_in_list
+ (const struct segside* segsides,
+ 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)segsides;
+ ASSERT(segsides && 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(segsides[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 FINLINE void
+add_side_to_stack
+ (const struct senc2d_scene* scn,
+ struct darray_side_id* stack,
+ struct segside* segsides,
+ const side_id_t side_id)
+{
+ (void)scn;
+ ASSERT(scn && segsides && stack
+ && side_id < SIDE_MAX__ && side_id < 2 * scn->nusegs);
+ ASSERT((darray_side_id_size_get(stack) > 128)
+ || !find_side_in_list(segsides, stack, side_id, FLAG_WAITING_STACK));
+ darray_side_id_push_back(stack, &side_id);
+ segsides[side_id].list_id = FLAG_WAITING_STACK;
+}
+
+static side_id_t
+get_side_not_in_connex_component
+ (const struct senc2d_scene* scn,
+ const struct segside* segsides,
+ side_id_t* first_side_not_in_component,
+ const medium_id_t medium)
+{
+ side_id_t i;
+ const seg_id_t last_side
+ = darray_side_range_cdata_get(&scn->media_use)[medium].last;
+ ASSERT(scn && segsides && first_side_not_in_component);
+ i = *first_side_not_in_component;
+ while(i <= last_side
+ && (segsides[i].medium != medium
+ || segsides[i].list_id != FLAG_LIST_SIDE_LIST)) {
+ ++i;
+ }
+
+ *first_side_not_in_component = i+1;
+ if(i > last_side) return SIDE_NULL__;
+ return i;
+}
+
+static FINLINE side_id_t
+get_side_from_stack
+ (const struct segside* segsides,
+ struct darray_side_id* stack)
+{
+ side_id_t id;
+ size_t sz;
+ (void)segsides;
+ ASSERT(segsides && stack);
+ sz = darray_side_id_size_get(stack);
+ ASSERT(sz);
+ id = darray_side_id_cdata_get(stack)[sz - 1];
+ ASSERT(segsides[id].list_id == FLAG_WAITING_STACK);
+ darray_side_id_pop_back(stack);
+ return id;
+}
+
+static void
+get_scn_indices(const unsigned iseg, unsigned ids[2], void* ctx) {
+ int i;
+ const struct senc2d_scene* scene = ctx;
+ const struct segment_in* seg =
+ darray_segment_in_cdata_get(&scene->segments_in) + iseg;
+ FOR_EACH(i, 0, 2) {
+ ASSERT(seg->vertice_id[i] < scene->nverts);
+ ids[i] = (unsigned)seg->vertice_id[i]; /* Back to API type */
+ }
+}
+
+static void
+get_scn_position(const unsigned ivert, float pos[2], void* ctx) {
+ const struct senc2d_scene* scene = ctx;
+ const union double2* pt =
+ darray_position_cdata_get(&scene->vertices) + ivert;
+ f2_set_d2(pos, pt->vec);
+}
+
+static int
+self_hit_filter
+ (const struct s2d_hit* hit,
+ const float ray_org[2],
+ const float ray_dir[2],
+ void* ray_data,
+ void* filter_data)
+{
+ const struct darray_segment_comp* segments_comp = filter_data;
+ const component_id_t* origin_component = ray_data;
+ const struct segment_comp* hit_seg_comp;
+ enum side_id hit_side;
+ component_id_t hit_component;
+
+ (void) ray_org; (void) ray_dir;
+ ASSERT(hit && segments_comp && origin_component);
+ ASSERT(hit->prim.prim_id < darray_segment_comp_size_get(segments_comp));
+ hit_seg_comp = darray_segment_comp_cdata_get(segments_comp)
+ + hit->prim.prim_id;
+ hit_side = (hit->normal[1] > 0) ? SIDE_FRONT : SIDE_BACK;
+ hit_component = hit_seg_comp->component[hit_side];
+
+ /* Not self hit or distance should be small */
+ ASSERT(hit_component != *origin_component || hit->distance < 1e-6);
+ return (hit_component == *origin_component);
+}
+
+static res_T
+extract_connex_components
+ (struct senc2d_descriptor* desc,
+ struct segside* segsides,
+ struct darray_ptr_component_descriptor* connex_components,
+ const struct darray_segment_tmp* segments_tmp_array,
+ struct darray_segment_comp* segments_comp,
+ struct s2d_scene_view** s2d_view)
+{
+ res_T res = RES_OK;
+ const struct senc2d_scene* scn;
+ struct mem_allocator* alloc;
+ ATOMIC component_count = 0;
+ volatile int exit_for = 0;
+ int64_t mm;
+#ifndef NDEBUG
+ seg_id_t s_;
+ component_id_t c;
+#endif
+
+ ASSERT(segsides && desc && connex_components && segments_tmp_array);
+ ASSERT(darray_ptr_component_descriptor_size_get(connex_components) == 0);
+ alloc = descriptor_get_allocator(desc);
+ scn = desc->scene;
+
+ /* Just a hint; to avoid contention on first loop */
+ OK2(darray_ptr_component_descriptor_reserve(connex_components, 2 * scn->nmeds),
+ error_); /* Cannot goto into openmp block */
+
+#ifndef NDEBUG
+ FOR_EACH(s_, 0, scn->nusegs) {
+ const struct segment_in* seg_in =
+ darray_segment_in_cdata_get(&scn->segments_in) + s_;
+ const struct side_range* media_use
+ = darray_side_range_cdata_get(&scn->media_use);
+ FOR_EACH(mm, 0, 2) {
+ const side_id_t side = SEGIDxSIDE_2_SEGSIDE(s_, mm);
+ const medium_id_t medium = seg_in->medium[mm];
+ ASSERT(media_use[medium].first <= side && side <= media_use[medium].last);
+ }
+ }
+#endif
+
+ #pragma omp parallel
+ {
+ struct darray_side_id stack;
+ darray_side_id_init(alloc, &stack);
+
+ #pragma omp for schedule(dynamic) nowait
+ for(mm = 0; mm < (int64_t)scn->nmeds; mm++) { /* Process all media */
+ const medium_id_t m = (medium_id_t)mm;
+ struct cc_descriptor* cc;
+ /* Any not-already-used side is used as a starting point */
+ side_id_t first_side_not_in_component;
+
+ if(exit_for) continue;
+ first_side_not_in_component
+ = darray_side_range_cdata_get(&scn->media_use)[m].first;
+ if(first_side_not_in_component == SIDE_NULL__)
+ continue; /* Unused medium */
+ ASSERT(first_side_not_in_component < 2 * scn->nusegs);
+ ASSERT(darray_side_id_size_get(&stack) == 0);
+ for(;;) { /* Process all components for this medium */
+ const side_id_t start_side_id = get_side_not_in_connex_component(scn,
+ segsides, &first_side_not_in_component, m);
+ side_id_t crt_side_id = start_side_id;
+ side_id_t last_side_id = start_side_id;
+ ASSERT(start_side_id == SIDE_NULL__ || start_side_id < 2 * scn->nusegs);
+ if(start_side_id == SIDE_NULL__)
+ break; /* start_side_id=SIDE_NULL__ => done! */
+ ASSERT(segsides[start_side_id].list_id == FLAG_LIST_SIDE_LIST);
+
+#ifndef NDEBUG
+ {
+ seg_id_t tid = SEGSIDE_2_SEG(start_side_id);
+ enum side_flag s = SEGSIDE_2_SIDE(start_side_id);
+ medium_id_t side_med
+ = darray_segment_in_data_get(&desc->scene->segments_in)[tid].medium[s];
+ ASSERT(side_med == m);
+ }
+#endif
+
+ /* Create and init a new component */
+ cc = MEM_ALLOC(alloc, sizeof(struct cc_descriptor));
+ if(!cc) {
+ res = RES_MEM_ERR;
+ goto error1;
+ }
+ cc_descriptor_init(alloc, cc);
+ ASSERT(m == segsides[start_side_id].medium);
+ cc->cc_id = (component_id_t)(ATOMIC_INCR(&component_count) - 1);
+ cc->medium = m;
+ cc->side_range.first = start_side_id;
+
+ for(;;) { /* Process all sides of this component */
+ int i;
+ enum side_flag crt_side_flag = SEGSIDE_2_SIDE(crt_side_id);
+ struct segside* crt_side = segsides + crt_side_id;
+ const seg_id_t crt_seg_id = SEGSIDE_2_SEG(crt_side_id);
+ const struct segment_in* seg_in =
+ darray_segment_in_cdata_get(&scn->segments_in) + crt_seg_id;
+ struct segment_comp* seg_comp =
+ darray_segment_comp_data_get(segments_comp) + crt_seg_id;
+ const struct segment_tmp* const seg_tmp =
+ darray_segment_tmp_cdata_get(segments_tmp_array) + crt_seg_id;
+ const union double2* vertices =
+ darray_position_cdata_get(&scn->vertices);
+ ASSERT(crt_seg_id < scn->nusegs);
+ ASSERT(segsides[crt_side_id].medium == m);
+
+ /* Record Ymax information
+ * 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 Y
+ * coordinate.
+ * If more than one vertex/side has the same Y, we want the side that
+ * most faces Y (that is the one with the greater ny).
+ * This is mandatory to select the correct side when both sides of a
+ * segment are candidate. */
+ if(cc->max_vrtx[1] <= seg_tmp->max_y) {
+ /* Can either improve y or ny */
+
+ if(cc->max_y_side_id == SEGSIDE_OPPOSITE(crt_side_id)) {
+ /* Both sides are in cc and the opposite side is currently selected
+ * Just keep the side with ny>0 */
+ if(cc->max_y_ny < 0) {
+ /* Change side! */
+ cc->max_y_ny = -cc->max_y_ny;
+ cc->max_y_side_id = crt_side_id;
+ }
+ } else {
+ double edge[2], normal[2], norm, side_ny;
+ int process = 0;
+
+ d2_sub(edge, vertices[seg_in->vertice_id[1]].vec,
+ vertices[seg_in->vertice_id[0]].vec);
+ d2(normal, edge[1], -edge[0]);
+ norm = d2_normalize(normal, normal);
+ ASSERT(norm); (void) norm;
+
+ /* Geometrical normal points toward the right */
+ if(SEGSIDE_IS_FRONT(crt_side_id)) {
+ side_ny = -normal[1];
+ process = 1;
+ } else {
+ side_ny = normal[1];
+ process = 1;
+ }
+
+ if(process) {
+ int change = 0;
+ if(cc->max_vrtx[1] < seg_tmp->max_y) {
+ change = 1; /* Try first to improve y */
+ } else if(cc->max_y_ny <= 0 && fabs(cc->max_y_ny) < fabs(side_ny)) {
+ change = 1; /* If ny <= 0, the more negative the better */
+ } else if(cc->max_y_ny > 0 && cc->max_y_ny < side_ny) {
+ change = 1; /* If ny > 0, the more positive the better */
+ }
+
+ if(change) {
+ cc->max_y_ny = side_ny;
+ cc->max_y_side_id = crt_side_id;
+ ASSERT(seg_tmp->max_y_vrtx_rank < 2);
+ ASSERT(seg_in->vertice_id[seg_tmp->max_y_vrtx_rank] < scn->nverts);
+ cc->max_y_vrtx_id = seg_in->vertice_id[seg_tmp->max_y_vrtx_rank];
+ ASSERT(seg_tmp->max_y == vertices[cc->max_y_vrtx_id].pos.y);
+ d2_set(cc->max_vrtx, vertices[cc->max_y_vrtx_id].vec);
+ }
+ }
+ }
+ }
+
+ /* Record crt_side both as component and segment level */
+ cc->side_count++;
+ segsides[crt_side_id].list_id = FLAG_LIST_COMPONENT;
+ ASSERT(seg_comp->component[crt_side_flag] == COMPONENT_NULL__);
+ seg_comp->component[crt_side_flag] = cc->cc_id;
+#ifndef NDEBUG
+ crt_side->member_of_cc = cc->cc_id;
+#endif
+
+ /* Store neighbour sides in a waiting stack */
+ FOR_EACH(i, 0, 2) {
+ side_id_t neighbour_id = crt_side->facing_side_id[i];
+ seg_id_t nbour_seg_id = SEGSIDE_2_SEG(neighbour_id);
+ const struct segside* neighbour = segsides + neighbour_id;
+ ASSERT(m == crt_side->medium);
+ if(neighbour->medium != crt_side->medium) {
+ /* Found medium discontinuity! Model topology is broken. */
+ const struct segment_in* segments_in
+ = darray_segment_in_cdata_get(&scn->segments_in);
+ const union double2* positions
+ = darray_position_cdata_get(&scn->vertices);
+ log_err(scn->dev,
+ "Medium mismatch found between neighbour segments %lu %s"
+ " side and %u %s side.\n",
+ (unsigned long) segments_in[crt_seg_id].global_id,
+ SEGSIDE_IS_FRONT(crt_side_id) ? "front" : "back",
+ segments_in[nbour_seg_id].global_id,
+ SEGSIDE_IS_FRONT(neighbour_id) ? "front" : "back");
+ log_err(scn->dev,
+ "Segment %lu:\n (%g %g) (%g %g))\n",
+ (unsigned long) segments_in[crt_seg_id].global_id,
+ SPLIT2(positions[segments_in[crt_seg_id].vertice_id[0]].vec),
+ SPLIT2(positions[segments_in[crt_seg_id].vertice_id[1]].vec));
+ log_err(scn->dev,
+ "Segment %lu:\n (%g %g) (%g %g)\n",
+ (unsigned long) segments_in[nbour_seg_id].global_id,
+ SPLIT2(positions[segments_in[nbour_seg_id].vertice_id[0]].vec),
+ SPLIT2(positions[segments_in[nbour_seg_id].vertice_id[1]].vec));
+ log_err(desc->scene->dev, "Media: %lu VS %lu\n",
+ (unsigned long)neighbour->medium, (unsigned long)crt_side->medium);
+ res = RES_BAD_ARG;
+ goto error1;
+ }
+ if(neighbour->list_id == FLAG_LIST_COMPONENT) {
+ /* Already processed */
+#ifndef NDEBUG
+ ASSERT(neighbour->member_of_cc == cc->cc_id);
+#endif
+ continue;
+ }
+ if(neighbour->list_id == FLAG_WAITING_STACK) {
+ continue; /* Already processed */
+ }
+ add_side_to_stack(scn, &stack, segsides, neighbour_id);
+ }
+ if(darray_side_id_size_get(&stack) == 0)
+ break; /* Empty stack => connex component is done! */
+ crt_side_id = get_side_from_stack(segsides, &stack);
+ last_side_id = MMAX(last_side_id, crt_side_id);
+ }
+ /* Keep track of this new connex component */
+ cc->side_range.last = last_side_id;
+ /* Need to synchronize connex_components growth as this global structure
+ * is accessed by multipe threads */
+ #pragma omp critical
+ {
+ struct cc_descriptor** components;
+ size_t sz = darray_ptr_component_descriptor_size_get(connex_components);
+ if(sz <= cc->cc_id) {
+ res_T tmp_res = darray_ptr_component_descriptor_resize(connex_components,
+ 1 + cc->cc_id);
+ if(tmp_res != RES_OK) res = tmp_res;
+ }
+ if(res == RES_OK) {
+ /* Don't set the pointer before resize as this can lead to move data */
+ components =
+ darray_ptr_component_descriptor_data_get(connex_components);
+ ASSERT(components[cc->cc_id] == NULL);
+ components[cc->cc_id] = cc;
+ }
+ }
+ OK2(res, error1);
+ }
+ continue;
+ error1:
+ /* Cannot goto out of openmp block */
+ exit_for = 1;
+ continue;
+ }
+ /* No barrier here (nowait clause).
+ * The first thread executes the single block */
+ darray_side_id_release(&stack);
+ #pragma omp single nowait
+ if(res == RES_OK) {
+ struct s2d_device* s2d = NULL;
+ struct s2d_scene* s2d_scn = NULL;
+ struct s2d_shape* s2d_shp = NULL;
+ struct s2d_vertex_data attribs;
+
+ attribs.type = S2D_FLOAT2;
+ attribs.usage = S2D_POSITION;
+ attribs.get = get_scn_position;
+
+ /* Put geometry in a 2D view */
+ OK(s2d_device_create(desc->scene->dev->logger, alloc, 0, &s2d));
+ OK(s2d_scene_create(s2d, &s2d_scn));
+ OK(s2d_shape_create_line_segments(s2d, &s2d_shp));
+
+ /* Back to API type for ntris and nverts */
+ ASSERT(desc->scene->nusegs < UINT_MAX);
+ ASSERT(desc->scene->nuverts < UINT_MAX);
+ OK(s2d_line_segments_setup_indexed_vertices(s2d_shp, (unsigned)desc->scene->nusegs,
+ get_scn_indices, (unsigned)desc->scene->nuverts, &attribs, 1, desc->scene));
+ s2d_line_segments_set_hit_filter_function(s2d_shp, self_hit_filter, segments_comp);
+ OK(s2d_scene_attach_shape(s2d_scn, s2d_shp));
+ OK(s2d_scene_view_create(s2d_scn, S2D_TRACE, s2d_view));
+ error:
+ if(s2d) S2D(device_ref_put(s2d));
+ if(s2d_scn) S2D(scene_ref_put(s2d_scn));
+ if(s2d_shp) S2D(shape_ref_put(s2d_shp));
+ }
+ }
+ OK2(res, error_);
+
+ ASSERT(component_count ==
+ (int)darray_ptr_component_descriptor_size_get(connex_components));
+#ifndef NDEBUG
+ FOR_EACH(s_, 0, scn->nusegs) {
+ struct segment_comp* seg_comp =
+ darray_segment_comp_data_get(segments_comp) + s_;
+ ASSERT(seg_comp->component[SIDE_FRONT] != COMPONENT_NULL__);
+ ASSERT(seg_comp->component[SIDE_BACK] != COMPONENT_NULL__);
+ }
+ FOR_EACH(c, 0, component_count) {
+ struct cc_descriptor** components =
+ darray_ptr_component_descriptor_data_get(connex_components);
+ ASSERT(components[c] != NULL &&
+ components[c]->cc_id == c);
+ }
+#endif
+
+exit:
+ ASSERT(desc->segment_count
+ == darray_segment_comp_size_get(segments_comp));
+ /* segments_enc is still unused: no size to assert */
+ return res;
+error_:
+ goto exit;
+}
+
+static res_T
+group_connex_components
+ (struct senc2d_descriptor* desc,
+ struct segside* segsides,
+ struct darray_segment_comp* segments_comp,
+ struct darray_ptr_component_descriptor* connex_components,
+ struct s2d_scene_view* s2d_view)
+{
+ res_T res = RES_OK;
+ struct cc_descriptor** descriptors;
+ size_t tmp;
+ component_id_t cc_count;
+ int64_t ccc;
+ volatile int exit_for = 0;
+ ATOMIC next_enclosure_id = desc->enclosures_count;
+ struct cc_descriptor* infinity_first_cc = NULL;
+ (void)segsides;
+ ASSERT(desc && segsides && segments_comp && connex_components);
+
+ descriptors = darray_ptr_component_descriptor_data_get(connex_components);
+ tmp = darray_ptr_component_descriptor_size_get(connex_components);
+ ASSERT(tmp <= COMPONENT_MAX__);
+ cc_count = (component_id_t)tmp;
+
+ /* Cast rays to find links between connex components */
+ #pragma omp parallel for
+ for(ccc = 0; ccc < (int64_t)cc_count; ccc++) {
+ component_id_t c = (component_id_t)ccc;
+ struct s2d_hit hit = S2D_HIT_NULL;
+ float origin[2];
+ const float dir[2] = { 0, 1 };
+ const float range[2] = { 0, FLT_MAX };
+ struct cc_descriptor* const cc = descriptors[c];
+ const struct segment_comp* origin_seg =
+ darray_segment_comp_cdata_get(segments_comp) + cc->max_y_vrtx_id;
+ component_id_t self_hit_component
+ = origin_seg->component[1 - SEGSIDE_2_SIDE(cc->max_y_side_id)];
+
+ if(exit_for) continue;
+ ASSERT(cc->cc_id == c);
+ ASSERT(cc->cc_group_root == CC_GROUP_ID_NONE);
+
+ if(cc->max_y_ny < 0) {
+ int64_t id;
+ /* Don't need to cast a ray */
+ cc->cc_group_root = cc->cc_id; /* New group with self as root */
+ id = ATOMIC_INCR(&next_enclosure_id) - 1;
+ ASSERT(id < ENCLOSURE_MAX__);
+ cc->enclosure_id = (enclosure_id_t)id;
+ continue;
+ }
+
+ ASSERT(cc->max_y_ny != 0
+ /* The only situation with ny==0 we can think of is this one: */
+ || (segsides[cc->max_y_side_id].medium
+ == segsides[SEGSIDE_OPPOSITE(cc->max_y_side_id)].medium
+ && (segsides[cc->max_y_side_id].facing_side_id[0]
+ == SEGSIDE_OPPOSITE(cc->max_y_side_id)
+ || segsides[cc->max_y_side_id].facing_side_id[1]
+ == SEGSIDE_OPPOSITE(cc->max_y_side_id))));
+ f2_set_d2(origin, cc->max_vrtx);
+ /* Self-hit data: self hit if hit this component "on the other side" */
+ OK2(s2d_scene_view_trace_ray(s2d_view, origin, dir, range,
+ &self_hit_component, &hit), error_);
+ /* If no hit, the component is facing an infinite medium */
+ if(S2D_HIT_NONE(&hit)) {
+ cc->cc_group_root = CC_GROUP_ROOT_INFINITE;
+ cc->enclosure_id = 0;
+ /* Keep track of the first component facing infinity */
+ ATOMIC_CAS_PTR(&infinity_first_cc, cc, NULL);
+ if(infinity_first_cc->medium != cc->medium) {
+ const side_id_t infinity_first_side = infinity_first_cc->max_y_side_id;
+ const medium_id_t infinity_medium = infinity_first_cc->medium;
+ /* Medium mismatch! Model topology is broken. */
+ const seg_id_t t1 = SEGSIDE_2_SEG(infinity_first_side);
+ const seg_id_t t2 = SEGSIDE_2_SEG(cc->max_y_side_id);
+ const struct segment_in* segments_in
+ = darray_segment_in_cdata_get(&desc->scene->segments_in);
+ const union double2* positions
+ = darray_position_cdata_get(&desc->scene->vertices);
+ log_err(desc->scene->dev,
+ "Medium mismatch found between segment %lu %s side and segment"
+ " %lu %s side, both facing infinity.\n",
+ (unsigned long) segments_in[t1].global_id,
+ SEGSIDE_IS_FRONT(infinity_first_side) ? "front" : "back",
+ (unsigned long) segments_in[t2].global_id,
+ SEGSIDE_IS_FRONT(cc->max_y_side_id) ? "front" : "back");
+ log_err(desc->scene->dev,
+ "Segment %lu:\n (%g %g) (%g %g)\n",
+ (unsigned long) segments_in[t1].global_id,
+ SPLIT2(positions[segments_in[t1].vertice_id[0]].vec),
+ SPLIT2(positions[segments_in[t1].vertice_id[1]].vec));
+ log_err(desc->scene->dev,
+ "Segment %lu:\n (%g %g) (%g %g)\n",
+ (unsigned long) segments_in[t2].global_id,
+ SPLIT2(positions[segments_in[t2].vertice_id[0]].vec),
+ SPLIT2(positions[segments_in[t2].vertice_id[1]].vec));
+ log_err(desc->scene->dev, "Media: %lu VS %lu\n",
+ (unsigned long)infinity_medium, (unsigned long)cc->medium);
+ res = RES_BAD_ARG;
+ goto error_;
+ }
+ /* Same medium as previous members of the group: OK */
+ continue;
+ } else {
+ /* If hit, group this component */
+ const seg_id_t hit_seg_id = (seg_id_t)hit.prim.prim_id;
+ const struct segment_in* hit_seg_in =
+ darray_segment_in_cdata_get(&desc->scene->segments_in) + hit_seg_id;
+ const struct segment_comp* hit_seg_comp =
+ darray_segment_comp_cdata_get(segments_comp) + hit_seg_id;
+ enum side_id hit_side = (hit.normal[1] > 0) ? SIDE_FRONT : SIDE_BACK;
+ const side_id_t hit_side_id = SEGIDxSIDE_2_SEGSIDE(hit_seg_id, hit_side);
+
+ ASSERT(hit_seg_id < desc->scene->nusegs);
+
+ /* Not really the root until following links */
+ cc->cc_group_root = hit_seg_comp->component[hit_side];
+#ifndef NDEBUG
+ {
+ const struct cc_descriptor* hit_desc;
+ ASSERT(cc->cc_group_root
+ < darray_ptr_component_descriptor_size_get(connex_components));
+ hit_desc = *(darray_ptr_component_descriptor_cdata_get(connex_components)
+ + cc->cc_group_root);
+ ASSERT(hit_desc->medium == hit_seg_in->medium[hit_side]);
+ }
+#endif
+ if(hit_seg_in->medium[hit_side] != cc->medium) {
+ /* Medium mismatch! Model topology is broken. */
+ const seg_id_t t1 = SEGSIDE_2_SEG(hit_side_id);
+ const seg_id_t t2 = SEGSIDE_2_SEG(cc->max_y_side_id);
+ const struct segment_in* segments_in
+ = darray_segment_in_cdata_get(&desc->scene->segments_in);
+ const union double2* positions
+ = darray_position_cdata_get(&desc->scene->vertices);
+ log_err(desc->scene->dev,
+ "Medium mismatch found between segment %lu %s side and segment"
+ " %lu %s side facing each other.\n",
+ (unsigned long) segments_in[t1].global_id,
+ SEGSIDE_IS_FRONT(hit_side) ? "front" : "back",
+ (unsigned long) segments_in[t2].global_id,
+ SEGSIDE_IS_FRONT(cc->max_y_side_id) ? "front" : "back");
+ log_err(desc->scene->dev,
+ "Segment %lu:\n (%g %g) (%g %g)\n",
+ (unsigned long) segments_in[t1].global_id,
+ SPLIT2(positions[segments_in[t1].vertice_id[0]].vec),
+ SPLIT2(positions[segments_in[t1].vertice_id[1]].vec));
+ log_err(desc->scene->dev,
+ "Segment %lu:\n (%g %g) (%g %g)\n",
+ (unsigned long) segments_in[t2].global_id,
+ SPLIT2(positions[segments_in[t2].vertice_id[0]].vec),
+ SPLIT2(positions[segments_in[t2].vertice_id[1]].vec));
+ log_err(desc->scene->dev, "Media: %lu VS %lu\n",
+ (unsigned long)hit_seg_in->medium[hit_side],
+ (unsigned long)cc->medium);
+
+ res = RES_BAD_ARG;
+ goto error_;
+ }
+ }
+ continue;
+ error_:
+ /* Cannot goto out of openmp block */
+ exit_for = 1;
+ continue;
+ }
+ ASSERT(next_enclosure_id < ENCLOSURE_MAX__);
+ desc->enclosures_count = (enclosure_id_t)next_enclosure_id;
+ OK(res);
+
+ /* Post-process links to group connex components */
+ OK(darray_enclosure_resize(&desc->enclosures, desc->enclosures_count));
+ FOR_EACH(ccc, 0, cc_count) {
+ component_id_t c = (component_id_t)ccc;
+ struct cc_descriptor* const cc = descriptors[c];
+ const struct cc_descriptor* other_desc = cc;
+ struct enclosure_data* enclosures
+ = darray_enclosure_data_get(&desc->enclosures);
+ component_id_t fst;
+
+ while(other_desc->enclosure_id == CC_GROUP_ID_NONE) {
+ other_desc = *(darray_ptr_component_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);
+ ASSERT(cc->medium == other_desc->medium);
+ cc->cc_group_root = other_desc->cc_group_root;
+ cc->enclosure_id = other_desc->enclosure_id;
+ ++enclosures[cc->enclosure_id].cc_count;
+ /* Linked list of componnents */
+ fst = enclosures[cc->enclosure_id].first_component;
+ cc->enclosure_next_component = fst;
+ enclosures[cc->enclosure_id].first_component = cc->cc_id;
+ enclosures[cc->enclosure_id].side_range.first
+ = MMIN(enclosures[cc->enclosure_id].side_range.first, cc->side_range.first);
+ enclosures[cc->enclosure_id].side_range.last
+ = MMAX(enclosures[cc->enclosure_id].side_range.last, cc->side_range.last);
+ enclosures[cc->enclosure_id].side_count += cc->side_count;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+collect_and_link_neighbours
+ (struct senc2d_scene* scn,
+ struct segside* segsides,
+ struct darray_segment_tmp* segments_tmp_array)
+{
+ res_T res = RES_OK;
+ const struct segment_in *segments_in;
+ struct segment_tmp *segments_tmp;
+ const union double2* vertices;
+ /* Array to keep neighbourhood of vertices.
+ * We create a neighbourhood for every single unique vertex,
+ * regardless the fact it is used by a segment
+ * Resize/Push operations on neighbourhood_by_vertex are valid in the
+ * openmp block because each neighbourhood is processes by an unique thread */
+ struct darray_neighbourhood neighbourhood_by_vertex;
+ volatile int exit_for = 0;
+
+ ASSERT(scn && segsides && segments_tmp_array);
+ ASSERT((size_t)scn->nuverts + (size_t)scn->nusegs + 2 <= EDGE_MAX__);
+
+ segments_in = darray_segment_in_cdata_get(&scn->segments_in);
+ segments_tmp = darray_segment_tmp_data_get(segments_tmp_array);
+ vertices = darray_position_cdata_get(&scn->vertices);
+
+ ASSERT(scn->nusegs == darray_segment_tmp_size_get(segments_tmp_array));
+
+ darray_neighbourhood_init(scn->dev->allocator, &neighbourhood_by_vertex);
+ OK2(darray_neighbourhood_resize(&neighbourhood_by_vertex, scn->nuverts),
+ error_);
+
+ printf("\n");
+ #pragma omp parallel
+ {
+ const int thread_count = omp_get_num_threads();
+ const int rank = omp_get_thread_num();
+ /* Array to keep neighbourhood of vertices
+ * Resize/Push operations on neighbourhood_by_vertex are valid in the
+ * openmp block because it is thread local data */
+ vrtx_id_t v;
+ seg_id_t s;
+ /* Loop on segments to collect edges' neighbours.
+ * All threads considering all the vertices and processing some */
+ FOR_EACH(s, 0, scn->nusegs) {
+ unsigned char vv;
+ FOR_EACH(vv, 0, 2) {
+ struct darray_neighbour* neighbourhood;
+ struct neighbour_info* info;
+ const vrtx_id_t vertex = segments_in[s].vertice_id[vv];
+ size_t sz;
+ ASSERT(vertex < scn->nuverts);
+ if(exit_for) continue;
+ /* Process only "my" vertices! */
+ if((int64_t)vertex % thread_count != rank) continue;
+ /* Find neighbourhood */
+ ASSERT(vertex < darray_neighbourhood_size_get(&neighbourhood_by_vertex));
+ neighbourhood =
+ darray_neighbourhood_data_get(&neighbourhood_by_vertex) + vertex;
+ sz = darray_neighbour_size_get(neighbourhood);
+ /* Make room for this neighbour */
+ if(darray_neighbour_capacity(neighbourhood) == sz) {
+ /* 2 seems to be a good guess for initial capacity */
+ size_t new_sz = sz ? sz + 1 : 2;
+ OK(darray_neighbour_reserve(neighbourhood, new_sz));
+ }
+ OK(darray_neighbour_resize(neighbourhood, 1 + sz));
+ /* Add neighbour info to vertex's neighbour list */
+ info = darray_neighbour_data_get(neighbourhood) + sz;
+ info->seg_id = s;
+ info->common_vertex_rank = vv;
+ }
+ }
+ /* No implicit barrier here. */
+
+ /* For each of "my" vertices sort segments sides by rotation angle
+ * and connect neighbours.
+ * All threads considering all the vertices and processing some */
+ FOR_EACH(v, 0, scn->nuverts) {
+ const vrtx_id_t common_vrtx = v;
+ vrtx_id_t other_vrtx;
+ struct darray_neighbour* neighbourhood;
+ side_id_t i, neighbour_count;
+ size_t sz;
+ /* Process only "my" vertices! */
+ if((int64_t)v % thread_count != rank) continue;
+ neighbourhood
+ = darray_neighbourhood_data_get(&neighbourhood_by_vertex) + v;
+ sz = darray_neighbour_size_get(neighbourhood);
+ /* sz can be 0 as a vertex can be unused */
+ if(!sz) continue;
+ ASSERT(sz <= SIDE_MAX__);
+ neighbour_count = (side_id_t)sz;
+ FOR_EACH(i, 0, neighbour_count) {
+ double max_y, disp[2];
+ unsigned char max_y_vrank;
+ struct neighbour_info* neighbour_info
+ = darray_neighbour_data_get(neighbourhood) + i;
+ const struct segment_in* seg_in = segments_in + neighbour_info->seg_id;
+ struct segment_tmp* neighbour = segments_tmp + neighbour_info->seg_id;
+ other_vrtx =
+ seg_in->vertice_id[(neighbour_info->common_vertex_rank + 1) % 2];
+ if(vertices[other_vrtx].pos.y > vertices[common_vrtx].pos.y) {
+ max_y = vertices[other_vrtx].pos.y;
+ max_y_vrank = 1 - neighbour_info->common_vertex_rank;
+ } else {
+ max_y = vertices[common_vrtx].pos.y;
+ max_y_vrank = neighbour_info->common_vertex_rank;
+ }
+ ASSERT(neighbour->max_y <= max_y);
+ neighbour->max_y = max_y;
+ neighbour->max_y_vrtx_rank = max_y_vrank;
+ /* Compute rotation angle around common vertex (in world system) */
+ d2_sub(disp, vertices[other_vrtx].vec, vertices[common_vrtx].vec);
+ ASSERT(disp[0] || disp[1]);
+ neighbour_info->angle = atan2(disp[1], disp[0]);
+ if(neighbour_info->angle < 0) neighbour_info->angle += 2 * PI;
+ /* Due to catastrophic cancelation, -eps+2pi translates to 2pi */
+ ASSERT(0 <= neighbour_info->angle && neighbour_info->angle <= 2 * PI);
+ }
+ /* Sort segments by rotation angle */
+ qsort(darray_neighbour_data_get(neighbourhood), neighbour_count,
+ sizeof(struct neighbour_info), neighbour_cmp);
+ /* Link sides.
+ * Create cycles of sides by neighbourhood around common vertex. */
+ FOR_EACH(i, 0, neighbour_count) {
+ /* Neighbourhood info for current pair of segments */
+ const struct neighbour_info* current
+ = darray_neighbour_cdata_get(neighbourhood) + i;
+ const struct neighbour_info* ccw_neighbour
+ = darray_neighbour_cdata_get(neighbourhood) + (i+1) % neighbour_count;
+ /* Rank of the end of interest in segments */
+ const unsigned char crt_end = current->common_vertex_rank;
+ const unsigned char ccw_end = ccw_neighbour->common_vertex_rank;
+ /* User id of current segments */
+ const seg_id_t crt_id = current->seg_id;
+ const seg_id_t ccw_id = ccw_neighbour->seg_id;
+ /* Facing sides of segments */
+ const enum side_id crt_side = crt_end ? SIDE_BACK : SIDE_FRONT;
+ const enum side_id ccw_side = ccw_end ? SIDE_FRONT : SIDE_BACK;
+ /* Index of sides in segsides */
+ const side_id_t crt_side_idx = SEGIDxSIDE_2_SEGSIDE(crt_id, crt_side);
+ const side_id_t ccw_side_idx = SEGIDxSIDE_2_SEGSIDE(ccw_id, ccw_side);
+ /* Side ptrs */
+ struct segside* const p_crt_side = segsides + crt_side_idx;
+ struct segside* const p_ccw_side = segsides + ccw_side_idx;
+ /* Link sides */
+ p_crt_side->facing_side_id[crt_end] = ccw_side_idx;
+ p_ccw_side->facing_side_id[ccw_end] = crt_side_idx;
+ /* Record media */
+ p_crt_side->medium = segments_in[crt_id].medium[crt_side];
+ p_ccw_side->medium = segments_in[ccw_id].medium[ccw_side];
+ ASSERT(p_crt_side->medium < scn->nmeds);
+ ASSERT(p_ccw_side->medium < scn->nmeds);
+ p_crt_side->list_id = FLAG_LIST_SIDE_LIST;
+ p_ccw_side->list_id = FLAG_LIST_SIDE_LIST;
+ }
+ }
+ /* jump error block */
+ goto after_error;
+error:
+ /* Cannot goto out of openmp block */
+ exit_for = 1;
+after_error:
+ ;
+ }
+error_:
+ darray_neighbourhood_release(&neighbourhood_by_vertex);
+
+ return res;
+}
+
+static res_T
+build_result
+ (struct senc2d_descriptor* desc,
+ const struct darray_ptr_component_descriptor* connex_components,
+ const struct darray_segment_comp* segments_comp_array)
+{
+ res_T res = RES_OK;
+ struct mem_allocator* alloc;
+ struct cc_descriptor* const* cc_descriptors;
+ struct enclosure_data* enclosures;
+ const struct segment_in* segments_in;
+ struct segment_enc* segments_enc;
+ const struct segment_comp* segments_comp;
+ volatile int exit_for = 0;
+
+ ASSERT(desc && connex_components && segments_comp_array);
+
+ alloc = descriptor_get_allocator(desc);
+ ASSERT(darray_ptr_component_descriptor_size_get(connex_components) < COMPONENT_MAX__);
+ cc_descriptors = darray_ptr_component_descriptor_cdata_get(connex_components);
+ enclosures = darray_enclosure_data_get(&desc->enclosures);
+ segments_in = darray_segment_in_cdata_get(&desc->scene->segments_in);
+ segments_comp = darray_segment_comp_cdata_get(segments_comp_array);
+ OK2(darray_segment_enc_resize(&desc->segments_enc, desc->scene->nusegs),
+ error_);
+ segments_enc = darray_segment_enc_data_get(&desc->segments_enc);
+
+ #pragma omp parallel
+ {
+ struct htable_vrtx_id vtable;
+ int64_t sg;
+ int64_t ee;
+
+ /* Build global enclosure information */
+ #pragma omp for
+ for(sg = 0; sg < (int64_t) desc->scene->nusegs; sg++) {
+ seg_id_t s = (seg_id_t) sg;
+ const component_id_t cf_id = segments_comp[s].component[SIDE_FRONT];
+ const component_id_t cb_id = segments_comp[s].component[SIDE_BACK];
+ const struct cc_descriptor* cf = cc_descriptors[cf_id];
+ const struct cc_descriptor* cb = cc_descriptors[cb_id];
+ const enclosure_id_t ef_id = cf->enclosure_id;
+ const enclosure_id_t eb_id = cb->enclosure_id;
+ ASSERT(segments_enc[s].enclosure[SIDE_FRONT] == ENCLOSURE_NULL__);
+ segments_enc[s].enclosure[SIDE_FRONT] = ef_id;
+ ASSERT(segments_enc[s].enclosure[SIDE_BACK] == ENCLOSURE_NULL__);
+ segments_enc[s].enclosure[SIDE_BACK] = eb_id;
+ }
+ /* Implicit barrier here */
+
+ /* Resize/push operations on enclosure's fields are valid in the
+ * openmp block as a given enclosure is processed by a single thread */
+ htable_vrtx_id_init(alloc, &vtable);
+
+ ASSERT(desc->enclosures_count <= ENCLOSURE_MAX__);
+ #pragma omp for schedule(dynamic) nowait
+ for(ee = 0; ee < (int64_t)desc->enclosures_count; ee++) {
+ const enclosure_id_t e = (enclosure_id_t)ee;
+ struct enclosure_data* enc = enclosures + e;
+ const struct cc_descriptor* current = cc_descriptors[enc->first_component];
+ seg_id_t fst_idx = 0;
+ seg_id_t sgd_idx = enc->side_count;
+ seg_id_t s;
+ ASSERT(enc->first_component
+ < darray_ptr_component_descriptor_size_get(connex_components));
+ ASSERT(current->cc_id == enc->first_component);
+
+ if(exit_for) continue;
+ ASSERT(e <= UINT_MAX);
+ enc->header.enclosure_id = (unsigned)e; /* Back to API type */
+ ASSERT(current->enclosure_id == enc->header.enclosure_id);
+ enc->header.is_infinite = (e == 0);
+ enc->header.enclosed_medium
+ = (unsigned)current->medium; /* Back to API type */
+ ASSERT(enc->header.enclosed_medium < desc->scene->nmeds);
+
+ /* Build side and vertex lists. */
+ OK(darray_segment_in_resize(&enc->sides, enc->side_count));
+ /* Size is just a int */
+ OK(darray_vrtx_id_reserve(&enc->vertices, enc->side_count + 1));
+ /* New vertex numbering scheme local to the enclosure */
+ htable_vrtx_id_clear(&vtable);
+ ASSERT(desc->scene->nusegs
+ == darray_segment_in_size_get(&desc->scene->segments_in));
+ /* Put at the end the back-faces of segments that also have their
+ * front-face in the list. */
+ for(s = SEGSIDE_2_SEG(enc->side_range.first);
+ s <= SEGSIDE_2_SEG(enc->side_range.last);
+ s++)
+ {
+ const struct segment_in* seg_in = segments_in + s;
+ struct segment_in* seg;
+ unsigned vertice_id[2];
+ int i;
+ if(segments_enc[s].enclosure[SIDE_FRONT] != e
+ && segments_enc[s].enclosure[SIDE_BACK] != e)
+ continue;
+ ++enc->header.unique_segment_count;
+
+ FOR_EACH(i, 0, 2) {
+ vrtx_id_t* id = htable_vrtx_id_find(&vtable, seg_in->vertice_id + i);
+ if(id) {
+ vertice_id[i] = *id; /* Known vertex */
+ } else {
+ /* Create new association */
+ size_t tmp = htable_vrtx_id_size_get(&vtable);
+ ASSERT(tmp == darray_vrtx_id_size_get(&enc->vertices));
+ ASSERT(tmp < VRTX_MAX__);
+ vertice_id[i] = (vrtx_id_t)tmp;
+ OK(htable_vrtx_id_set(&vtable, seg_in->vertice_id + i,
+ vertice_id + i));
+ OK(darray_vrtx_id_push_back(&enc->vertices, seg_in->vertice_id + i));
+ ++enc->header.vertices_count;
+ }
+ }
+ ASSERT(segments_enc[s].enclosure[SIDE_FRONT] == e
+ || segments_enc[s].enclosure[SIDE_BACK] == e);
+ if(segments_enc[s].enclosure[SIDE_FRONT] == e) {
+ ++enc->header.segment_count;
+ seg = darray_segment_in_data_get(&enc->sides) + fst_idx++;
+
+ FOR_EACH(i, 0, 2) seg->medium[i] = seg_in->medium[i];
+ seg->global_id = seg_in->global_id;
+ FOR_EACH(i, 0, 2) seg->vertice_id[i] = vertice_id[i];
+ }
+ if(segments_enc[s].enclosure[SIDE_BACK] == e) {
+ ++enc->header.segment_count;
+ seg = darray_segment_in_data_get(&enc->sides) +
+ ((segments_enc[s].enclosure[SIDE_FRONT] == e) ? --sgd_idx : fst_idx++);
+
+ FOR_EACH(i, 0, 2) seg->medium[i] = seg_in->medium[1 - i];
+ seg->global_id = seg_in->global_id;
+ FOR_EACH(i, 0, 2) seg->vertice_id[i] = vertice_id[1 - i];
+ }
+ if(fst_idx == sgd_idx) break;
+ }
+ continue;
+ error:
+ /* Cannot goto out of openmp block */
+ exit_for = 1;
+ }
+ htable_vrtx_id_release(&vtable);
+ }
+ OK2(res, error_);
+
+exit:
+ return res;
+error_:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+senc2d_scene_analyze(struct senc2d_scene* scn, struct senc2d_descriptor** out_desc)
+{
+ res_T res = RES_OK;
+ struct senc2d_descriptor* desc = NULL;
+ /* By segment tmp data */
+ struct darray_segment_tmp segments_tmp;
+ char segments_tmp_initialized = 0;
+ /* Array of connex components.
+ * They are refered to by arrays of ids. */
+ struct darray_ptr_component_descriptor connex_components;
+ char connex_components_initialized = 0;
+ /* Segment neighbourhood by edge. */
+ struct darray_neighbourhood neighbourhood_by_edge;
+ char neighbourhood_by_edge_initialized = 0;
+ /* Store by-segment components */
+ struct darray_segment_comp segments_comp;
+ char segments_comp_initialized = 0;
+ /* Array of vertices (segment ends). */
+ struct segside* segsides = NULL;
+ struct s2d_scene_view* s2d_view = NULL;
+
+ if(!scn || !out_desc) return RES_BAD_ARG;
+
+ desc = descriptor_create(scn);
+ if(!desc) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ if(!scn->nusegs) goto exit;
+
+ darray_segment_tmp_init(scn->dev->allocator, &segments_tmp);
+ segments_tmp_initialized = 1;
+
+ OK(darray_segment_tmp_resize(&segments_tmp, scn->nusegs));
+ segsides
+ = MEM_CALLOC(scn->dev->allocator, 2 * scn->nusegs, sizeof(struct segside));
+ if(!segsides) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ /* Step 1: build neighbourhoods */
+ res = collect_and_link_neighbours(scn, segsides, &segments_tmp);
+
+ if(res != RES_OK) {
+ log_err(scn->dev,
+ "%s: could not build neighbourhoods from scene.\n", FUNC_NAME);
+ goto error;
+ }
+
+ darray_ptr_component_descriptor_init(scn->dev->allocator, &connex_components);
+ connex_components_initialized = 1;
+ darray_segment_comp_init(scn->dev->allocator, &segments_comp);
+ segments_comp_initialized = 1;
+ OK(darray_segment_comp_resize(&segments_comp, scn->nusegs));
+
+ /* Step 2: extract segment connex components */
+ res = extract_connex_components(desc, segsides, &connex_components,
+ &segments_tmp, &segments_comp, &s2d_view);
+ if(res != RES_OK) {
+ log_err(scn->dev,
+ "%s: could not extract connex components from scene.\n", FUNC_NAME);
+ goto error;
+ }
+
+ darray_segment_tmp_release(&segments_tmp);
+ segments_tmp_initialized = 0;
+
+ /* Step 3: group components */
+ res = group_connex_components(desc, segsides, &segments_comp,
+ &connex_components, s2d_view);
+ if (s2d_view) S2D(scene_view_ref_put(s2d_view));
+ 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, &segments_comp);
+ if(res != RES_OK) {
+ log_err(scn->dev, "%s: could not build result.\n", FUNC_NAME);
+ goto error;
+ }
+
+ darray_segment_comp_release(&segments_comp);
+ segments_comp_initialized = 0;
+
+exit:
+ if(connex_components_initialized) {
+ size_t c, cc_count =
+ darray_ptr_component_descriptor_size_get(&connex_components);
+ struct cc_descriptor** components =
+ darray_ptr_component_descriptor_data_get(&connex_components);
+ FOR_EACH(c, 0, cc_count) {
+ ptr_component_descriptor_release(scn->dev->allocator, components + c);
+ }
+ darray_ptr_component_descriptor_release(&connex_components);
+ }
+ if(neighbourhood_by_edge_initialized)
+ darray_neighbourhood_release(&neighbourhood_by_edge);
+ if(segments_tmp_initialized) darray_segment_tmp_release(&segments_tmp);
+ if(segments_comp_initialized) darray_segment_comp_release(&segments_comp);
+ if(segsides) MEM_RM(scn->dev->allocator, segsides);
+ if(desc) *out_desc = desc;
+
+ return res;
+error:
+ if(desc) SENC2D(descriptor_ref_put(desc));
+ desc = NULL;
+ goto exit;
+}
diff --git a/src/senc2d_scene_analyze_c.h b/src/senc2d_scene_analyze_c.h
@@ -0,0 +1,169 @@
+/* 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 SENC2D_SCNENE_ANALYZE_C_H
+#define SENC2D_SCNENE_ANALYZE_C_H
+
+#include "senc2d_scene_c.h"
+#include "senc2d_internal_types.h"
+
+#include <rsys/mem_allocator.h>
+#include <rsys/dynamic_array_uchar.h>
+#include <rsys/hash_table.h>
+#include <rsys/double2.h>
+
+
+/* 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_SIDE_LIST = BIT(1),
+ FLAG_LIST_COMPONENT = BIT(2),
+ FLAG_WAITING_STACK = BIT(3),
+ FLAG_ANY_LIST = 0xFF
+};
+
+/* Information kept during the building side groups. */
+struct segside {
+ /* Rank of the segside facing this segside through its vertices */
+ side_id_t facing_side_id[2];
+ /* Id of this segside's medium */
+ medium_id_t medium;
+ /* The list containing the segside; made of enum list_id flags */
+ unsigned char list_id;
+
+ /* Implicit information that we don't need to store:
+ * - segment_id
+ * - side
+ * This is due to the memory layout of the elt darray:
+ * front(seg_0), back(seg_0), front(seg_1), back(seg_1), ... */
+
+#ifndef NDEBUG
+ component_id_t member_of_cc;
+#endif
+};
+
+/* Descriptors for connex component.
+ * Define lists of seg sides starting from a given head.
+ * Also keeps the maximum y info of the component
+ * along with the associated segment and vertex ids */
+#define CC_ID_NONE COMPONENT_MAX__
+#define CC_GROUP_ROOT_NONE COMPONENT_MAX__
+#define CC_GROUP_ROOT_INFINITE (COMPONENT_MAX__-1)
+#define CC_GROUP_ID_NONE COMPONENT_MAX__
+struct cc_descriptor {
+ double max_vrtx[2];
+ double max_y_ny;
+ side_id_t max_y_side_id;
+ vrtx_id_t max_y_vrtx_id;
+ side_id_t side_count;
+ medium_id_t medium;
+ /* Used when grouping components to form enclosures */
+ component_id_t cc_id;
+ component_id_t cc_group_root;
+ enclosure_id_t enclosure_id;
+ /* To create by-medium linked lists of componnents */
+ component_id_t enclosure_next_component;
+ /* Range of sides member of this component */
+ struct side_range side_range;
+
+};
+extern const struct cc_descriptor CC_DESCRIPTOR_NULL;
+
+static FINLINE void
+cc_descriptor_init
+ (struct mem_allocator* alloc,
+ struct cc_descriptor* data)
+{
+ ASSERT(data);
+ (void)alloc;
+ *data = CC_DESCRIPTOR_NULL;
+}
+
+static FINLINE void
+ptr_component_descriptor_init
+ (struct mem_allocator* alloc,
+ struct cc_descriptor** data)
+{
+ (void)alloc;
+ ASSERT(data);
+ *data = NULL;
+}
+static FINLINE void
+ptr_component_descriptor_release
+ (struct mem_allocator* allocator,
+ struct cc_descriptor** data)
+{
+ ASSERT(allocator && data);
+ MEM_RM(allocator, *data);
+}
+
+#define DARRAY_NAME ptr_component_descriptor
+#define DARRAY_DATA struct cc_descriptor*
+#define DARRAY_FUNCTOR_INIT ptr_component_descriptor_init
+#include <rsys/dynamic_array.h>
+
+/* Segment information.
+ * Depending on lifespan, information is kept in different places:
+ * - segment_in for user provided information (kept in scene)
+ * - segment_tmp for tmp information (kept until segment_comp is ready)
+ * - segment_comp for information describing components (kept until
+ * segment_enc is ready)
+ * - segment_enc for information describing enclosures (kept in
+ * senc2d_descriptor). */
+struct segment_tmp {
+ /* tmp data used to find the +Y-most vertex of components */
+ unsigned char max_y_vrtx_rank;
+ double max_y;
+};
+
+#ifndef NDEBUG
+static FINLINE void
+segment_tmp_init(struct mem_allocator* alloc, struct segment_tmp* seg) {
+ (void)alloc;
+ ASSERT(seg);
+ seg->max_y_vrtx_rank = UCHAR_MAX;
+ seg->max_y = -DBL_MAX;
+}
+#define DARRAY_FUNCTOR_INIT segment_tmp_init
+#endif
+
+#define DARRAY_NAME segment_tmp
+#define DARRAY_DATA struct segment_tmp
+#include <rsys/dynamic_array.h>
+
+struct neighbour_info {
+ double angle;
+ seg_id_t seg_id;
+ /* Rank of the vertex in the segment (in [0 1]) */
+ unsigned char common_vertex_rank;
+};
+#define DARRAY_NAME neighbour
+#define DARRAY_DATA struct neighbour_info
+#include <rsys/dynamic_array.h>
+
+#define DARRAY_NAME neighbourhood
+#define DARRAY_DATA struct darray_neighbour
+#define DARRAY_FUNCTOR_INIT darray_neighbour_init
+#define DARRAY_FUNCTOR_COPY darray_neighbour_copy
+#define DARRAY_FUNCTOR_RELEASE darray_neighbour_release
+#define DARRAY_FUNCTOR_COPY_AND_RELEASE darray_neighbour_copy_and_release
+#include <rsys/dynamic_array.h>
+
+#endif /* SENC2D_SCNENE_ANALYZE_C_H */
diff --git a/src/senc2d_scene_c.h b/src/senc2d_scene_c.h
@@ -0,0 +1,185 @@
+/* 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 SENC2D_SCNENE_C_H
+#define SENC2D_SCNENE_C_H
+
+#include "senc2d_internal_types.h"
+
+#include <rsys/ref_count.h>
+#include <rsys/dynamic_array.h>
+#include <rsys/hash_table.h>
+
+struct mem_allocator;
+
+#define HTABLE_NAME vrtx_id
+#define HTABLE_KEY vrtx_id_t
+#define HTABLE_DATA vrtx_id_t
+#include <rsys/hash_table.h>
+
+union double2 {
+ struct {
+ double x, y;
+ } pos;
+ double vec[2];
+};
+#define DARRAY_NAME position
+#define DARRAY_DATA union double2
+#include <rsys/dynamic_array.h>
+/* Segment information.
+ * Depending on lifespan, information is kept in different places:
+ * - segment_in for user provided information (kept in scene)
+ * - segment_comp for information describing components (kept in senc2d_descriptor)
+ * - segment_cmp for tmp information (kept until segment_comp is ready) */
+struct segment_in {
+ /* Ids of the segment's vertices */
+ vrtx_id_t vertice_id[2];
+ /* Ids of this segment's media */
+ medium_id_t medium[2];
+ /* Segment index in user world (that is regardless of deduplication). */
+ unsigned global_id;
+};
+
+#ifndef NDEBUG
+static FINLINE void
+segment_in_init(struct mem_allocator* alloc, struct segment_in* seg) {
+ int i;
+ (void)alloc;
+ ASSERT(seg);
+ FOR_EACH(i, 0, 2) seg->vertice_id[i] = VRTX_NULL__;
+ FOR_EACH(i, 0, 2) seg->medium[i] = MEDIUM_NULL__;
+ seg->global_id = 0;
+}
+#define DARRAY_FUNCTOR_INIT segment_in_init
+#endif
+
+#define DARRAY_NAME segment_in
+#define DARRAY_DATA struct segment_in
+#include <rsys/dynamic_array.h>
+
+static FINLINE void
+segment_in_flip(struct segment_in* seg) {
+ vrtx_id_t v;
+ medium_id_t m;
+ ASSERT(seg);
+ v = seg->vertice_id[1];
+ seg->vertice_id[1] = seg->vertice_id[2];
+ seg->vertice_id[2] = v;
+ m = seg->medium[0];
+ seg->medium[0] = seg->medium[1];
+ seg->medium[1] = m;
+}
+
+static FINLINE int
+vrtx_eq(const union double2* v1, const union double2* v2)
+{
+ ASSERT(v1 && v2);
+ return (v1->pos.x == v2->pos.x && v1->pos.y == v2->pos.y);
+}
+
+#define HTABLE_NAME vrtx
+#define HTABLE_KEY union double2
+#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_id2 {
+ struct {
+ vrtx_id_t v0, v1;
+ } pos;
+ vrtx_id_t vec[2];
+};
+
+static FINLINE char /* Return 1 if reversed */
+seg_make_key(union vrtx_id2* k, const vrtx_id_t v[2])
+{
+ ASSERT(v);
+ ASSERT(v[0] != v[1]);
+ if (v[0] < v[1]) {
+ k->vec[0] = v[0];
+ k->vec[1] = v[1];
+ return 0;
+ }
+ else {
+ k->vec[0] = v[1];
+ k->vec[1] = v[0];
+ return 1;
+ }
+}
+
+static FINLINE int
+seg_key_eq(const union vrtx_id2* k1, const union vrtx_id2* k2)
+{
+ ASSERT(k1 && k2);
+ ASSERT(k1->vec[0] < k1->vec[1]);
+ ASSERT(k2->vec[0] < k2->vec[1]);
+ return (k1->vec[0] == k2->vec[0]) && (k1->vec[1] == k2->vec[1]);
+}
+
+#define HTABLE_NAME seg
+#define HTABLE_KEY union vrtx_id2
+#define HTABLE_DATA seg_id_t
+#define HTABLE_KEY_FUNCTOR_EQ seg_key_eq
+#include <rsys/hash_table.h>
+
+struct side_range {
+ side_id_t first, last;
+};
+static FINLINE void
+side_range_init(struct mem_allocator* alloc, struct side_range* data)
+{
+ ASSERT(data);
+ (void)alloc;
+ data->first = SIDE_NULL__;
+ data->last = 0;
+}
+#define DARRAY_NAME side_range
+#define DARRAY_DATA struct side_range
+#define DARRAY_FUNCTOR_INIT side_range_init
+#include <rsys/dynamic_array.h>
+
+struct senc2d_scene {
+ /* Segment information as given by user; no duplicates here */
+ struct darray_segment_in segments_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;
+
+ /* Htables used to detect duplicate segments. */
+ /* Keep each unique segment; no duplicates here. */
+ struct htable_seg unique_segments;
+
+ /* Keep sizes */
+ unsigned ngeoms; /* Not used yet (just counted). */
+ seg_id_t nsegs, nusegs; /* Segment count, unique segment count */
+ vrtx_id_t nverts, nuverts; /* Vrtx count, unique vrtx count */
+ medium_id_t nmeds;
+ struct darray_side_range media_use;
+
+ ref_T ref;
+ struct senc2d_device* dev;
+};
+
+#endif /* SENC2D_SCNENE_C_H */
diff --git a/src/test_senc2d_descriptor.c b/src/test_senc2d_descriptor.c
@@ -0,0 +1,158 @@
+/* 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 "senc2d.h"
+#include "test_senc2d_utils.h"
+
+#include <rsys/float2.h>
+#include <rsys/double2.h>
+
+#include <star/s2d.h>
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc2d_device* dev = NULL;
+ struct senc2d_scene* scn = NULL;
+ struct senc2d_descriptor* desc = NULL;
+ struct senc2d_enclosure* enc = NULL;
+ struct context ctx;
+ unsigned count;
+ unsigned indices[2];
+ double coord[2];
+ unsigned media[2];
+ unsigned enclosures[2];
+ (void)argc, (void)argv;
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK);
+ CHK(senc2d_device_create(NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev)
+ == RES_OK);
+
+ CHK(senc2d_scene_create(dev, 2, &scn) == RES_OK);
+
+ /* A 2D square */
+ ctx.positions = square_vertices;
+ ctx.indices = square_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d2(ctx.offset, 0, 0);
+ ctx.front_media = medium0;
+ ctx.back_media = medium1;
+
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_OK);
+
+ CHK(senc2d_descriptor_ref_get(desc) == RES_OK);
+ CHK(senc2d_descriptor_ref_get(NULL) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_ref_put(NULL) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_ref_put(desc) == RES_OK);
+
+ CHK(senc2d_descriptor_get_enclosure_count(NULL, &count) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_enclosure_count(desc, NULL) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_enclosure_count(NULL, NULL) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_enclosure_count(desc, &count) == RES_OK);
+
+ CHK(count == 2);
+
+ CHK(senc2d_descriptor_get_enclosure(NULL, 0, &enc) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_enclosure(desc, count, &enc) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_enclosure(desc, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_enclosure(NULL, count, &enc) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_enclosure(NULL, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_enclosure(desc, count, NULL) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_enclosure(NULL, count, NULL) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_enclosure(desc, 0, &enc) == RES_OK);
+
+ CHK(senc2d_descriptor_get_global_vertices_count(NULL, &count) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_vertices_count(desc, NULL) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_vertices_count(desc, &count) == RES_OK);
+ CHK(count == square_nvertices);
+
+ CHK(senc2d_descriptor_get_global_segments_count(NULL, &count) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_segments_count(desc, NULL) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_segments_count(desc, &count) == RES_OK);
+ CHK(count == square_nsegments);
+
+ CHK(senc2d_descriptor_get_global_segment(NULL, 0, indices) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_segment(NULL, square_nsegments, indices)
+ == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_segment(desc, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_segment(desc, 0, indices) == RES_OK);
+ CHK(indices[0] == square_indices[0] && indices[1] == square_indices[1]);
+
+ CHK(senc2d_descriptor_get_global_vertex(NULL, 0, coord) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_vertex(NULL, square_nvertices, coord)
+ == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_vertex(desc, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_vertex(desc, 0, coord) == RES_OK);
+ CHK(coord[0] == square_vertices[0] && coord[1] == square_vertices[1]);
+
+ CHK(senc2d_descriptor_get_global_segment_media(NULL, 0, media)
+ == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_segment_media(NULL, square_nvertices, media)
+ == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_segment_media(desc, 0, NULL)
+ == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_segment_media(desc, 0, media) == RES_OK);
+ CHK(media[0] == ctx.front_media[0] && media[1] == ctx.back_media[1]);
+
+ CHK(senc2d_descriptor_get_global_segment_enclosures(
+ NULL, 0, enclosures) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_segment_enclosures(
+ NULL, square_nvertices, enclosures) == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_segment_enclosures(desc, 0, NULL)
+ == RES_BAD_ARG);
+ CHK(senc2d_descriptor_get_global_segment_enclosures(desc, 0, enclosures)
+ == RES_OK);
+ CHK(enclosures[0] == 0 && enclosures[1] == 1);
+
+ /* Add valid duplicate geometry */
+ CHK(senc2d_descriptor_ref_put(desc) == RES_OK);
+ desc = NULL;
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media,
+ NULL, square_nvertices, get_position, &ctx) == RES_OK);
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_OK);
+
+ CHK(senc2d_descriptor_get_global_vertices_count(desc, &count) == RES_OK);
+ /* Duplicate vertices have been replaced */
+ CHK(count == square_nvertices);
+
+ CHK(senc2d_descriptor_get_global_segments_count(desc, &count) == RES_OK);
+ /* Duplicate segments have been replaced */
+ CHK(count == square_nsegments);
+
+ /* Add invalid duplicate geometry */
+ CHK(senc2d_descriptor_ref_put(desc) == RES_OK);
+ desc = NULL;
+ ctx.front_media = medium1;
+ ctx.back_media = medium0;
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_BAD_ARG);
+
+ CHK(senc2d_scene_ref_put(scn) == RES_OK);
+ CHK(senc2d_device_ref_put(dev) == RES_OK);
+ if(desc) CHK(senc2d_descriptor_ref_put(desc) == RES_OK);
+ CHK(senc2d_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_senc2d_device.c b/src/test_senc2d_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 "senc2d.h"
+#include "test_senc2d_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 senc2d_device* dev;
+ (void)argc, (void)argv;
+
+ CHK(senc2d_device_create(NULL, NULL, 0, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_device_create(NULL, NULL, 0, 0, &dev) == RES_BAD_ARG);
+ CHK(senc2d_device_create(NULL, NULL, 1, 0, &dev) == RES_OK);
+ CHK(senc2d_device_ref_get(NULL) == RES_BAD_ARG);
+ CHK(senc2d_device_ref_get(dev) == RES_OK);
+ CHK(senc2d_device_ref_put(NULL) == RES_BAD_ARG);
+ CHK(senc2d_device_ref_put(dev) == RES_OK);
+ CHK(senc2d_device_ref_put(dev) == RES_OK);
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)
+ == RES_OK);
+
+ CHK(MEM_ALLOCATED_SIZE(&allocator) == 0);
+ CHK(senc2d_device_create(NULL, &allocator, 1, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_device_create(NULL, &allocator, 1, 0, &dev) == RES_OK);
+ CHK(senc2d_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(senc2d_device_create(&logger, NULL, 1, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_device_create(&logger, NULL, 1, 0, &dev) == RES_OK);
+ CHK(senc2d_device_ref_put(dev) == RES_OK);
+
+ CHK(senc2d_device_create(&logger, &allocator, 1, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_device_create(&logger, &allocator, 1, 0, &dev) == RES_OK);
+ CHK(senc2d_device_ref_put(dev) == RES_OK);
+
+ CHK(senc2d_device_create
+ (&logger, &allocator, SENC2D_NTHREADS_DEFAULT, 0, &dev) == RES_OK);
+ CHK(senc2d_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_senc2d_enclosure.c b/src/test_senc2d_enclosure.c
@@ -0,0 +1,247 @@
+/* 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 "senc2d.h"
+#include "senc2d_s2d_wrapper.h"
+#include "test_senc2d_utils.h"
+
+#include <rsys/float2.h>
+#include <rsys/double2.h>
+
+#include <star/s2d.h>
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc2d_descriptor* desc = NULL;
+ struct senc2d_device* dev = NULL;
+ struct senc2d_scene* scn = NULL;
+ struct senc2d_enclosure* enclosures[2] = { NULL, NULL };
+ struct senc2d_enclosure* enclosure;
+ const struct enclosure2d_header* header;
+ struct s2d_device* s2d = NULL;
+ struct s2d_scene* s2d_scn = NULL;
+ struct s2d_shape* s2d_shp = NULL;
+ struct s2d_vertex_data s2d_attribs;
+ unsigned indices[2][2];
+ unsigned medium[2];
+ unsigned gid;
+ double vrtx[2];
+ struct context ctx;
+ unsigned i, n, t, ecount;
+ (void)argc, (void)argv;
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK);
+ CHK(senc2d_device_create(NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev)
+ == RES_OK);
+
+ CHK(senc2d_scene_create(dev, 2, &scn) == RES_OK);
+
+ s2d_attribs.type = S2D_FLOAT2;
+ s2d_attribs.usage = S2D_POSITION;
+ s2d_attribs.get = senc2d_enclosure_get_vertex__;
+
+ CHK(s2d_device_create(NULL, &allocator, 0, &s2d) == RES_OK);
+
+ CHK(s2d_scene_create(s2d, &s2d_scn) == RES_OK);
+
+ /* A 2D square.
+ * 2 enclosures (inside, outside) sharing the same segments,
+ * but opposite sides */
+ ctx.positions = square_vertices;
+ ctx.indices = square_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d2(ctx.offset, 0, 0);
+ ctx.front_media = medium0;
+ ctx.back_media = medium1;
+
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media,
+ NULL, square_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_OK);
+
+ CHK(senc2d_descriptor_get_enclosure_count(desc, &ecount) == RES_OK);
+ CHK(ecount == 2);
+
+ CHK(senc2d_descriptor_get_enclosure(desc, 0, &enclosure) == RES_OK);
+ CHK(senc2d_enclosure_ref_get(NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_ref_get(enclosure) == RES_OK);
+ CHK(senc2d_enclosure_ref_put(NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_ref_put(enclosure) == RES_OK);
+
+ CHK(senc2d_enclosure_get_segment(NULL, 0, indices[0]) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment(enclosure, square_nsegments, indices[0])
+ == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment(enclosure, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment(NULL, square_nsegments, indices[0])
+ == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment(NULL, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment(enclosure, square_nsegments, NULL)
+ == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment(NULL, square_nsegments, NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment(enclosure, 0, indices[0]) == RES_OK);
+
+ CHK(senc2d_enclosure_get_vertex(NULL, 0, vrtx) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_vertex(enclosure, square_nvertices, vrtx)
+ == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_vertex(enclosure, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_vertex(NULL, square_nvertices, vrtx) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_vertex(NULL, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_vertex(enclosure, square_nvertices, NULL)
+ == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_vertex(NULL, square_nvertices, NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_vertex(enclosure, 0, vrtx) == RES_OK);
+
+ CHK(senc2d_enclosure_get_segment_media(NULL, 0, medium) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment_media(enclosure, square_nsegments, medium)
+ == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment_media(enclosure, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment_media(NULL, square_nsegments, medium)
+ == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment_media(NULL, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment_media(enclosure, square_nsegments, NULL)
+ == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment_media(NULL, square_nsegments, NULL)
+ == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment_media(enclosure, 0, medium) == RES_OK);
+
+ CHK(senc2d_enclosure_get_segment_global_id(NULL, 0, &gid) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment_global_id(enclosure, square_nsegments, &gid)
+ == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment_global_id(enclosure, 0, NULL)
+ == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment_global_id(NULL, square_nsegments, &gid)
+ == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment_global_id(NULL, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment_global_id(enclosure, square_nsegments, NULL)
+ == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment_global_id(NULL, square_nsegments, NULL)
+ == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_segment_global_id(enclosure, 0, &gid) == RES_OK);
+
+ CHK(senc2d_enclosure_ref_put(enclosure) == RES_OK);
+
+ FOR_EACH(i, 0, ecount) {
+ CHK(senc2d_descriptor_get_enclosure(desc, i, &enclosure) == RES_OK);
+
+ CHK(senc2d_enclosure_get_header(NULL, &header) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_header(enclosure, NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_header(NULL, NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK);
+
+ CHK(header->enclosure_id == i);
+ CHK(header->enclosed_medium == (i == 0 ? 0U : 1U));
+ CHK(header->segment_count == square_nsegments);
+ CHK(header->unique_segment_count == square_nsegments);
+ CHK(header->vertices_count == square_nvertices);
+ CHK(header->is_infinite == (i == 0));
+
+ FOR_EACH(t, 0, header->segment_count) {
+ CHK(senc2d_enclosure_get_segment_global_id(enclosure, t, &gid) == RES_OK);
+ CHK(gid == t);
+ }
+
+ CHK(senc2d_enclosure_ref_put(enclosure) == RES_OK);
+ }
+
+ FOR_EACH(i, 0, 2)
+ CHK(senc2d_descriptor_get_enclosure(desc, i, enclosures + i) == RES_OK);
+ FOR_EACH(n, 0, square_nsegments) {
+ /* Read same segments in both enclosures */
+ FOR_EACH(i, 0, 2)
+ CHK(senc2d_enclosure_get_segment(enclosures[i], n, indices[i]) == RES_OK);
+ /* Same segments, opposite sides */
+ CHK(indices[0][0] == indices[1][1]);
+ CHK(indices[0][1] == indices[1][0]);
+ }
+ FOR_EACH(i, 0, 2)
+ CHK(senc2d_enclosure_ref_put(enclosures[i]) == RES_OK);
+
+ CHK(senc2d_scene_ref_put(scn) == RES_OK);
+ CHK(senc2d_descriptor_ref_put(desc) == RES_OK);
+
+ /* Same 2D square, but with a hole (incomplete).
+ * 1 single enclosure including both sides of segments */
+
+ CHK(senc2d_scene_create(dev, 2, &scn) == RES_OK);
+
+ ctx.positions = square_vertices;
+ ctx.indices = square_indices;
+ ctx.scale = 1;
+ d2(ctx.offset, 0, 0);
+ ctx.front_media = medium0;
+ ctx.back_media = medium0;
+
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments - 1, get_indices, get_media,
+ NULL, square_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_OK);
+
+ CHK(senc2d_descriptor_get_enclosure_count(desc, &ecount) == RES_OK);
+ CHK(ecount == 1);
+
+ dump_enclosure(desc, 0, "test2d_enclosure_hole.obj");
+
+ CHK(senc2d_descriptor_get_enclosure(desc, 0, &enclosure) == RES_OK);
+
+ CHK(senc2d_enclosure_get_header(NULL, &header) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_header(enclosure, NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_header(NULL, NULL) == RES_BAD_ARG);
+ CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK);
+
+ CHK(header->enclosure_id == 0);
+ CHK(header->enclosed_medium == 0);
+ CHK(header->segment_count == 2 * header->unique_segment_count);
+ CHK(header->unique_segment_count == square_nsegments - 1);
+ CHK(header->vertices_count == square_nvertices);
+ CHK(header->is_infinite == 1);
+
+ FOR_EACH(t, 0, header->unique_segment_count) {
+ /* The first unique_segment_count segments of an enclosure
+ * are unique segments */
+ CHK(senc2d_enclosure_get_segment_global_id(enclosure, t, &gid) == RES_OK);
+ CHK(gid == t);
+ }
+
+ FOR_EACH(n, 0, header->unique_segment_count) {
+ /* Put geometry in a 2D view */
+ CHK(s2d_shape_create_line_segments(s2d, &s2d_shp) == RES_OK);
+
+ CHK(s2d_line_segments_setup_indexed_vertices(s2d_shp, header->segment_count,
+ senc2d_enclosure_get_segment__, header->vertices_count, &s2d_attribs,
+ 1, enclosure)
+ == RES_OK);
+
+ CHK(s2d_scene_attach_shape(s2d_scn, s2d_shp) == RES_OK);
+ S2D(shape_ref_put(s2d_shp));
+ }
+
+ S2D(device_ref_put(s2d));
+ S2D(scene_ref_put(s2d_scn));
+
+ SENC2D(scene_ref_put(scn));
+ SENC2D(device_ref_put(dev));
+ SENC2D(descriptor_ref_put(desc));
+ SENC2D(enclosure_ref_put(enclosure));
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+
+ return 0;
+}
diff --git a/src/test_senc2d_many_enclosures.c b/src/test_senc2d_many_enclosures.c
@@ -0,0 +1,150 @@
+/* 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 "senc2d.h"
+#include "test_senc2d_utils.h"
+
+#include <rsys/double2.h>
+#include <rsys/stretchy_array.h>
+#include <rsys/clock_time.h>
+
+#include <limits.h>
+
+static void
+get_ctx_indices(const unsigned iseg, unsigned ids[2], void* context)
+{
+ struct context* ctx = context;
+ (void) ctx;
+ ASSERT(ids && ctx);
+ ASSERT(2 * iseg + 1 < sa_size(ctx->indices));
+ get_indices(iseg, ids, context);
+}
+
+static void
+get_ctx_position(const unsigned ivert, double pos[2], void* context)
+{
+ struct context* ctx = context;
+ (void) ctx;
+ ASSERT(pos && ctx);
+ ASSERT(2 * ivert + 1 < sa_size(ctx->positions));
+ get_position(ivert, pos, context);
+}
+
+static void
+get_ctx_media(const unsigned iseg, unsigned medium[2], void* context)
+{
+ struct context* ctx = context;
+ (void) iseg;
+ ASSERT(medium && ctx);
+ medium[ctx->reverse_med ? 1 : 0] = *ctx->front_media;
+ medium[ctx->reverse_med ? 0 : 1] = *ctx->back_media;
+}
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc2d_descriptor* desc = NULL;
+ struct senc2d_device* dev = NULL;
+ struct senc2d_scene* scn = NULL;
+ struct context ctx;
+ unsigned m_in, m_out;
+ unsigned count;
+ unsigned circ_seg_count, circ_vrtx_count, e;
+ int i, j, k;
+ char dump[64];
+ struct time t0, t1;
+ (void)argc, (void)argv;
+
+ CHK(mem_init_regular_allocator(&allocator) == RES_OK);
+ CHK(senc2d_device_create(NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev)
+ == RES_OK);
+
+#define NB_CIRC_1 64
+ /* 64^3 = 262144 circles */
+#define NB_CIRC (NB_CIRC_1 * NB_CIRC_1 * NB_CIRC_1)
+ /* Create the scene */
+ CHK(senc2d_scene_create(dev, NB_CIRC_1 + 1, &scn) == RES_OK);
+
+ ctx.positions = NULL;
+ ctx.indices = NULL;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ ctx.front_media = &m_in;
+ ctx.back_media = &m_out;
+ /* A 16 segments circle template */
+ create_circle(1, 16, &ctx);
+ ASSERT(sa_size(ctx.positions) % 2 == 0
+ && sa_size(ctx.positions) / 2 < UINT_MAX);
+ ASSERT(sa_size(ctx.indices) % 2 == 0
+ && sa_size(ctx.indices) / 2 < UINT_MAX);
+ circ_seg_count = (unsigned) sa_size(ctx.indices) / 2;
+ circ_vrtx_count = (unsigned) sa_size(ctx.positions) / 2;
+ FOR_EACH(i, 0, NB_CIRC_1) {
+ double center_x = 2 * (1 + NB_CIRC_1) * (i - NB_CIRC_1 / 2);
+ FOR_EACH(j, 0, NB_CIRC_1) {
+ FOR_EACH(k, 0, NB_CIRC_1) {
+ double center_y = 2 * (1 + NB_CIRC_1) * (j - NB_CIRC_1 / 2);
+ m_in = (unsigned)k;
+ m_out = (unsigned)(k + 1);
+ ctx.scale = k + 1;
+ d2(ctx.offset, center_x, center_y);
+ CHK(senc2d_scene_add_geometry(scn, circ_seg_count, get_ctx_indices,
+ get_ctx_media, NULL, circ_vrtx_count, get_ctx_position, &ctx)
+ == RES_OK);
+ }
+ }
+ }
+ circle_release(&ctx);
+
+ time_current(&t0);
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_OK);
+ time_sub(&t0, time_current(&t1), &t0);
+ time_dump(&t0, TIME_MSEC | TIME_SEC | TIME_MIN, NULL, dump, sizeof(dump));
+ printf("Scene analyzed in: %s\n", dump);
+
+ /* dump_global(desc, "test2d_many_enclosures.obj"); */
+
+ CHK(senc2d_descriptor_get_global_vertices_count(desc, &count) == RES_OK);
+ CHK(count == NB_CIRC * circ_vrtx_count);
+ CHK(senc2d_descriptor_get_global_segments_count(desc, &count) == RES_OK);
+ CHK(count == NB_CIRC * circ_seg_count);
+
+ CHK(senc2d_descriptor_get_enclosure_count(desc, &count) == RES_OK);
+ CHK(count == 1 + NB_CIRC);
+
+ FOR_EACH(e, 0, count) {
+ struct senc2d_enclosure* enclosure;
+ const struct enclosure2d_header* header;
+ CHK(senc2d_descriptor_get_enclosure(desc, e, &enclosure) == RES_OK);
+ CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK);
+ CHK(header->segment_count ==
+ (e == 0 /* Outermost enclosure: NB_CIRC_1*NB_CIRC_1 circles */
+ ? NB_CIRC_1 * NB_CIRC_1 * circ_seg_count
+ : (header->enclosed_medium == 0
+ ? circ_seg_count /* Innermost enclosures: 1 circle */
+ : 2 * circ_seg_count))); /* Other enclosures: 2 circles */
+ CHK(senc2d_enclosure_ref_put(enclosure) == RES_OK);
+ }
+
+ CHK(senc2d_scene_ref_put(scn) == RES_OK);
+ CHK(senc2d_device_ref_put(dev) == RES_OK);
+ CHK(senc2d_descriptor_ref_put(desc) == RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_regular_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+ return 0;
+}
diff --git a/src/test_senc2d_many_segments.c b/src/test_senc2d_many_segments.c
@@ -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/>. */
+
+#include "senc2d.h"
+#include "test_senc2d_utils.h"
+
+#include <rsys/double2.h>
+#include <rsys/stretchy_array.h>
+#include <rsys/clock_time.h>
+
+#include <limits.h>
+
+static void
+get_ctx_indices(const unsigned iseg, unsigned ids[2], void* context)
+{
+ struct context* ctx = context;
+ (void)ctx;
+ ASSERT(ids && ctx);
+ ASSERT(2*iseg+1 < sa_size(ctx->indices));
+ get_indices(iseg, ids, context);
+}
+
+static void
+get_ctx_position(const unsigned ivert, double pos[2], void* context)
+{
+ struct context* ctx = context;
+ (void)ctx;
+ ASSERT(pos && ctx);
+ ASSERT(2*ivert+1 < sa_size(ctx->positions));
+ get_position(ivert, pos, context);
+}
+
+static void
+get_ctx_media(const unsigned iseg, unsigned medium[2], void* context)
+{
+ struct context* ctx = context;
+ (void)iseg;
+ ASSERT(medium && ctx);
+ medium[ctx->reverse_med ? 1 : 0] = *ctx->front_media;
+ medium[ctx->reverse_med ? 0 : 1] = *ctx->back_media;
+}
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc2d_descriptor* desc = NULL;
+ struct senc2d_device* dev = NULL;
+ struct senc2d_scene* scn = NULL;
+ struct context ctx;
+ unsigned m0 = 0, m1;
+ unsigned count;
+ unsigned circ_seg_count, circ_vrtx_count, i;
+ char dump[64];
+ struct time t0, t1;
+ (void)argc, (void)argv;
+
+ CHK(mem_init_regular_allocator(&allocator) == RES_OK);
+ CHK(senc2d_device_create (NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev)
+ == RES_OK);
+
+#define NB_CIRC 4
+ /* Create the scene */
+ CHK(senc2d_scene_create(dev, NB_CIRC+1, &scn) == RES_OK);
+
+ ctx.positions = NULL;
+ ctx.indices = NULL;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ ctx.back_media = &m0;
+ ctx.front_media = &m1;
+ /* A 1,048,576 segments circle template */
+ create_circle(1, 1048576, &ctx);
+ ASSERT(sa_size(ctx.positions) % 2 == 0
+ && sa_size(ctx.positions) / 2 < UINT_MAX);
+ ASSERT(sa_size(ctx.indices) % 2 == 0
+ && sa_size(ctx.indices) / 2 < UINT_MAX);
+ circ_seg_count = (unsigned)sa_size(ctx.indices) / 2;
+ circ_vrtx_count = (unsigned)sa_size(ctx.positions) / 2;
+ FOR_EACH(i, 0, NB_CIRC) {
+ m1 = i;
+ d2(ctx.offset, 0, i * 10);
+ CHK(senc2d_scene_add_geometry(scn, circ_seg_count, get_ctx_indices,
+ get_ctx_media, NULL, circ_vrtx_count, get_ctx_position, &ctx)
+ == RES_OK);
+ }
+ circle_release(&ctx);
+
+ time_current(&t0);
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_OK);
+ time_sub(&t0, time_current(&t1), &t0);
+ time_dump(&t0, TIME_MSEC | TIME_SEC | TIME_MIN, NULL, dump, sizeof(dump));
+ printf("Scene analyzed in: %s\n", dump);
+
+ /* dump_global(desc, "test2d_many_segments.obj"); */
+
+ CHK(senc2d_descriptor_get_global_vertices_count(desc, &count) == RES_OK);
+ CHK(count == NB_CIRC * circ_vrtx_count);
+ CHK(senc2d_descriptor_get_global_segments_count(desc, &count) == RES_OK);
+ CHK(count == NB_CIRC * circ_seg_count);
+
+ CHK(senc2d_descriptor_get_enclosure_count(desc, &count) == RES_OK);
+ CHK(count == 1 + NB_CIRC);
+ FOR_EACH(i, 0, count) {
+ struct senc2d_enclosure* enclosure;
+ const struct enclosure2d_header* header;
+ CHK(senc2d_descriptor_get_enclosure(desc, i, &enclosure) == RES_OK);
+ CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK);
+ CHK(header->segment_count ==
+ i ? circ_seg_count : NB_CIRC * circ_seg_count);
+ CHK(senc2d_enclosure_ref_put(enclosure) == RES_OK);
+ }
+
+ CHK(senc2d_scene_ref_put(scn) == RES_OK);
+ CHK(senc2d_device_ref_put(dev) == RES_OK);
+ CHK(senc2d_descriptor_ref_put(desc) == RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_regular_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+ return 0;
+}
diff --git a/src/test_senc2d_sample_enclosure.c b/src/test_senc2d_sample_enclosure.c
@@ -0,0 +1,138 @@
+/* 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 "senc2d.h"
+#include "senc2d_s2d_wrapper.h"
+#include "test_senc2d_utils.h"
+
+#include <rsys/float2.h>
+#include <rsys/double2.h>
+
+#include <star/s2d.h>
+#include <star/ssp.h>
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc2d_descriptor* desc = NULL;
+ struct senc2d_device* dev = NULL;
+ struct senc2d_scene* scn = NULL;
+ struct senc2d_enclosure* enclosure = NULL;
+ const struct enclosure2d_header* header = NULL;
+ struct s2d_device* s2d = NULL;
+ struct s2d_scene* s2d_scn = NULL;
+ struct s2d_scene_view* s2d_view = NULL;
+ struct s2d_shape* s2d_shp = NULL;
+ struct s2d_primitive prim;
+ struct s2d_vertex_data vrtx_get;
+ struct ssp_rng* rng;
+ struct context ctx;
+ int i;
+ float s;
+ (void)argc, (void)argv;
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK);
+ CHK(senc2d_device_create(NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev)
+ == RES_OK);
+
+ CHK(senc2d_scene_create(dev, 2, &scn) == RES_OK);
+
+ vrtx_get.type = S2D_FLOAT2;
+ vrtx_get.usage = S2D_POSITION;
+ vrtx_get.get = senc2d_enclosure_get_vertex__;
+
+ S2D(device_create(NULL, &allocator, 0, &s2d));
+
+ S2D(scene_create(s2d, &s2d_scn));
+
+ /* A 2D square, but with a hole (incomplete).
+ * 1 single enclosure including both sides of segments */
+
+ ctx.positions = square_vertices;
+ ctx.indices = square_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d2(ctx.offset, 0, 0);
+ ctx.front_media = medium0;
+ ctx.back_media = medium0;
+
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments - 1, get_indices,
+ get_media, NULL, square_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_OK);
+
+ CHK(senc2d_descriptor_get_enclosure(desc, 0, &enclosure) == RES_OK);
+ CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK);
+
+ /* Put enclosure in a 2D view... */
+ S2D(shape_create_line_segments(s2d, &s2d_shp));
+ S2D(line_segments_setup_indexed_vertices(s2d_shp, header->segment_count,
+ senc2d_enclosure_get_segment__, header->vertices_count, &vrtx_get, 1,
+ enclosure));
+
+ S2D(scene_attach_shape(s2d_scn, s2d_shp));
+
+ S2D(scene_view_create(s2d_scn, S2D_SAMPLE, &s2d_view));
+
+ /* ... and sample it. */
+ CHK(ssp_rng_create(&allocator, &ssp_rng_threefry, &rng) == RES_OK);
+ FOR_EACH(i, 0, 10000) {
+ struct s2d_attrib attrib;
+ int n, c;
+ S2D(scene_view_sample(s2d_view,
+ ssp_rng_canonical_float(rng),
+ ssp_rng_canonical_float(rng),
+ &prim, &s));
+ S2D(primitive_get_attrib(&prim, S2D_POSITION, s, &attrib));
+ c = 0;
+ FOR_EACH(n, 0, 2)
+ if(eq_eps(attrib.value[n], 0, FLT_EPSILON)
+ || eq_eps(attrib.value[n], 1, FLT_EPSILON))
+ c++;
+ ASSERT(c == 1);
+ S2D(primitive_get_attrib(&prim, S2D_GEOMETRY_NORMAL, s, &attrib));
+ c = 0;
+ FOR_EACH(n, 0, 2)
+ 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, 2)
+ if(eq_eps(attrib.value[n], 0, FLT_EPSILON))
+ c++;
+ ASSERT(c == 1);
+ }
+
+ SENC2D(enclosure_ref_put(enclosure));
+ SENC2D(scene_ref_put(scn));
+ SENC2D(device_ref_put(dev));
+ SENC2D(descriptor_ref_put(desc));
+
+ SSP(rng_ref_put(rng));
+
+ S2D(shape_ref_put(s2d_shp));
+ S2D(scene_view_ref_put(s2d_view));
+ S2D(device_ref_put(s2d));
+ S2D(scene_ref_put(s2d_scn));
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+
+ return 0;
+}
diff --git a/src/test_senc2d_scene.c b/src/test_senc2d_scene.c
@@ -0,0 +1,194 @@
+/* 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 "senc2d.h"
+#include "test_senc2d_utils.h"
+
+#include <rsys/float2.h>
+#include <rsys/double2.h>
+
+#include <star/s2d.h>
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc2d_device* dev = NULL;
+ struct senc2d_scene* scn = NULL;
+ struct senc2d_descriptor* desc = NULL;
+ struct context ctx;
+ unsigned count, i;
+ (void)argc, (void)argv;
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK);
+ CHK(senc2d_device_create(NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev)
+ == RES_OK);
+
+ CHK(senc2d_scene_create(NULL, 2, &scn) == RES_BAD_ARG);
+ CHK(senc2d_scene_create(dev, 0, &scn) == RES_BAD_ARG);
+ CHK(senc2d_scene_create(dev, 2, NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_create(NULL, 0, &scn) == RES_BAD_ARG);
+ CHK(senc2d_scene_create(NULL, 2, NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_create(dev, 0, NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_create(NULL, 0, NULL) == RES_BAD_ARG);
+ /* It is valid to have unused media */
+ CHK(senc2d_scene_create(dev, 4, &scn) == RES_OK);
+
+ CHK(senc2d_scene_get_segments_count(NULL, &count) == RES_BAD_ARG);
+ CHK(senc2d_scene_get_segments_count(scn, NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_get_segments_count(NULL, NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_get_segments_count(scn, &count) == RES_OK);
+ CHK(count == 0);
+
+ CHK(senc2d_scene_get_unique_segments_count(NULL, &count) == RES_BAD_ARG);
+ CHK(senc2d_scene_get_unique_segments_count(scn, NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_get_unique_segments_count(NULL, NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_get_unique_segments_count(scn, &count) == RES_OK);
+ CHK(count == 0);
+
+ CHK(senc2d_scene_get_vertices_count(NULL, &count) == RES_BAD_ARG);
+ CHK(senc2d_scene_get_vertices_count(scn, NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_get_vertices_count(NULL, NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_get_vertices_count(scn, &count) == RES_OK);
+ CHK(count == 0);
+
+ CHK(senc2d_scene_get_unique_vertices_count(NULL, &count) == RES_BAD_ARG);
+ CHK(senc2d_scene_get_unique_vertices_count(scn, NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_get_unique_vertices_count(NULL, NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_get_unique_vertices_count(scn, &count) == RES_OK);
+ CHK(count == 0);
+
+ /* A 2D square */
+ ctx.positions = square_vertices;
+ ctx.indices = square_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d2(ctx.offset, 0, 0);
+ ctx.front_media = medium0;
+ ctx.back_media = medium1;
+ ctx.global_ids = gid_face;
+
+ CHK(senc2d_scene_add_geometry(NULL, square_nsegments, get_indices, get_media,
+ get_global_id, square_nvertices, get_position, &ctx) == RES_BAD_ARG);
+ CHK(senc2d_scene_add_geometry(scn, 0, get_indices, get_media, get_global_id,
+ square_nvertices, get_position, &ctx) == RES_BAD_ARG);
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, NULL, get_media,
+ get_global_id, square_nvertices, get_position, &ctx) == RES_BAD_ARG);
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, NULL,
+ get_global_id, square_nvertices, get_position, &ctx) == RES_BAD_ARG);
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media,
+ get_global_id, 0, get_position, &ctx) == RES_BAD_ARG);
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media,
+ get_global_id, square_nvertices, NULL, &ctx) == RES_BAD_ARG);
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media,
+ get_global_id, square_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc2d_scene_get_segments_count(scn, &count) == RES_OK);
+ CHK(count == square_nsegments);
+ CHK(senc2d_scene_get_unique_segments_count(scn, &count) == RES_OK);
+ CHK(count == square_nsegments);
+ CHK(senc2d_scene_get_vertices_count(scn, &count) == RES_OK);
+ CHK(count == square_nvertices);
+ CHK(senc2d_scene_get_unique_vertices_count(scn, &count) == RES_OK);
+ CHK(count == square_nvertices);
+
+ CHK(senc2d_scene_analyze(NULL, NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_analyze(scn, NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_analyze(NULL, &desc) == RES_BAD_ARG);
+ CHK(senc2d_scene_analyze(NULL, NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_OK);
+
+ CHK(senc2d_scene_ref_get(NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_ref_get(scn) == RES_OK);
+ CHK(senc2d_scene_ref_put(NULL) == RES_BAD_ARG);
+ CHK(senc2d_scene_ref_put(scn) == RES_OK);
+
+ CHK(senc2d_scene_ref_put(scn) == RES_OK);
+ CHK(senc2d_descriptor_ref_put(desc) == RES_OK);
+ CHK(senc2d_scene_create(dev, 4, &scn) == RES_OK);
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media,
+ get_global_id, square_nvertices, get_position, &ctx) == RES_OK);
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_OK);
+
+ FOR_EACH(i, 0, square_nsegments) {
+ unsigned gid;
+ CHK(senc2d_descriptor_get_global_segment_global_id(desc, i, &gid) == RES_OK);
+ /* gid has been set to gid_face. */
+ CHK(gid == gid_face[i]);
+ }
+
+ CHK(senc2d_scene_ref_put(scn) == RES_OK);
+ CHK(senc2d_descriptor_ref_put(desc) == RES_OK);
+ CHK(senc2d_scene_create(dev, 4, &scn) == RES_OK);
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_OK);
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_OK);
+
+ FOR_EACH(i, 0, square_nsegments) {
+ unsigned gid;
+ CHK(senc2d_descriptor_get_global_segment_global_id(desc, i, &gid) == RES_OK);
+ /* Default gid: segments rank. */
+ CHK(gid == i);
+ }
+
+ /* Invalid medium ID */
+ ctx.back_media = medium1_12;
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_BAD_ARG);
+ ctx.back_media = medium1;
+
+ /* Invalid vertex ID */
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices - 1, get_position, &ctx) == RES_BAD_ARG);
+
+ /* Incoherent medium on a duplicate segment */
+ ctx.back_media = medium1_3;
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_BAD_ARG);
+
+ /* It is OK dd geometry after a failed add */
+ ctx.back_media = medium1;
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_OK);
+
+ /* Coherent medium on duplicate segment */
+ ctx.back_media = medium1;
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_OK);
+
+ /* Coherent medium on duplicate segment V2 */
+ ctx.reverse_med = 1;
+ ctx.front_media = medium1;
+ ctx.back_media = medium0;
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_OK);
+
+ /* Coherent medium on duplicate segment V3 */
+ ctx.reverse_med = 0;
+ ctx.reverse_vrtx = 1;
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc2d_scene_ref_put(scn) == RES_OK);
+ CHK(senc2d_device_ref_put(dev) == RES_OK);
+ CHK(senc2d_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_senc2d_square_behind_square.c b/src/test_senc2d_square_behind_square.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 "senc2d.h"
+#include "test_senc2d_utils.h"
+
+#include <rsys/double2.h>
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc2d_descriptor* desc = NULL;
+ struct senc2d_device* dev = NULL;
+ struct senc2d_scene* scn = NULL;
+ struct context ctx;
+ (void)argc, (void)argv;
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK);
+ CHK(senc2d_device_create
+ (NULL, &allocator, 1/*SENC2D_NTHREADS_DEFAULT*/, 1, &dev) == RES_OK);
+
+ /* Create the scene */
+ CHK(senc2d_scene_create(dev, 2, &scn) == RES_OK);
+
+ ctx.positions = square_vertices;
+ ctx.indices = square_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d2(ctx.offset, 0, 0);
+ ctx.front_media = medium0;
+ ctx.back_media = medium1;
+
+ /* First square */
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_OK);
+
+ /* +Y from the first square,
+ * big enough to prevent rays from the first square to miss this one */
+ d2(ctx.offset, -2, 20);
+ ctx.scale = 5;
+
+ /* Second square */
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_OK);
+
+ /* Even further in +Y, even bigger */
+ d2(ctx.offset, -3, 30);
+ ctx.scale = 7;
+ ctx.front_media = medium1;
+ ctx.back_media = medium0;
+
+ /* Third square */
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_OK);
+
+ if(desc) CHK(senc2d_descriptor_ref_put(desc) == RES_OK);
+ desc = NULL;
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_BAD_ARG);
+
+ CHK(senc2d_scene_ref_put(scn) == RES_OK);
+ CHK(senc2d_device_ref_put(dev) == RES_OK);
+ if(desc) CHK(senc2d_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_senc2d_square_in_square.c b/src/test_senc2d_square_in_square.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 "senc2d.h"
+#include "test_senc2d_utils.h"
+
+#include <rsys/double2.h>
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc2d_descriptor* desc = NULL;
+ struct senc2d_device* dev = NULL;
+ struct senc2d_scene* scn = NULL;
+ struct context ctx;
+ (void)argc, (void)argv;
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK);
+ CHK(senc2d_device_create
+ (NULL, &allocator, 2/*SENC2D_NTHREADS_DEFAULT*/, 1, &dev) == RES_OK);
+
+ /* Create the scene */
+ CHK(senc2d_scene_create(dev, 2, &scn) == RES_OK);
+
+ ctx.positions = square_vertices;
+ ctx.indices = square_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d2(ctx.offset, 0, 0);
+ /* Smallest square exterior is medium 0 */
+ ctx.front_media = medium0;
+ /* Smallest square interior is medium 1 */
+ ctx.back_media = medium1;
+
+ /* First square */
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_OK);
+
+ d2(ctx.offset, -1, -1);
+ ctx.scale = 3;
+ /* Bigger square exterior is medium 1 */
+ /* Bigger square interior is medium 0 */
+ ctx.reverse_vrtx = 1;
+
+ /* Second square */
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_OK);
+
+ d2(ctx.offset, -4, -4);
+ ctx.scale = 10;
+ ctx.reverse_vrtx = 1;
+ ctx.reverse_med = 1;
+ /* Biggest square exterior is medium 1 */
+ ctx.front_media = medium1;
+ /* Biggest square interior is medium 0 */
+ ctx.back_media = medium0; /* mismatch with square 2 */
+
+ /* Third square */
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL,
+ square_nvertices, get_position, &ctx) == RES_OK);
+
+ if(desc) CHK(senc2d_descriptor_ref_put(desc) == RES_OK);
+ desc = NULL;
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_BAD_ARG);
+
+ CHK(senc2d_scene_ref_put(scn) == RES_OK);
+ CHK(senc2d_device_ref_put(dev) == RES_OK);
+ if(desc) CHK(senc2d_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_senc2d_square_on_square.c b/src/test_senc2d_square_on_square.c
@@ -0,0 +1,129 @@
+/* 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/>. */
+
+#define _POSIX_C_SOURCE 200112L /* snprintf */
+
+#include "senc2d.h"
+#include "test_senc2d_utils.h"
+
+#include <rsys/double2.h>
+
+#include <stdio.h>
+
+/*
+ Y
+ ^
+4 | +-----------------------+
+ | | 3
+3 | | +-----+ |
+ | m2 | m1 | m0 2 |
+ | | | | |
+2 | | +-----+ |
+ | | | m0 1 |
+ | | | | |
+1 | | +-----+ |
+ | | |
+0 - +-----------------------+
+
+ |--------------------------> X
+ 0 1 2 3 4
+*/
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc2d_descriptor* desc = NULL;
+ struct senc2d_device* dev = NULL;
+ struct senc2d_scene* scn = NULL;
+ struct context ctx;
+ unsigned count, i;
+ (void)argc, (void)argv;
+
+ CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK);
+ CHK(senc2d_device_create
+ (NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev) == RES_OK);
+
+ /* Create the scene */
+ CHK(senc2d_scene_create(dev, 3, &scn) == RES_OK);
+
+ ctx.positions = square_vertices;
+ ctx.indices = square_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d2(ctx.offset, 1, 1);
+ /* Small square #1 exterior is medium 1,
+ * except for the top face where it is 0 */
+ ctx.front_media = medium1_front0;
+ /* Smallest square interior is medium 0 */
+ ctx.back_media = medium0;
+
+ /* Add square #1 */
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media,
+ NULL, square_nvertices, get_position, &ctx) == RES_OK);
+
+ d2(ctx.offset, 1, 2);
+ ctx.scale = 1;
+ /* Small square #2 exterior is medium 1,
+ * except for the bottom face where it is 0 */
+ ctx.front_media = medium1_back0;
+ /* Smallest square interior is medium 0 */
+ ctx.back_media = medium0;
+
+ /* Add square #2 (has a duplicate face with square #1) */
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media,
+ NULL, square_nvertices, get_position, &ctx) == RES_OK);
+
+ d2(ctx.offset, 0, 0);
+ ctx.scale = 4;
+ ctx.reverse_vrtx = 1;
+ ctx.reverse_med = 1;
+ /* Big square #3 exterior is medium 2 */
+ ctx.front_media = medium2;
+ /* Big square #3 interior is medium 1 */
+ ctx.back_media = medium1;
+
+ /* Add square #3 */
+ CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media,
+ NULL, square_nvertices, get_position, &ctx) == RES_OK);
+
+ CHK(senc2d_scene_analyze(scn, &desc) == RES_OK);
+
+ CHK(senc2d_descriptor_get_enclosure_count(desc, &count) == RES_OK);
+ CHK(count == 4);
+
+ CHK(senc2d_descriptor_get_global_vertices_count(desc, &count) == RES_OK);
+ CHK(count == 10);
+
+ CHK(senc2d_descriptor_get_global_segments_count(desc, &count) == RES_OK);
+ CHK(count == 11);
+
+ CHK(senc2d_descriptor_get_enclosure_count(desc, &count) == RES_OK);
+ FOR_EACH(i, 0, count) {
+ char name[128];
+ snprintf(name, sizeof(name), "test2d_square_on_square_%u.obj", i);
+ dump_enclosure(desc, i, name);
+ }
+
+ CHK(senc2d_scene_ref_put(scn) == RES_OK);
+ CHK(senc2d_device_ref_put(dev) == RES_OK);
+ if(desc) CHK(senc2d_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_senc2d_utils.h b/src/test_senc2d_utils.h
@@ -0,0 +1,229 @@
+/* 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_UTILS2_H
+#define TEST_UTILS2_H
+
+#include <rsys/rsys.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/stretchy_array.h>
+
+#include <stdio.h>
+
+/*******************************************************************************
+ * Geometry
+ ******************************************************************************/
+static const double square_vertices[4/*#vertices*/*2/*#coords per vertex*/] = {
+ 0.0, 0.0,
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 1.0, 1.0
+};
+static const unsigned
+square_nvertices = sizeof(square_vertices) / (2 * sizeof(*square_vertices));
+
+/* The following array lists the indices toward the 2D vertices of each
+ * segment.
+ * Y
+ * 2----3 |
+ * | | 0----X
+ * | |
+ * 0----1
+ */
+static const unsigned
+square_indices[4/*#segments*/*2/*#indices per segment*/] = {
+ 0, 2,
+ 2, 3,
+ 3, 1,
+ 1, 0
+};
+static const unsigned
+square_nsegments = sizeof(square_indices) / (2 * sizeof(*square_indices));
+
+struct context {
+ double* positions;
+ unsigned* indices;
+ const unsigned* front_media;
+ const unsigned* back_media;
+ const unsigned* global_ids;
+ double offset[2];
+ double scale;
+ char reverse_vrtx, reverse_med;
+};
+
+static const unsigned medium0[4] = { 0, 0, 0, 0 };
+static const unsigned medium1[4] = { 1, 1, 1, 1 };
+static const unsigned medium2[4] = { 2, 2, 2, 2 };
+static const unsigned medium1_3[4] = { 1, 1, 3, 1 };
+static const unsigned medium1_12[4] = { 1, 12, 1, 1 };
+static const unsigned medium1_back0[4] = { 1, 1, 1, 0 };
+static const unsigned medium1_front0[4] = { 1, 0, 1, 1 };
+
+static const unsigned gid_face[4] = { 0, 1, 2, 3 };
+
+static INLINE void
+get_indices(const unsigned iseg, unsigned ids[2], void* context)
+{
+ struct context* ctx = context;
+ ASSERT(ids && ctx);
+ ids[ctx->reverse_vrtx ? 1 : 0] = ctx->indices[iseg * 2 + 0];
+ ids[ctx->reverse_vrtx ? 0 : 1] = ctx->indices[iseg * 2 + 1];
+}
+
+static INLINE void
+get_position(const unsigned ivert, double pos[2], void* context)
+{
+ struct context* ctx = context;
+ ASSERT(pos && ctx);
+ pos[0] = ctx->positions[ivert * 2 + 0] * ctx->scale + ctx->offset[0];
+ pos[1] = ctx->positions[ivert * 2 + 1] * ctx->scale + ctx->offset[1];
+}
+
+static INLINE void
+get_media(const unsigned iseg, unsigned medium[2], void* context)
+{
+ struct context* ctx = context;
+ ASSERT(medium && ctx);
+ medium[ctx->reverse_med ? 1 : 0] = ctx->front_media[iseg];
+ medium[ctx->reverse_med ? 0 : 1] = ctx->back_media[iseg];
+}
+
+static INLINE void
+get_global_id(const unsigned iseg, unsigned* gid, void* context)
+{
+ struct context* ctx = context;
+ ASSERT(gid && context);
+ *gid = ctx->global_ids[iseg];
+}
+
+/*******************************************************************************
+ * Miscellaneous
+ ******************************************************************************/
+static INLINE void
+dump_global
+ (struct senc2d_descriptor* desc,
+ const char* name)
+{
+ FILE* stream;
+ unsigned segment_count, vertices_count, i;
+
+ ASSERT(desc && name);
+
+ CHK(senc2d_descriptor_get_global_vertices_count(desc, &vertices_count) == RES_OK);
+ CHK(senc2d_descriptor_get_global_segments_count(desc, &segment_count) == RES_OK);
+
+ stream = fopen(name, "w");
+ CHK(stream);
+ FOR_EACH(i, 0, vertices_count) {
+ double tmp[2];
+ CHK(senc2d_descriptor_get_global_vertex(desc, i, tmp) == RES_OK);
+ fprintf(stream, "v %g %g 0\n", SPLIT2(tmp));
+ }
+ FOR_EACH(i, 0, segment_count) {
+ unsigned indices[2];
+ CHK(senc2d_descriptor_get_global_segment(desc, i, indices) == RES_OK);
+ fprintf(stream, "l %lu %lu\n",
+ (unsigned long)(1 + indices[0]), (unsigned long)(1 + indices[1]));
+ }
+ fclose(stream);
+}
+
+static INLINE void
+dump_enclosure
+ (struct senc2d_descriptor* desc,
+ const unsigned enc,
+ const char* name)
+{
+ struct senc2d_enclosure* enclosure;
+ const struct enclosure2d_header* header;
+ FILE* stream;
+ unsigned count, i;
+
+ ASSERT(desc && name);
+
+ SENC2D(descriptor_get_enclosure_count(desc, &count));
+ ASSERT(enc < count);
+ CHK(senc2d_descriptor_get_enclosure(desc, enc, &enclosure) == RES_OK);
+ CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK);
+
+ stream = fopen(name, "w");
+ CHK(stream);
+ FOR_EACH(i, 0, header->vertices_count) {
+ double tmp[2];
+ CHK(senc2d_enclosure_get_vertex(enclosure, i, tmp) == RES_OK);
+ fprintf(stream, "v %g %g 0\n", SPLIT2(tmp));
+ }
+ FOR_EACH(i, 0, header->segment_count) {
+ unsigned indices[2];
+ CHK(senc2d_enclosure_get_segment(enclosure, i, indices) == RES_OK);
+ fprintf(stream, "l %lu %lu\n",
+ (unsigned long)(1+indices[0]), (unsigned long)(1+indices[1]));
+ }
+ CHK(senc2d_enclosure_ref_put(enclosure) == RES_OK);
+ fclose(stream);
+}
+
+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");
+ }
+}
+
+/*******************************************************************************
+ * Circle functions
+ ******************************************************************************/
+void create_circle
+ (const double radius,
+ const unsigned nslices,
+ struct context* ctx)
+{
+ double step_theta;
+ unsigned itheta;
+ unsigned islice;
+ ASSERT(radius > 0 && nslices >= 3 && ctx);
+
+ step_theta = 2 * PI / (double)nslices;
+ FOR_EACH(itheta, 0, nslices) {
+ const double theta = (double)itheta * step_theta;
+ const double x = cos(theta);
+ const double y = sin(theta);
+ sa_push(ctx->positions, x*radius);
+ sa_push(ctx->positions, y*radius);
+ }
+
+ FOR_EACH(islice, 0, nslices) {
+ const unsigned v0 = islice;
+ const unsigned v1 = ((islice + 1) % nslices);
+ sa_push(ctx->indices, v0);
+ sa_push(ctx->indices, v1);
+ }
+}
+
+void circle_release(struct context* ctx)
+{
+ ASSERT(ctx);
+ sa_release(ctx->positions);
+ sa_release(ctx->indices);
+ ctx->positions = NULL;
+ ctx->indices = NULL;
+}
+
+#endif /* TEST_UTILS2_H */
+