star-enclosures-2d

Extract enclosures from 2D geometry
git clone git://git.meso-star.fr/star-enclosures-2d.git
Log | Files | Refs | README | LICENSE

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:
ACOPYING | 674+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AREADME.md | 31+++++++++++++++++++++++++++++++
Acmake/CMakeLists.txt | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc2d.h | 273+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc2d_descriptor.c | 242+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc2d_descriptor_c.h | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc2d_device.c | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc2d_device_c.h | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc2d_enclosure.c | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc2d_enclosure_c.h | 37+++++++++++++++++++++++++++++++++++++
Asrc/senc2d_enclosure_data.h | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc2d_internal_types.h | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc2d_s2d_wrapper.h | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc2d_scene.c | 341+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc2d_scene_analyze.c | 1165+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc2d_scene_analyze_c.h | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc2d_scene_c.h | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc2d_descriptor.c | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc2d_device.c | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc2d_enclosure.c | 247+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc2d_many_enclosures.c | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc2d_many_segments.c | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc2d_sample_enclosure.c | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc2d_scene.c | 194+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc2d_square_behind_square.c | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc2d_square_in_square.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc2d_square_on_square.c | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc2d_utils.h | 229+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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 */ +