hcdt 0.1.0.5 → 0.1.1.0
raw patch · 23 files changed
+6971/−6010 lines, 23 filesdep +hashabledep +system-cxx-std-libdep +witherablesetup-changed
Dependencies added: hashable, system-cxx-std-lib, witherable
Files
- CHANGELOG.md +15/−11
- LICENSE +29/−29
- LICENSE_CDT.txt +384/−384
- README.md +81/−74
- Setup.hs +2/−2
- cpp/CDT.h +448/−448
- cpp/CDT.hpp +107/−107
- cpp/CDTUtils.h +507/−475
- cpp/CDTUtils.hpp +308/−279
- cpp/KDTree.h +412/−399
- cpp/LocatorKDTree.h +81/−71
- cpp/Triangulation.h +779/−617
- cpp/Triangulation.hpp +2041/−1558
- cpp/hcdt.cpp +132/−132
- cpp/hcdt.hpp +34/−34
- cpp/predicates.h +915/−939
- hcdt.cabal +65/−53
- src/Geometry/HCDT.hs +5/−5
- src/Geometry/HCDT/Delaunay.hs +42/−42
- src/Geometry/HCDT/Triangulation.hs +277/−277
- src/Geometry/HCDT/Triangulation.hsc +224/−0
- src/Geometry/HCDT/Types.hs +34/−27
- src/Geometry/HCDT/Utils.hs +49/−47
CHANGELOG.md view
@@ -1,11 +1,15 @@-0.1.0.0---------* initial release--0.1.0.4---------* removed the upper bounds of the dependencies--0.1.0.5---------* fixed the cabal file+0.1.0.0 +------- +* initial release + +0.1.0.4 +------- +* removed the upper bounds of the dependencies + +0.1.0.5 +------- +* fixed the cabal file + +0.1.1.0 +------- +* upgraded the 'CDT' library
LICENSE view
@@ -1,30 +1,30 @@-Copyright Stéphane Laurent (c) 2022--All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.-- * Redistributions in binary form must reproduce the above- copyright notice, this list of conditions and the following- disclaimer in the documentation and/or other materials provided- with the distribution.-- * Neither the name of Author name here nor the names of other- contributors may be used to endorse or promote products derived- from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+Copyright Stéphane Laurent (c) 2022 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Stéphane Laurent nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
LICENSE_CDT.txt view
@@ -1,384 +1,384 @@-The 'hcdt' package as a whole is distributed under BSD-3-Clause.--It includes an open source C++ library: 'CDT'.--Full copy of the license agreement used by this component is included-below.----### 'CDT' license--Mozilla Public License Version 2.0-==================================--1. Definitions-----------------1.1. "Contributor"- means each individual or legal entity that creates, contributes to- the creation of, or owns Covered Software.--1.2. "Contributor Version"- means the combination of the Contributions of others (if any) used- by a Contributor and that particular Contributor's Contribution.--1.3. "Contribution"- means Covered Software of a particular Contributor.--1.4. "Covered Software"- means Source Code Form to which the initial Contributor has attached- the notice in Exhibit A, the Executable Form of such Source Code- Form, and Modifications of such Source Code Form, in each case- including portions thereof.--1.5. "Incompatible With Secondary Licenses"- means-- (a) that the initial Contributor has attached the notice described- in Exhibit B to the Covered Software; or-- (b) that the Covered Software was made available under the terms of- version 1.1 or earlier of the License, but not also under the- terms of a Secondary License.--1.6. "Executable Form"- means any form of the work other than Source Code Form.--1.7. "Larger Work"- means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software.--1.8. "License"- means this document.--1.9. "Licensable"- means having the right to grant, to the maximum extent possible,- whether at the time of the initial grant or subsequently, any and- all of the rights conveyed by this License.--1.10. "Modifications"- means any of the following:-- (a) any file in Source Code Form that results from an addition to,- deletion from, or modification of the contents of Covered- Software; or-- (b) any new file in Source Code Form that contains any Covered- Software.--1.11. "Patent Claims" of a Contributor- means any patent claim(s), including without limitation, method,- process, and apparatus claims, in any patent Licensable by such- Contributor that would be infringed, but for the grant of the- License, by the making, using, selling, offering for sale, having- made, import, or transfer of either its Contributions or its- Contributor Version.--1.12. "Secondary License"- means either the GNU General Public License, Version 2.0, the GNU- Lesser General Public License, Version 2.1, the GNU Affero General- Public License, Version 3.0, or any later versions of those- licenses.--1.13. "Source Code Form"- means the form of the work preferred for making modifications.--1.14. "You" (or "Your")- means an individual or a legal entity exercising rights under this- License. For legal entities, "You" includes any entity that- controls, is controlled by, or is under common control with You. For- purposes of this definition, "control" means (a) the power, direct- or indirect, to cause the direction or management of such entity,- whether by contract or otherwise, or (b) ownership of more than- fifty percent (50%) of the outstanding shares or beneficial- ownership of such entity.--2. License Grants and Conditions-----------------------------------2.1. Grants--Each Contributor hereby grants You a world-wide, royalty-free,-non-exclusive license:--(a) under intellectual property rights (other than patent or trademark)- Licensable by such Contributor to use, reproduce, make available,- modify, display, perform, distribute, and otherwise exploit its- Contributions, either on an unmodified basis, with Modifications, or- as part of a Larger Work; and--(b) under Patent Claims of such Contributor to make, use, sell, offer- for sale, have made, import, and otherwise transfer either its- Contributions or its Contributor Version.--2.2. Effective Date--The licenses granted in Section 2.1 with respect to any Contribution-become effective for each Contribution on the date the Contributor first-distributes such Contribution.--2.3. Limitations on Grant Scope--The licenses granted in this Section 2 are the only rights granted under-this License. No additional rights or licenses will be implied from the-distribution or licensing of Covered Software under this License.-Notwithstanding Section 2.1(b) above, no patent license is granted by a-Contributor:--(a) for any code that a Contributor has removed from Covered Software;- or--(b) for infringements caused by: (i) Your and any other third party's- modifications of Covered Software, or (ii) the combination of its- Contributions with other software (except as part of its Contributor- Version); or--(c) under Patent Claims infringed by Covered Software in the absence of- its Contributions.--This License does not grant any rights in the trademarks, service marks,-or logos of any Contributor (except as may be necessary to comply with-the notice requirements in Section 3.4).--2.4. Subsequent Licenses--No Contributor makes additional grants as a result of Your choice to-distribute the Covered Software under a subsequent version of this-License (see Section 10.2) or under the terms of a Secondary License (if-permitted under the terms of Section 3.3).--2.5. Representation--Each Contributor represents that the Contributor believes its-Contributions are its original creation(s) or it has sufficient rights-to grant the rights to its Contributions conveyed by this License.--2.6. Fair Use--This License is not intended to limit any rights You have under-applicable copyright doctrines of fair use, fair dealing, or other-equivalents.--2.7. Conditions--Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted-in Section 2.1.--3. Responsibilities----------------------3.1. Distribution of Source Form--All distribution of Covered Software in Source Code Form, including any-Modifications that You create or to which You contribute, must be under-the terms of this License. You must inform recipients that the Source-Code Form of the Covered Software is governed by the terms of this-License, and how they can obtain a copy of this License. You may not-attempt to alter or restrict the recipients' rights in the Source Code-Form.--3.2. Distribution of Executable Form--If You distribute Covered Software in Executable Form then:--(a) such Covered Software must also be made available in Source Code- Form, as described in Section 3.1, and You must inform recipients of- the Executable Form how they can obtain a copy of such Source Code- Form by reasonable means in a timely manner, at a charge no more- than the cost of distribution to the recipient; and--(b) You may distribute such Executable Form under the terms of this- License, or sublicense it under different terms, provided that the- license for the Executable Form does not attempt to limit or alter- the recipients' rights in the Source Code Form under this License.--3.3. Distribution of a Larger Work--You may create and distribute a Larger Work under terms of Your choice,-provided that You also comply with the requirements of this License for-the Covered Software. If the Larger Work is a combination of Covered-Software with a work governed by one or more Secondary Licenses, and the-Covered Software is not Incompatible With Secondary Licenses, this-License permits You to additionally distribute such Covered Software-under the terms of such Secondary License(s), so that the recipient of-the Larger Work may, at their option, further distribute the Covered-Software under the terms of either this License or such Secondary-License(s).--3.4. Notices--You may not remove or alter the substance of any license notices-(including copyright notices, patent notices, disclaimers of warranty,-or limitations of liability) contained within the Source Code Form of-the Covered Software, except that You may alter any license notices to-the extent required to remedy known factual inaccuracies.--3.5. Application of Additional Terms--You may choose to offer, and to charge a fee for, warranty, support,-indemnity or liability obligations to one or more recipients of Covered-Software. However, You may do so only on Your own behalf, and not on-behalf of any Contributor. You must make it absolutely clear that any-such warranty, support, indemnity, or liability obligation is offered by-You alone, and You hereby agree to indemnify every Contributor for any-liability incurred by such Contributor as a result of warranty, support,-indemnity or liability terms You offer. You may include additional-disclaimers of warranty and limitations of liability specific to any-jurisdiction.--4. Inability to Comply Due to Statute or Regulation------------------------------------------------------If it is impossible for You to comply with any of the terms of this-License with respect to some or all of the Covered Software due to-statute, judicial order, or regulation then You must: (a) comply with-the terms of this License to the maximum extent possible; and (b)-describe the limitations and the code they affect. Such description must-be placed in a text file included with all distributions of the Covered-Software under this License. Except to the extent prohibited by statute-or regulation, such description must be sufficiently detailed for a-recipient of ordinary skill to be able to understand it.--5. Termination-----------------5.1. The rights granted under this License will terminate automatically-if You fail to comply with any of its terms. However, if You become-compliant, then the rights granted under this License from a particular-Contributor are reinstated (a) provisionally, unless and until such-Contributor explicitly and finally terminates Your grants, and (b) on an-ongoing basis, if such Contributor fails to notify You of the-non-compliance by some reasonable means prior to 60 days after You have-come back into compliance. Moreover, Your grants from a particular-Contributor are reinstated on an ongoing basis if such Contributor-notifies You of the non-compliance by some reasonable means, this is the-first time You have received notice of non-compliance with this License-from such Contributor, and You become compliant prior to 30 days after-Your receipt of the notice.--5.2. If You initiate litigation against any entity by asserting a patent-infringement claim (excluding declaratory judgment actions,-counter-claims, and cross-claims) alleging that a Contributor Version-directly or indirectly infringes any patent, then the rights granted to-You by any and all Contributors for the Covered Software under Section-2.1 of this License shall terminate.--5.3. In the event of termination under Sections 5.1 or 5.2 above, all-end user license agreements (excluding distributors and resellers) which-have been validly granted by You or Your distributors under this License-prior to termination shall survive termination.--************************************************************************-* *-* 6. Disclaimer of Warranty *-* ------------------------- *-* *-* Covered Software is provided under this License on an "as is" *-* basis, without warranty of any kind, either expressed, implied, or *-* statutory, including, without limitation, warranties that the *-* Covered Software is free of defects, merchantable, fit for a *-* particular purpose or non-infringing. The entire risk as to the *-* quality and performance of the Covered Software is with You. *-* Should any Covered Software prove defective in any respect, You *-* (not any Contributor) assume the cost of any necessary servicing, *-* repair, or correction. This disclaimer of warranty constitutes an *-* essential part of this License. No use of any Covered Software is *-* authorized under this License except under this disclaimer. *-* *-************************************************************************--************************************************************************-* *-* 7. Limitation of Liability *-* -------------------------- *-* *-* Under no circumstances and under no legal theory, whether tort *-* (including negligence), contract, or otherwise, shall any *-* Contributor, or anyone who distributes Covered Software as *-* permitted above, be liable to You for any direct, indirect, *-* special, incidental, or consequential damages of any character *-* including, without limitation, damages for lost profits, loss of *-* goodwill, work stoppage, computer failure or malfunction, or any *-* and all other commercial damages or losses, even if such party *-* shall have been informed of the possibility of such damages. This *-* limitation of liability shall not apply to liability for death or *-* personal injury resulting from such party's negligence to the *-* extent applicable law prohibits such limitation. Some *-* jurisdictions do not allow the exclusion or limitation of *-* incidental or consequential damages, so this exclusion and *-* limitation may not apply to You. *-* *-************************************************************************--8. Litigation----------------Any litigation relating to this License may be brought only in the-courts of a jurisdiction where the defendant maintains its principal-place of business and such litigation shall be governed by laws of that-jurisdiction, without reference to its conflict-of-law provisions.-Nothing in this Section shall prevent a party's ability to bring-cross-claims or counter-claims.--9. Miscellaneous-------------------This License represents the complete agreement concerning the subject-matter hereof. If any provision of this License is held to be-unenforceable, such provision shall be reformed only to the extent-necessary to make it enforceable. Any law or regulation which provides-that the language of a contract shall be construed against the drafter-shall not be used to construe this License against a Contributor.--10. Versions of the License------------------------------10.1. New Versions--Mozilla Foundation is the license steward. Except as provided in Section-10.3, no one other than the license steward has the right to modify or-publish new versions of this License. Each version will be given a-distinguishing version number.--10.2. Effect of New Versions--You may distribute the Covered Software under the terms of the version-of the License under which You originally received the Covered Software,-or under the terms of any subsequent version published by the license-steward.--10.3. Modified Versions--If you create software not governed by this License, and you want to-create a new license for such software, you may create and use a-modified version of this License if you rename the license and remove-any references to the name of the license steward (except to note that-such modified license differs from this License).--10.4. Distributing Source Code Form that is Incompatible With Secondary-Licenses--If You choose to distribute Source Code Form that is Incompatible With-Secondary Licenses under the terms of this version of the License, the-notice described in Exhibit B of this License must be attached.--Exhibit A - Source Code Form License Notice---------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public- License, v. 2.0. If a copy of the MPL was not distributed with this- file, You can obtain one at http://mozilla.org/MPL/2.0/.--If it is not possible or desirable to put the notice in a particular-file, then You may include the notice in a location (such as a LICENSE-file in a relevant directory) where a recipient would be likely to look-for such a notice.--You may add additional accurate notices of copyright ownership.--Exhibit B - "Incompatible With Secondary Licenses" Notice------------------------------------------------------------ This Source Code Form is "Incompatible With Secondary Licenses", as- defined by the Mozilla Public License, v. 2.0.+The 'hcdt' package as a whole is distributed under BSD-3-Clause. + +It includes an open source C++ library: 'CDT'. + +Full copy of the license agreement used by this component is included +below. + + + +### 'CDT' license + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0.
README.md view
@@ -1,75 +1,82 @@-# hcdt--### Delaunay triangulation --```haskell-ghci> import Text.Show.Pretty-ghci> import Geometry.HCDT-ghci> vertices = [Vertex 0 0, Vertex 0 1, Vertex 1 1, Vertex 1 0]-ghci> triangulation <- delaunay vertices-ghci> pPrint triangulation-Triangulation- { _vertices =- fromList- [ ( 0 , Vertex 0.0 0.0 )- , ( 1 , Vertex 0.0 1.0 )- , ( 2 , Vertex 1.0 1.0 )- , ( 3 , Vertex 1.0 0.0 )- ]- , _triangles = [ Triangle 1 0 2 , Triangle 2 0 3 ]- , _edges = [ Edge 2 3 , Edge 0 3 , Edge 1 2 , Edge 0 2 , Edge 0 1 ]- }-ghci> pPrint $ borderEdges triangulation-fromList [ Edge 1 0 , Edge 1 2 , Edge 0 3 , Edge 2 3 ]-```---### Constrained Delaunay triangulation --```haskell-ghci> import Text.Show.Pretty-ghci> import Geometry.HCDT-ghci> vertices = [Vertex 1 1, Vertex 3 1, Vertex 2 2, Vertex 0 0, Vertex 4 0, Vertex 2 5]-ghci> edges = [Edge 0 1, Edge 0 2, Edge 1 2, Edge 3 4, Edge 3 5, Edge 4 5]-ghci> triangulation <- cdelaunay vertices edges-ntriangles: 6-ghci> pPrint triangulation-ConstrainedTriangulation- { _triangulation =- Triangulation- { _vertices =- fromList- [ ( 0 , Vertex 1.0 1.0 )- , ( 1 , Vertex 3.0 1.0 )- , ( 2 , Vertex 2.0 2.0 )- , ( 3 , Vertex 0.0 0.0 )- , ( 4 , Vertex 4.0 0.0 )- , ( 5 , Vertex 2.0 5.0 )- ]- , _triangles =- [ Triangle 1 0 3- , Triangle 2 1 5- , Triangle 2 5 0- , Triangle 0 5 3- , Triangle 1 4 5- , Triangle 1 3 4- ]- , _edges =- [ Edge 3 4- , Edge 1 4- , Edge 3 5- , Edge 0 5- , Edge 2 5- , Edge 0 2- , Edge 1 5- , Edge 1 2- , Edge 1 3- , Edge 4 5- , Edge 0 3- , Edge 0 1- ]- }- , _fixedEdges =- [ Edge 0 1 , Edge 0 2 , Edge 4 5 , Edge 1 2 , Edge 3 4 , Edge 3 5 ]- }+# hcdt + +<!-- badges: start --> +[](https://github.com/stla/hcdt/actions/workflows/Stack-lts.yml) +[](https://github.com/stla/hcdt/actions/workflows/Stack-lts-Mac.yml) +[](https://github.com/stla/hcdt/actions/workflows/Stack-nightly.yml) +<!-- badges: end --> + + +### Delaunay triangulation + +```haskell +ghci> import Text.Show.Pretty +ghci> import Geometry.HCDT +ghci> vertices = [Vertex 0 0, Vertex 0 1, Vertex 1 1, Vertex 1 0] +ghci> triangulation <- delaunay vertices +ghci> pPrint triangulation +Triangulation + { _vertices = + fromList + [ ( 0 , Vertex 0.0 0.0 ) + , ( 1 , Vertex 0.0 1.0 ) + , ( 2 , Vertex 1.0 1.0 ) + , ( 3 , Vertex 1.0 0.0 ) + ] + , _triangles = [ Triangle 1 0 2 , Triangle 2 0 3 ] + , _edges = [ Edge 2 3 , Edge 0 3 , Edge 1 2 , Edge 0 2 , Edge 0 1 ] + } +ghci> pPrint $ borderEdges triangulation +fromList [ Edge 1 0 , Edge 1 2 , Edge 0 3 , Edge 2 3 ] +``` + + +### Constrained Delaunay triangulation + +```haskell +ghci> import Text.Show.Pretty +ghci> import Geometry.HCDT +ghci> vertices = [Vertex 1 1, Vertex 3 1, Vertex 2 2, Vertex 0 0, Vertex 4 0, Vertex 2 5] +ghci> edges = [Edge 0 1, Edge 0 2, Edge 1 2, Edge 3 4, Edge 3 5, Edge 4 5] +ghci> triangulation <- cdelaunay vertices edges +ntriangles: 6 +ghci> pPrint triangulation +ConstrainedTriangulation + { _triangulation = + Triangulation + { _vertices = + fromList + [ ( 0 , Vertex 1.0 1.0 ) + , ( 1 , Vertex 3.0 1.0 ) + , ( 2 , Vertex 2.0 2.0 ) + , ( 3 , Vertex 0.0 0.0 ) + , ( 4 , Vertex 4.0 0.0 ) + , ( 5 , Vertex 2.0 5.0 ) + ] + , _triangles = + [ Triangle 1 0 3 + , Triangle 2 1 5 + , Triangle 2 5 0 + , Triangle 0 5 3 + , Triangle 1 4 5 + , Triangle 1 3 4 + ] + , _edges = + [ Edge 3 4 + , Edge 1 4 + , Edge 3 5 + , Edge 0 5 + , Edge 2 5 + , Edge 0 2 + , Edge 1 5 + , Edge 1 2 + , Edge 1 3 + , Edge 4 5 + , Edge 0 3 + , Edge 0 1 + ] + } + , _fixedEdges = + [ Edge 0 1 , Edge 0 2 , Edge 4 5 , Edge 1 2 , Edge 3 4 , Edge 3 5 ] + } ```
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple +main = defaultMain
cpp/CDT.h view
@@ -1,448 +1,448 @@-/* This Source Code Form is subject to the terms of the Mozilla Public- * License, v. 2.0. If a copy of the MPL was not distributed with this- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */--/**- * @file- * Public API- */--#ifndef CDT_lNrmUayWQaIR5fxnsg9B-#define CDT_lNrmUayWQaIR5fxnsg9B--#include "CDTUtils.h"-#include "Triangulation.h"--#include "remove_at.hpp"--#include <algorithm>-#include <cassert>-#include <cstdlib>-#include <iterator>-#include <memory>-#include <stack>-#include <vector>--/// Namespace containing triangulation functionality-namespace CDT-{--/** @defgroup API Public API- * Contains API for constrained and conforming Delaunay triangulations- */-/// @{--/**- * Type used for storing layer depths for triangles- * @note LayerDepth should support 60K+ layers, which could be to much or- * too little for some use cases. Feel free to re-define this typedef.- */-typedef unsigned short LayerDepth;-typedef LayerDepth BoundaryOverlapCount;--/// Triangles by vertex index-typedef std::vector<TriIndVec> VerticesTriangles;--/** @defgroup helpers Helpers- * Helpers for working with CDT::Triangulation.- */-/// @{--/**- * Calculate triangles adjacent to vertices (triangles by vertex index)- * @param triangles triangulation- * @param verticesSize total number of vertices to pre-allocate the output- * @return triangles by vertex index- */-CDT_EXPORT VerticesTriangles-calculateTrianglesByVertex(const TriangleVec& triangles, VertInd verticesSize);--/**- * Information about removed duplicated vertices.- *- * Contains mapping information and removed duplicates indices.- * @note vertices {0,1,2,3,4} where 0 and 3 are the same will produce mapping- * {0,1,2,0,3} (to new vertices {0,1,2,3}) and duplicates {3}- */-struct CDT_EXPORT DuplicatesInfo-{- std::vector<std::size_t> mapping; ///< vertex index mapping- std::vector<std::size_t> duplicates; ///< duplicates' indices-};--/**- * Find duplicates in given custom point-type range- * @note duplicates are points with exactly same X and Y coordinates- * @tparam TVertexIter iterator that dereferences to custom point type- * @tparam TGetVertexCoordX function object getting x coordinate from vertex.- * Getter signature: const TVertexIter::value_type& -> T- * @tparam TGetVertexCoordY function object getting y coordinate from vertex.- * Getter signature: const TVertexIter::value_type& -> T- * @param first beginning of the range of vertices- * @param last end of the range of vertices- * @param getX getter of X-coordinate- * @param getY getter of Y-coordinate- * @returns information about vertex duplicates- */-template <- typename T,- typename TVertexIter,- typename TGetVertexCoordX,- typename TGetVertexCoordY>-DuplicatesInfo FindDuplicates(- TVertexIter first,- TVertexIter last,- TGetVertexCoordX getX,- TGetVertexCoordY getY);--/**- * Remove duplicates in-place from vector of custom points- * @tparam TVertex vertex type- * @tparam TAllocator allocator used by input vector of vertices- * @param vertices vertices to remove duplicates from- * @param duplicates information about duplicates- */-template <typename TVertex, typename TAllocator>-void RemoveDuplicates(- std::vector<TVertex, TAllocator>& vertices,- const std::vector<std::size_t>& duplicates);--/**- * Remove duplicated points in-place- *- * @tparam T type of vertex coordinates (e.g., float, double)- * @param[in, out] vertices collection of vertices to remove duplicates from- * @returns information about duplicated vertices that were removed.- */-template <typename T>-CDT_EXPORT DuplicatesInfo RemoveDuplicates(std::vector<V2d<T> >& vertices);--/**- * Remap vertex indices in edges (in-place) using given vertex-index mapping.- * @tparam TEdgeIter iterator that dereferences to custom edge type- * @tparam TGetEdgeVertexStart function object getting start vertex index- * from an edge.- * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd- * @tparam TGetEdgeVertexEnd function object getting end vertex index from- * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd- * @tparam TMakeEdgeFromStartAndEnd function object that makes new edge from- * start and end vertices- * @param first beginning of the range of edges- * @param last end of the range of edges- * @param mapping vertex-index mapping- * @param getStart getter of edge start vertex index- * @param getEnd getter of edge end vertex index- * @param makeEdge factory for making edge from vetices- */-template <- typename TEdgeIter,- typename TGetEdgeVertexStart,- typename TGetEdgeVertexEnd,- typename TMakeEdgeFromStartAndEnd>-CDT_EXPORT void RemapEdges(- TEdgeIter first,- TEdgeIter last,- const std::vector<std::size_t>& mapping,- TGetEdgeVertexStart getStart,- TGetEdgeVertexEnd getEnd,- TMakeEdgeFromStartAndEnd makeEdge);--/**- * Remap vertex indices in edges (in-place) using given vertex-index mapping.- *- * @note Mapping can be a result of RemoveDuplicates function- * @param[in,out] edges collection of edges to remap- * @param mapping vertex-index mapping- */-CDT_EXPORT void-RemapEdges(std::vector<Edge>& edges, const std::vector<std::size_t>& mapping);--/**- * Find point duplicates, remove them from vector (in-place) and remap edges- * (in-place)- * @note Same as a chained call of CDT::FindDuplicates, CDT::RemoveDuplicates,- * and CDT::RemapEdges- * @tparam T type of vertex coordinates (e.g., float, double)- * @tparam TVertex type of vertex- * @tparam TGetVertexCoordX function object getting x coordinate from vertex.- * Getter signature: const TVertexIter::value_type& -> T- * @tparam TGetVertexCoordY function object getting y coordinate from vertex.- * Getter signature: const TVertexIter::value_type& -> T- * @tparam TEdgeIter iterator that dereferences to custom edge type- * @tparam TGetEdgeVertexStart function object getting start vertex index- * from an edge.- * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd- * @tparam TGetEdgeVertexEnd function object getting end vertex index from- * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd- * @tparam TMakeEdgeFromStartAndEnd function object that makes new edge from- * start and end vertices- * @param[in, out] vertices vertices to remove duplicates from- * @param[in, out] edges collection of edges connecting vertices- * @param getX getter of X-coordinate- * @param getY getter of Y-coordinate- * @param edgesFirst beginning of the range of edges- * @param edgesLast end of the range of edges- * @param getStart getter of edge start vertex index- * @param getEnd getter of edge end vertex index- * @param makeEdge factory for making edge from vetices- * @returns information about vertex duplicates- */-template <- typename T,- typename TVertex,- typename TGetVertexCoordX,- typename TGetVertexCoordY,- typename TVertexAllocator,- typename TEdgeIter,- typename TGetEdgeVertexStart,- typename TGetEdgeVertexEnd,- typename TMakeEdgeFromStartAndEnd>-DuplicatesInfo RemoveDuplicatesAndRemapEdges(- std::vector<TVertex, TVertexAllocator>& vertices,- TGetVertexCoordX getX,- TGetVertexCoordY getY,- TEdgeIter edgesFirst,- TEdgeIter edgesLast,- TGetEdgeVertexStart getStart,- TGetEdgeVertexEnd getEnd,- TMakeEdgeFromStartAndEnd makeEdge);--/**- * Same as a chained call of CDT::RemoveDuplicates + CDT::RemapEdges- *- * @tparam T type of vertex coordinates (e.g., float, double)- * @param[in, out] vertices collection of vertices to remove duplicates from- * @param[in,out] edges collection of edges to remap- */-template <typename T>-CDT_EXPORT DuplicatesInfo RemoveDuplicatesAndRemapEdges(- std::vector<V2d<T> >& vertices,- std::vector<Edge>& edges);--/**- * Extract all edges of triangles- *- * @param triangles triangles used to extract edges- * @return an unordered set of all edges of triangulation- */-CDT_EXPORT EdgeUSet extractEdgesFromTriangles(const TriangleVec& triangles);--/*!- * Converts piece->original_edges mapping to original_edge->pieces- * @param pieceToOriginals maps pieces to original edges- * @return mapping of original edges to pieces- */-CDT_EXPORT unordered_map<Edge, EdgeVec>-EdgeToPiecesMapping(const unordered_map<Edge, EdgeVec>& pieceToOriginals);--/*!- * Convert edge-to-pieces mapping into edge-to-split-vertices mapping- * @tparam T type of vertex coordinates (e.g., float, double)- * @param edgeToPieces edge-to-pieces mapping- * @param vertices vertex buffer- * @return mapping of edge-to-split-points.- * Split points are sorted from edge's start (v1) to end (v2)- */-template <typename T>-CDT_EXPORT unordered_map<Edge, std::vector<VertInd> > EdgeToSplitVertices(- const unordered_map<Edge, EdgeVec>& edgeToPieces,- const std::vector<V2d<T> >& vertices);--/// @}--/// @}--} // namespace CDT--//*****************************************************************************-// Implementations of template functionlity-//*****************************************************************************-// hash for CDT::V2d<T>-#ifdef CDT_CXX11_IS_SUPPORTED-namespace std-#else-namespace boost-#endif-{-template <typename T>-struct hash<CDT::V2d<T> >-{- size_t operator()(const CDT::V2d<T>& xy) const- {-#ifdef CDT_CXX11_IS_SUPPORTED- typedef std::hash<T> Hasher;-#else- typedef boost::hash<T> Hasher;-#endif- return Hasher()(xy.x) ^ Hasher()(xy.y);- }-};-} // namespace std--namespace CDT-{--//------// API-//------template <- typename T,- typename TVertexIter,- typename TGetVertexCoordX,- typename TGetVertexCoordY>-DuplicatesInfo FindDuplicates(- TVertexIter first,- TVertexIter last,- TGetVertexCoordX getX,- TGetVertexCoordY getY)-{- typedef unordered_map<V2d<T>, std::size_t> PosToIndex;- PosToIndex uniqueVerts;- const std::size_t verticesSize = std::distance(first, last);- DuplicatesInfo di = {- std::vector<std::size_t>(verticesSize), std::vector<std::size_t>()};- for(std::size_t iIn = 0, iOut = iIn; iIn < verticesSize; ++iIn, ++first)- {- typename PosToIndex::const_iterator it;- bool isUnique;- tie(it, isUnique) = uniqueVerts.insert(- std::make_pair(V2d<T>::make(getX(*first), getY(*first)), iOut));- if(isUnique)- {- di.mapping[iIn] = iOut++;- continue;- }- di.mapping[iIn] = it->second; // found a duplicate- di.duplicates.push_back(iIn);- }- return di;-}--template <typename TVertex, typename TAllocator>-void RemoveDuplicates(- std::vector<TVertex, TAllocator>& vertices,- const std::vector<std::size_t>& duplicates)-{- vertices.erase(- remove_at(- vertices.begin(),- vertices.end(),- duplicates.begin(),- duplicates.end()),- vertices.end());-}--template <- typename TEdgeIter,- typename TGetEdgeVertexStart,- typename TGetEdgeVertexEnd,- typename TMakeEdgeFromStartAndEnd>-void RemapEdges(- TEdgeIter first,- const TEdgeIter last,- const std::vector<std::size_t>& mapping,- TGetEdgeVertexStart getStart,- TGetEdgeVertexEnd getEnd,- TMakeEdgeFromStartAndEnd makeEdge)-{- for(; first != last; ++first)- {- *first = makeEdge(- static_cast<VertInd>(mapping[getStart(*first)]),- static_cast<VertInd>(mapping[getEnd(*first)]));- }-}--template <- typename T,- typename TVertex,- typename TGetVertexCoordX,- typename TGetVertexCoordY,- typename TVertexAllocator,- typename TEdgeIter,- typename TGetEdgeVertexStart,- typename TGetEdgeVertexEnd,- typename TMakeEdgeFromStartAndEnd>-DuplicatesInfo RemoveDuplicatesAndRemapEdges(- std::vector<TVertex, TVertexAllocator>& vertices,- TGetVertexCoordX getX,- TGetVertexCoordY getY,- const TEdgeIter edgesFirst,- const TEdgeIter edgesLast,- TGetEdgeVertexStart getStart,- TGetEdgeVertexEnd getEnd,- TMakeEdgeFromStartAndEnd makeEdge)-{- const DuplicatesInfo di =- FindDuplicates<T>(vertices.begin(), vertices.end(), getX, getY);- RemoveDuplicates(vertices, di.duplicates);- RemapEdges(edgesFirst, edgesLast, di.mapping, getStart, getEnd, makeEdge);- return di;-}--template <typename T>-unordered_map<Edge, std::vector<VertInd> > EdgeToSplitVertices(- const unordered_map<Edge, EdgeVec>& edgeToPieces,- const std::vector<V2d<T> >& vertices)-{- typedef std::pair<VertInd, T> VertCoordPair;- struct ComparePred- {- bool operator()(const VertCoordPair& a, const VertCoordPair& b) const- {- return a.second < b.second;- }- } comparePred;-- unordered_map<Edge, std::vector<VertInd> > edgeToSplitVerts;- typedef unordered_map<Edge, EdgeVec>::const_iterator It;- for(It it = edgeToPieces.begin(); it != edgeToPieces.end(); ++it)- {- const Edge& e = it->first;- const T dX = vertices[e.v2()].x - vertices[e.v1()].x;- const T dY = vertices[e.v2()].y - vertices[e.v1()].y;- const bool isX = std::abs(dX) >= std::abs(dY); // X-coord longer- const bool isAscending =- isX ? dX >= 0 : dY >= 0; // Longer coordinate ascends- const EdgeVec& pieces = it->second;- std::vector<VertCoordPair> splitVerts;- // size is: 2[ends] + (pieces - 1)[split vertices] = pieces + 1- splitVerts.reserve(pieces.size() + 1);- typedef EdgeVec::const_iterator EIt;- for(EIt it = pieces.begin(); it != pieces.end(); ++it)- {- const array<VertInd, 2> vv = {it->v1(), it->v2()};- typedef array<VertInd, 2>::const_iterator VIt;- for(VIt v = vv.begin(); v != vv.end(); ++v)- {- const T c = isX ? vertices[*v].x : vertices[*v].y;- splitVerts.push_back(std::make_pair(*v, isAscending ? c : -c));- }- }- // sort by longest coordinate- std::sort(splitVerts.begin(), splitVerts.end(), comparePred);- // remove duplicates- splitVerts.erase(- std::unique(splitVerts.begin(), splitVerts.end()),- splitVerts.end());- assert(splitVerts.size() > 2); // 2 end points with split vertices- std::pair<Edge, std::vector<VertInd> > val =- std::make_pair(e, std::vector<VertInd>());- val.second.reserve(splitVerts.size());- typedef typename std::vector<VertCoordPair>::const_iterator SEIt;- for(SEIt it = splitVerts.begin() + 1; it != splitVerts.end() - 1; ++it)- {- val.second.push_back(it->first);- }- edgeToSplitVerts.insert(val);- }- return edgeToSplitVerts;-}--} // namespace CDT--#ifndef CDT_USE_AS_COMPILED_LIBRARY-#include "CDT.hpp"-#endif--#endif // header-guard+/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +/** + * @file + * Public API + */ + +#ifndef CDT_lNrmUayWQaIR5fxnsg9B +#define CDT_lNrmUayWQaIR5fxnsg9B + +#include "CDTUtils.h" +#include "Triangulation.h" + +#include "remove_at.hpp" + +#include <algorithm> +#include <cassert> +#include <cstdlib> +#include <iterator> +#include <memory> +#include <stack> +#include <vector> + +/// Namespace containing triangulation functionality +namespace CDT +{ + +/** @defgroup API Public API + * Contains API for constrained and conforming Delaunay triangulations + */ +/// @{ + +/** + * Type used for storing layer depths for triangles + * @note LayerDepth should support 60K+ layers, which could be to much or + * too little for some use cases. Feel free to re-define this typedef. + */ +typedef unsigned short LayerDepth; +typedef LayerDepth BoundaryOverlapCount; + +/// Triangles by vertex index +typedef std::vector<TriIndVec> VerticesTriangles; + +/** @defgroup helpers Helpers + * Helpers for working with CDT::Triangulation. + */ +/// @{ + +/** + * Calculate triangles adjacent to vertices (triangles by vertex index) + * @param triangles triangulation + * @param verticesSize total number of vertices to pre-allocate the output + * @return triangles by vertex index + */ +CDT_EXPORT VerticesTriangles +calculateTrianglesByVertex(const TriangleVec& triangles, VertInd verticesSize); + +/** + * Information about removed duplicated vertices. + * + * Contains mapping information and removed duplicates indices. + * @note vertices {0,1,2,3,4} where 0 and 3 are the same will produce mapping + * {0,1,2,0,3} (to new vertices {0,1,2,3}) and duplicates {3} + */ +struct CDT_EXPORT DuplicatesInfo +{ + std::vector<std::size_t> mapping; ///< vertex index mapping + std::vector<std::size_t> duplicates; ///< duplicates' indices +}; + +/** + * Find duplicates in given custom point-type range + * @note duplicates are points with exactly same X and Y coordinates + * @tparam TVertexIter iterator that dereferences to custom point type + * @tparam TGetVertexCoordX function object getting x coordinate from vertex. + * Getter signature: const TVertexIter::value_type& -> T + * @tparam TGetVertexCoordY function object getting y coordinate from vertex. + * Getter signature: const TVertexIter::value_type& -> T + * @param first beginning of the range of vertices + * @param last end of the range of vertices + * @param getX getter of X-coordinate + * @param getY getter of Y-coordinate + * @returns information about vertex duplicates + */ +template < + typename T, + typename TVertexIter, + typename TGetVertexCoordX, + typename TGetVertexCoordY> +DuplicatesInfo FindDuplicates( + TVertexIter first, + TVertexIter last, + TGetVertexCoordX getX, + TGetVertexCoordY getY); + +/** + * Remove duplicates in-place from vector of custom points + * @tparam TVertex vertex type + * @tparam TAllocator allocator used by input vector of vertices + * @param vertices vertices to remove duplicates from + * @param duplicates information about duplicates + */ +template <typename TVertex, typename TAllocator> +void RemoveDuplicates( + std::vector<TVertex, TAllocator>& vertices, + const std::vector<std::size_t>& duplicates); + +/** + * Remove duplicated points in-place + * + * @tparam T type of vertex coordinates (e.g., float, double) + * @param[in, out] vertices collection of vertices to remove duplicates from + * @returns information about duplicated vertices that were removed. + */ +template <typename T> +CDT_EXPORT DuplicatesInfo RemoveDuplicates(std::vector<V2d<T> >& vertices); + +/** + * Remap vertex indices in edges (in-place) using given vertex-index mapping. + * @tparam TEdgeIter iterator that dereferences to custom edge type + * @tparam TGetEdgeVertexStart function object getting start vertex index + * from an edge. + * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd + * @tparam TGetEdgeVertexEnd function object getting end vertex index from + * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd + * @tparam TMakeEdgeFromStartAndEnd function object that makes new edge from + * start and end vertices + * @param first beginning of the range of edges + * @param last end of the range of edges + * @param mapping vertex-index mapping + * @param getStart getter of edge start vertex index + * @param getEnd getter of edge end vertex index + * @param makeEdge factory for making edge from vetices + */ +template < + typename TEdgeIter, + typename TGetEdgeVertexStart, + typename TGetEdgeVertexEnd, + typename TMakeEdgeFromStartAndEnd> +CDT_EXPORT void RemapEdges( + TEdgeIter first, + TEdgeIter last, + const std::vector<std::size_t>& mapping, + TGetEdgeVertexStart getStart, + TGetEdgeVertexEnd getEnd, + TMakeEdgeFromStartAndEnd makeEdge); + +/** + * Remap vertex indices in edges (in-place) using given vertex-index mapping. + * + * @note Mapping can be a result of RemoveDuplicates function + * @param[in,out] edges collection of edges to remap + * @param mapping vertex-index mapping + */ +CDT_EXPORT void +RemapEdges(std::vector<Edge>& edges, const std::vector<std::size_t>& mapping); + +/** + * Find point duplicates, remove them from vector (in-place) and remap edges + * (in-place) + * @note Same as a chained call of CDT::FindDuplicates, CDT::RemoveDuplicates, + * and CDT::RemapEdges + * @tparam T type of vertex coordinates (e.g., float, double) + * @tparam TVertex type of vertex + * @tparam TGetVertexCoordX function object getting x coordinate from vertex. + * Getter signature: const TVertexIter::value_type& -> T + * @tparam TGetVertexCoordY function object getting y coordinate from vertex. + * Getter signature: const TVertexIter::value_type& -> T + * @tparam TEdgeIter iterator that dereferences to custom edge type + * @tparam TGetEdgeVertexStart function object getting start vertex index + * from an edge. + * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd + * @tparam TGetEdgeVertexEnd function object getting end vertex index from + * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd + * @tparam TMakeEdgeFromStartAndEnd function object that makes new edge from + * start and end vertices + * @param[in, out] vertices vertices to remove duplicates from + * @param[in, out] edges collection of edges connecting vertices + * @param getX getter of X-coordinate + * @param getY getter of Y-coordinate + * @param edgesFirst beginning of the range of edges + * @param edgesLast end of the range of edges + * @param getStart getter of edge start vertex index + * @param getEnd getter of edge end vertex index + * @param makeEdge factory for making edge from vetices + * @returns information about vertex duplicates + */ +template < + typename T, + typename TVertex, + typename TGetVertexCoordX, + typename TGetVertexCoordY, + typename TVertexAllocator, + typename TEdgeIter, + typename TGetEdgeVertexStart, + typename TGetEdgeVertexEnd, + typename TMakeEdgeFromStartAndEnd> +DuplicatesInfo RemoveDuplicatesAndRemapEdges( + std::vector<TVertex, TVertexAllocator>& vertices, + TGetVertexCoordX getX, + TGetVertexCoordY getY, + TEdgeIter edgesFirst, + TEdgeIter edgesLast, + TGetEdgeVertexStart getStart, + TGetEdgeVertexEnd getEnd, + TMakeEdgeFromStartAndEnd makeEdge); + +/** + * Same as a chained call of CDT::RemoveDuplicates + CDT::RemapEdges + * + * @tparam T type of vertex coordinates (e.g., float, double) + * @param[in, out] vertices collection of vertices to remove duplicates from + * @param[in,out] edges collection of edges to remap + */ +template <typename T> +CDT_EXPORT DuplicatesInfo RemoveDuplicatesAndRemapEdges( + std::vector<V2d<T> >& vertices, + std::vector<Edge>& edges); + +/** + * Extract all edges of triangles + * + * @param triangles triangles used to extract edges + * @return an unordered set of all edges of triangulation + */ +CDT_EXPORT EdgeUSet extractEdgesFromTriangles(const TriangleVec& triangles); + +/*! + * Converts piece->original_edges mapping to original_edge->pieces + * @param pieceToOriginals maps pieces to original edges + * @return mapping of original edges to pieces + */ +CDT_EXPORT unordered_map<Edge, EdgeVec> +EdgeToPiecesMapping(const unordered_map<Edge, EdgeVec>& pieceToOriginals); + +/*! + * Convert edge-to-pieces mapping into edge-to-split-vertices mapping + * @tparam T type of vertex coordinates (e.g., float, double) + * @param edgeToPieces edge-to-pieces mapping + * @param vertices vertex buffer + * @return mapping of edge-to-split-points. + * Split points are sorted from edge's start (v1) to end (v2) + */ +template <typename T> +CDT_EXPORT unordered_map<Edge, std::vector<VertInd> > EdgeToSplitVertices( + const unordered_map<Edge, EdgeVec>& edgeToPieces, + const std::vector<V2d<T> >& vertices); + +/// @} + +/// @} + +} // namespace CDT + +//***************************************************************************** +// Implementations of template functionlity +//***************************************************************************** +// hash for CDT::V2d<T> +#ifdef CDT_CXX11_IS_SUPPORTED +namespace std +#else +namespace boost +#endif +{ +template <typename T> +struct hash<CDT::V2d<T> > +{ + size_t operator()(const CDT::V2d<T>& xy) const + { +#ifdef CDT_CXX11_IS_SUPPORTED + typedef std::hash<T> Hasher; +#else + typedef boost::hash<T> Hasher; +#endif + return Hasher()(xy.x) ^ Hasher()(xy.y); + } +}; +} // namespace std + +namespace CDT +{ + +//----- +// API +//----- +template < + typename T, + typename TVertexIter, + typename TGetVertexCoordX, + typename TGetVertexCoordY> +DuplicatesInfo FindDuplicates( + TVertexIter first, + TVertexIter last, + TGetVertexCoordX getX, + TGetVertexCoordY getY) +{ + typedef unordered_map<V2d<T>, std::size_t> PosToIndex; + PosToIndex uniqueVerts; + const std::size_t verticesSize = std::distance(first, last); + DuplicatesInfo di = { + std::vector<std::size_t>(verticesSize), std::vector<std::size_t>()}; + for(std::size_t iIn = 0, iOut = iIn; iIn < verticesSize; ++iIn, ++first) + { + typename PosToIndex::const_iterator it; + bool isUnique; + tie(it, isUnique) = uniqueVerts.insert( + std::make_pair(V2d<T>::make(getX(*first), getY(*first)), iOut)); + if(isUnique) + { + di.mapping[iIn] = iOut++; + continue; + } + di.mapping[iIn] = it->second; // found a duplicate + di.duplicates.push_back(iIn); + } + return di; +} + +template <typename TVertex, typename TAllocator> +void RemoveDuplicates( + std::vector<TVertex, TAllocator>& vertices, + const std::vector<std::size_t>& duplicates) +{ + vertices.erase( + remove_at( + vertices.begin(), + vertices.end(), + duplicates.begin(), + duplicates.end()), + vertices.end()); +} + +template < + typename TEdgeIter, + typename TGetEdgeVertexStart, + typename TGetEdgeVertexEnd, + typename TMakeEdgeFromStartAndEnd> +void RemapEdges( + TEdgeIter first, + const TEdgeIter last, + const std::vector<std::size_t>& mapping, + TGetEdgeVertexStart getStart, + TGetEdgeVertexEnd getEnd, + TMakeEdgeFromStartAndEnd makeEdge) +{ + for(; first != last; ++first) + { + *first = makeEdge( + static_cast<VertInd>(mapping[getStart(*first)]), + static_cast<VertInd>(mapping[getEnd(*first)])); + } +} + +template < + typename T, + typename TVertex, + typename TGetVertexCoordX, + typename TGetVertexCoordY, + typename TVertexAllocator, + typename TEdgeIter, + typename TGetEdgeVertexStart, + typename TGetEdgeVertexEnd, + typename TMakeEdgeFromStartAndEnd> +DuplicatesInfo RemoveDuplicatesAndRemapEdges( + std::vector<TVertex, TVertexAllocator>& vertices, + TGetVertexCoordX getX, + TGetVertexCoordY getY, + const TEdgeIter edgesFirst, + const TEdgeIter edgesLast, + TGetEdgeVertexStart getStart, + TGetEdgeVertexEnd getEnd, + TMakeEdgeFromStartAndEnd makeEdge) +{ + const DuplicatesInfo di = + FindDuplicates<T>(vertices.begin(), vertices.end(), getX, getY); + RemoveDuplicates(vertices, di.duplicates); + RemapEdges(edgesFirst, edgesLast, di.mapping, getStart, getEnd, makeEdge); + return di; +} + +template <typename T> +unordered_map<Edge, std::vector<VertInd> > EdgeToSplitVertices( + const unordered_map<Edge, EdgeVec>& edgeToPieces, + const std::vector<V2d<T> >& vertices) +{ + typedef std::pair<VertInd, T> VertCoordPair; + struct ComparePred + { + bool operator()(const VertCoordPair& a, const VertCoordPair& b) const + { + return a.second < b.second; + } + } comparePred; + + unordered_map<Edge, std::vector<VertInd> > edgeToSplitVerts; + typedef unordered_map<Edge, EdgeVec>::const_iterator It; + for(It e2pIt = edgeToPieces.begin(); e2pIt != edgeToPieces.end(); ++e2pIt) + { + const Edge& e = e2pIt->first; + const T dX = vertices[e.v2()].x - vertices[e.v1()].x; + const T dY = vertices[e.v2()].y - vertices[e.v1()].y; + const bool isX = std::abs(dX) >= std::abs(dY); // X-coord longer + const bool isAscending = + isX ? dX >= 0 : dY >= 0; // Longer coordinate ascends + const EdgeVec& pieces = e2pIt->second; + std::vector<VertCoordPair> splitVerts; + // size is: 2[ends] + (pieces - 1)[split vertices] = pieces + 1 + splitVerts.reserve(pieces.size() + 1); + typedef EdgeVec::const_iterator EIt; + for(EIt pieceIt = pieces.begin(); pieceIt != pieces.end(); ++pieceIt) + { + const array<VertInd, 2> vv = {pieceIt->v1(), pieceIt->v2()}; + typedef array<VertInd, 2>::const_iterator VIt; + for(VIt v = vv.begin(); v != vv.end(); ++v) + { + const T c = isX ? vertices[*v].x : vertices[*v].y; + splitVerts.push_back(std::make_pair(*v, isAscending ? c : -c)); + } + } + // sort by longest coordinate + std::sort(splitVerts.begin(), splitVerts.end(), comparePred); + // remove duplicates + splitVerts.erase( + std::unique(splitVerts.begin(), splitVerts.end()), + splitVerts.end()); + assert(splitVerts.size() > 2); // 2 end points with split vertices + std::pair<Edge, std::vector<VertInd> > val = + std::make_pair(e, std::vector<VertInd>()); + val.second.reserve(splitVerts.size()); + typedef typename std::vector<VertCoordPair>::const_iterator SEIt; + for(SEIt it = splitVerts.begin() + 1; it != splitVerts.end() - 1; ++it) + { + val.second.push_back(it->first); + } + edgeToSplitVerts.insert(val); + } + return edgeToSplitVerts; +} + +} // namespace CDT + +#ifndef CDT_USE_AS_COMPILED_LIBRARY +#include "CDT.hpp" +#endif + +#endif // header-guard
cpp/CDT.hpp view
@@ -1,107 +1,107 @@-/* This Source Code Form is subject to the terms of the Mozilla Public- * License, v. 2.0. If a copy of the MPL was not distributed with this- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */--/**- * @file- * Public API - implementation- */--#include "CDT.h"--#include <algorithm>-#include <deque>-#include <limits>-#include <stdexcept>--namespace CDT-{--CDT_INLINE_IF_HEADER_ONLY VerticesTriangles calculateTrianglesByVertex(- const TriangleVec& triangles,- const VertInd verticesSize)-{- VerticesTriangles vertTris(verticesSize);- for(TriInd iT = 0; iT < triangles.size(); ++iT)- {- const VerticesArr3& vv = triangles[iT].vertices;- for(VerticesArr3::const_iterator v = vv.begin(); v != vv.end(); ++v)- {- vertTris[*v].push_back(iT);- }- }- return vertTris;-}--template <typename T>-DuplicatesInfo RemoveDuplicates(std::vector<V2d<T> >& vertices)-{- const DuplicatesInfo di = FindDuplicates<T>(- vertices.begin(), vertices.end(), getX_V2d<T>, getY_V2d<T>);- RemoveDuplicates(vertices, di.duplicates);- return di;-}--CDT_INLINE_IF_HEADER_ONLY void-RemapEdges(std::vector<Edge>& edges, const std::vector<std::size_t>& mapping)-{- RemapEdges(- edges.begin(),- edges.end(),- mapping,- edge_get_v1,- edge_get_v2,- edge_make);-}--template <typename T>-DuplicatesInfo RemoveDuplicatesAndRemapEdges(- std::vector<V2d<T> >& vertices,- std::vector<Edge>& edges)-{- return RemoveDuplicatesAndRemapEdges<T>(- vertices,- getX_V2d<T>,- getY_V2d<T>,- edges.begin(),- edges.end(),- edge_get_v1,- edge_get_v2,- edge_make);-}--CDT_INLINE_IF_HEADER_ONLY EdgeUSet-extractEdgesFromTriangles(const TriangleVec& triangles)-{- EdgeUSet edges;- typedef TriangleVec::const_iterator CIt;- for(CIt t = triangles.begin(); t != triangles.end(); ++t)- {- edges.insert(Edge(VertInd(t->vertices[0]), VertInd(t->vertices[1])));- edges.insert(Edge(VertInd(t->vertices[1]), VertInd(t->vertices[2])));- edges.insert(Edge(VertInd(t->vertices[2]), VertInd(t->vertices[0])));- }- return edges;-}--CDT_INLINE_IF_HEADER_ONLY unordered_map<Edge, EdgeVec>-EdgeToPiecesMapping(const unordered_map<Edge, EdgeVec>& pieceToOriginals)-{- unordered_map<Edge, EdgeVec> originalToPieces;- typedef unordered_map<Edge, EdgeVec>::const_iterator Cit;- for(Cit ptoIt = pieceToOriginals.begin(); ptoIt != pieceToOriginals.end();- ++ptoIt)- {- const Edge piece = ptoIt->first;- const EdgeVec& originals = ptoIt->second;- for(EdgeVec::const_iterator origIt = originals.begin();- origIt != originals.end();- ++origIt)- {- originalToPieces[*origIt].push_back(piece);- }- }- return originalToPieces;-}--} // namespace CDT+/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +/** + * @file + * Public API - implementation + */ + +#include "CDT.h" + +#include <algorithm> +#include <deque> +#include <limits> +#include <stdexcept> + +namespace CDT +{ + +CDT_INLINE_IF_HEADER_ONLY VerticesTriangles calculateTrianglesByVertex( + const TriangleVec& triangles, + const VertInd verticesSize) +{ + VerticesTriangles vertTris(verticesSize); + for(TriInd iT(0); iT < triangles.size(); ++iT) + { + const VerticesArr3& vv = triangles[iT].vertices; + for(VerticesArr3::const_iterator v = vv.begin(); v != vv.end(); ++v) + { + vertTris[*v].push_back(iT); + } + } + return vertTris; +} + +template <typename T> +DuplicatesInfo RemoveDuplicates(std::vector<V2d<T> >& vertices) +{ + const DuplicatesInfo di = FindDuplicates<T>( + vertices.begin(), vertices.end(), getX_V2d<T>, getY_V2d<T>); + RemoveDuplicates(vertices, di.duplicates); + return di; +} + +CDT_INLINE_IF_HEADER_ONLY void +RemapEdges(std::vector<Edge>& edges, const std::vector<std::size_t>& mapping) +{ + RemapEdges( + edges.begin(), + edges.end(), + mapping, + edge_get_v1, + edge_get_v2, + edge_make); +} + +template <typename T> +DuplicatesInfo RemoveDuplicatesAndRemapEdges( + std::vector<V2d<T> >& vertices, + std::vector<Edge>& edges) +{ + return RemoveDuplicatesAndRemapEdges<T>( + vertices, + getX_V2d<T>, + getY_V2d<T>, + edges.begin(), + edges.end(), + edge_get_v1, + edge_get_v2, + edge_make); +} + +CDT_INLINE_IF_HEADER_ONLY EdgeUSet +extractEdgesFromTriangles(const TriangleVec& triangles) +{ + EdgeUSet edges; + typedef TriangleVec::const_iterator CIt; + for(CIt t = triangles.begin(); t != triangles.end(); ++t) + { + edges.insert(Edge(VertInd(t->vertices[0]), VertInd(t->vertices[1]))); + edges.insert(Edge(VertInd(t->vertices[1]), VertInd(t->vertices[2]))); + edges.insert(Edge(VertInd(t->vertices[2]), VertInd(t->vertices[0]))); + } + return edges; +} + +CDT_INLINE_IF_HEADER_ONLY unordered_map<Edge, EdgeVec> +EdgeToPiecesMapping(const unordered_map<Edge, EdgeVec>& pieceToOriginals) +{ + unordered_map<Edge, EdgeVec> originalToPieces; + typedef unordered_map<Edge, EdgeVec>::const_iterator Cit; + for(Cit ptoIt = pieceToOriginals.begin(); ptoIt != pieceToOriginals.end(); + ++ptoIt) + { + const Edge piece = ptoIt->first; + const EdgeVec& originals = ptoIt->second; + for(EdgeVec::const_iterator origIt = originals.begin(); + origIt != originals.end(); + ++origIt) + { + originalToPieces[*origIt].push_back(piece); + } + } + return originalToPieces; +} + +} // namespace CDT
cpp/CDTUtils.h view
@@ -1,475 +1,507 @@-/* This Source Code Form is subject to the terms of the Mozilla Public- * License, v. 2.0. If a copy of the MPL was not distributed with this- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */--/**- * @file- * Utilities and helpers- */--#ifndef CDT_obwOaxOTdAWcLNTlNnaq-#define CDT_obwOaxOTdAWcLNTlNnaq--#ifdef CDT_DONT_USE_BOOST_RTREE-// CDT_DONT_USE_BOOST_RTREE was replaced with CDT_USE_BOOST-typedef char CDT_DONT_USE_BOOST_RTREE__was__replaced__with__CDT_USE_BOOST[-1];-#endif--// #define CDT_USE_STRONG_TYPING // strong type checks on indices--// check if c++11 is supported-#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)-#define CDT_CXX11_IS_SUPPORTED-#elif !defined(__cplusplus) && !defined(_MSC_VER)-typedef char couldnt_parse_cxx_standard[-1]; ///< Error: couldn't parse standard-#endif--// Functions defined outside the class need to be 'inline'-// if CDT is configured to be used as header-only library:-// single-definition rule is violated otherwise-#ifdef CDT_USE_AS_COMPILED_LIBRARY-#define CDT_INLINE_IF_HEADER_ONLY-#include "cdt_export.h" // automatically generated by CMake-#else-/**- * Macro for inlining non-template functions when in header-only mode to- * avoid multiple declaration errors.- */-#define CDT_INLINE_IF_HEADER_ONLY inline-/// Export not needed in header-only mode-#define CDT_EXPORT-#endif--#include <cassert>-#include <cmath>-#include <limits>-#include <vector>--#ifdef CDT_USE_BOOST-#include <boost/container/flat_set.hpp>-#endif--// use fall-backs for c++11 features-#ifdef CDT_CXX11_IS_SUPPORTED--#include <array>-#include <functional>-#include <random>-#include <tuple>-#include <unordered_map>-#include <unordered_set>-namespace CDT-{-using std::array;-using std::get;-using std::make_tuple;-using std::mt19937;-using std::tie;-using std::tuple;-using std::unordered_map;-using std::unordered_set;-} // namespace CDT--#else-#include <boost/array.hpp>-#include <boost/functional/hash.hpp>-#include <boost/random.hpp>-#include <boost/tuple/tuple.hpp>-#include <boost/unordered_map.hpp>-#include <boost/unordered_set.hpp>-namespace CDT-{-using boost::array;-using boost::get;-using boost::make_tuple;-using boost::tie;-using boost::tuple;-using boost::unordered_map;-using boost::unordered_set;-using boost::random::mt19937;-} // namespace CDT-#endif--namespace CDT-{--/// 2D vector-template <typename T>-struct CDT_EXPORT V2d-{- T x; ///< X-coordinate- T y; ///< Y-coordinate-- /// Create vector from X and Y coordinates- static V2d make(T x, T y);-};--/// X- coordinate getter for V2d-template <typename T>-const T& getX_V2d(const V2d<T>& v)-{- return v.x;-}--/// Y-coordinate getter for V2d-template <typename T>-const T& getY_V2d(const V2d<T>& v)-{- return v.y;-}--/// If two 2D vectors are exactly equal-template <typename T>-bool operator==(const CDT::V2d<T>& lhs, const CDT::V2d<T>& rhs)-{- return lhs.x == rhs.x && lhs.y == rhs.y;-}--#ifdef CDT_USE_64_BIT_INDEX_TYPE-typedef unsigned long long IndexSizeType;-#else-typedef unsigned int IndexSizeType;-#endif--#ifndef CDT_USE_STRONG_TYPING-/// Index in triangle-typedef unsigned char Index;-/// Vertex index-typedef IndexSizeType VertInd;-/// Triangle index-typedef IndexSizeType TriInd;-#else-/// Index in triangle-BOOST_STRONG_TYPEDEF(unsigned char, Index);-/// Vertex index-BOOST_STRONG_TYPEDEF(IndexSizeType, VertInd);-/// Triangle index-BOOST_STRONG_TYPEDEF(IndexSizeType, TriInd);-#endif--/// Constant representing no valid neighbor for a triangle-const static TriInd noNeighbor(std::numeric_limits<TriInd>::max());-/// Constant representing no valid vertex for a triangle-const static VertInd noVertex(std::numeric_limits<VertInd>::max());--typedef std::vector<TriInd> TriIndVec; ///< Vector of triangle indices-typedef array<VertInd, 3> VerticesArr3; ///< array of three vertex indices-typedef array<TriInd, 3> NeighborsArr3; ///< array of three neighbors--/// 2D bounding box-template <typename T>-struct CDT_EXPORT Box2d-{- V2d<T> min; ///< min box corner- V2d<T> max; ///< max box corner-- /// Envelop box around a point- void envelopPoint(const V2d<T>& p)- {- envelopPoint(p.x, p.y);- }- /// Envelop box around a point with given coordinates- void envelopPoint(const T x, const T y)- {- min.x = std::min(x, min.x);- max.x = std::max(x, max.x);- min.y = std::min(y, min.y);- max.y = std::max(y, max.y);- }-};--/// Bounding box of a collection of custom 2D points given coordinate getters-template <- typename T,- typename TVertexIter,- typename TGetVertexCoordX,- typename TGetVertexCoordY>-Box2d<T> envelopBox(- TVertexIter first,- TVertexIter last,- TGetVertexCoordX getX,- TGetVertexCoordY getY)-{- const T max = std::numeric_limits<T>::max();- Box2d<T> box = {{max, max}, {-max, -max}};- for(; first != last; ++first)- {- box.envelopPoint(getX(*first), getY(*first));- }- return box;-}--/// Bounding box of a collection of 2D points-template <typename T>-CDT_EXPORT Box2d<T> envelopBox(const std::vector<V2d<T> >& vertices);--/// Edge connecting two vertices: vertex with smaller index is always first-/// \note: hash Edge is specialized at the bottom-struct CDT_EXPORT Edge-{- /// Constructor- Edge(VertInd iV1, VertInd iV2);- /// Equals operator- bool operator==(const Edge& other) const;- /// Not-equals operator- bool operator!=(const Edge& other) const;- /// V1 getter- VertInd v1() const;- /// V2 getter- VertInd v2() const;- /// Edges' vertices- const std::pair<VertInd, VertInd>& verts() const;--private:- std::pair<VertInd, VertInd> m_vertices;-};--/// Get edge first vertex-inline VertInd edge_get_v1(const Edge& e)-{- return e.v1();-}--/// Get edge second vertex-inline VertInd edge_get_v2(const Edge& e)-{- return e.v2();-}--/// Get edge second vertex-inline Edge edge_make(VertInd iV1, VertInd iV2)-{- return Edge(iV1, iV2);-}--typedef std::vector<Edge> EdgeVec; ///< Vector of edges-typedef unordered_set<Edge> EdgeUSet; ///< Hash table of edges-typedef unordered_set<TriInd> TriIndUSet; ///< Hash table of triangles-typedef unordered_map<TriInd, TriInd> TriIndUMap; ///< Triangle hash map-#ifdef CDT_USE_BOOST-/// Flat hash table of triangles-typedef boost::container::flat_set<TriInd> TriIndFlatUSet;-#endif--/// Triangulation triangle (CCW winding)-/* Counter-clockwise winding:- v3- /\- n3/ \n2- /____\- v1 n1 v2 */-struct CDT_EXPORT Triangle-{- VerticesArr3 vertices; ///< triangle's three vertices- NeighborsArr3 neighbors; ///< triangle's three neighbors-- /**- * Factory method- * @note needed for c++03 compatibility (no uniform initialization- * available)- */- static Triangle- make(const array<VertInd, 3>& vertices, const array<TriInd, 3>& neighbors)- {- Triangle t = {vertices, neighbors};- return t;- }-};--typedef std::vector<Triangle> TriangleVec; ///< Vector of triangles--/// Advance vertex or neighbor index counter-clockwise-CDT_EXPORT Index ccw(Index i);--/// Advance vertex or neighbor index clockwise-CDT_EXPORT Index cw(Index i);--/// Location of point on a triangle-struct CDT_EXPORT PtTriLocation-{- /// Enum- enum Enum- {- Inside,- Outside,- OnEdge1,- OnEdge2,- OnEdge3,- };-};--/// Check if location is classified as on any of three edges-CDT_EXPORT bool isOnEdge(PtTriLocation::Enum location);--/// Neighbor index from a on-edge location-/// \note Call only if located on the edge!-CDT_EXPORT Index edgeNeighbor(PtTriLocation::Enum location);--/// Relative location of point to a line-struct CDT_EXPORT PtLineLocation-{- /// Enum- enum Enum- {- Left,- Right,- OnLine,- };-};--/// Orient p against line v1-v2 2D: robust geometric predicate-template <typename T>-CDT_EXPORT T orient2D(const V2d<T>& p, const V2d<T>& v1, const V2d<T>& v2);--/// Check if point lies to the left of, to the right of, or on a line-template <typename T>-CDT_EXPORT PtLineLocation::Enum locatePointLine(- const V2d<T>& p,- const V2d<T>& v1,- const V2d<T>& v2,- T orientationTolerance = T(0));--/// Classify value of orient2d predicate-template <typename T>-CDT_EXPORT PtLineLocation::Enum-classifyOrientation(T orientation, T orientationTolerance = T(0));--/// Check if point a lies inside of, outside of, or on an edge of a triangle-template <typename T>-CDT_EXPORT PtTriLocation::Enum locatePointTriangle(- const V2d<T>& p,- const V2d<T>& v1,- const V2d<T>& v2,- const V2d<T>& v3);--/// Opposed neighbor index from vertex index-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index opoNbr(Index vertIndex);--/// Opposed vertex index from neighbor index-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index opoVrt(Index neighborIndex);--/// Index of triangle's neighbor opposed to a vertex-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index-opposedTriangleInd(const Triangle& tri, VertInd iVert);--/// Index of triangle's neighbor opposed to an edge-CDT_INLINE_IF_HEADER_ONLY Index-opposedTriangleInd(const Triangle& tri, VertInd iVedge1, VertInd iVedge2);--/// Index of triangle's vertex opposed to a triangle-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index-opposedVertexInd(const Triangle& tri, TriInd iTopo);--/// If triangle has a given neighbor return neighbor-index, throw otherwise-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index-neighborInd(const Triangle& tri, TriInd iTnbr);--/// If triangle has a given vertex return vertex-index, throw otherwise-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index-vertexInd(const Triangle& tri, VertInd iV);--/// Given triangle and a vertex find opposed triangle-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY TriInd-opposedTriangle(const Triangle& tri, VertInd iVert);--/// Given two triangles, return vertex of first triangle opposed to the second-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY VertInd-opposedVertex(const Triangle& tri, TriInd iTopo);--/// Test if point lies in a circumscribed circle of a triangle-template <typename T>-CDT_EXPORT bool isInCircumcircle(- const V2d<T>& p,- const V2d<T>& v1,- const V2d<T>& v2,- const V2d<T>& v3);--/// Test if two vertices share at least one common triangle-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY bool-verticesShareEdge(const TriIndVec& aTris, const TriIndVec& bTris);--/// Distance between two 2D points-template <typename T>-CDT_EXPORT T distance(const V2d<T>& a, const V2d<T>& b);--/// Squared distance between two 2D points-template <typename T>-CDT_EXPORT T distanceSquared(const V2d<T>& a, const V2d<T>& b);--} // namespace CDT--#ifndef CDT_USE_AS_COMPILED_LIBRARY-#include "CDTUtils.hpp"-#endif--//*****************************************************************************-// Specialize hash functions-//*****************************************************************************-#ifdef CDT_CXX11_IS_SUPPORTED-namespace std-#else-namespace boost-#endif-{--#ifdef CDT_USE_STRONG_TYPING--/// Vertex index hasher-template <>-struct hash<CDT::VertInd>-{- /// Hash operator- std::size_t operator()(const CDT::VertInd& vi) const- {- return std::hash<std::size_t>()(vi.t);- }-};--/// Triangle index hasher-template <>-struct hash<CDT::TriInd>-{- /// Hash operator- std::size_t operator()(const CDT::TriInd& vi) const- {- return std::hash<std::size_t>()(vi.t);- }-};--#endif // CDT_USE_STRONG_TYPING--/// Edge hasher-template <>-struct hash<CDT::Edge>-{- /// Hash operator- std::size_t operator()(const CDT::Edge& e) const- {- return hashEdge(e);- }--private:- static void hashCombine(std::size_t& seed, const CDT::VertInd& key)- {-#ifdef CDT_CXX11_IS_SUPPORTED- typedef std::hash<CDT::VertInd> Hasher;-#else- typedef boost::hash<CDT::VertInd> Hasher;-#endif- seed ^= Hasher()(key) + 0x9e3779b9 + (seed << 6) + (seed >> 2);- }- static std::size_t hashEdge(const CDT::Edge& e)- {- const std::pair<CDT::VertInd, CDT::VertInd>& vv = e.verts();- std::size_t seed1(0);- hashCombine(seed1, vv.first);- hashCombine(seed1, vv.second);- std::size_t seed2(0);- hashCombine(seed2, vv.second);- hashCombine(seed2, vv.first);- return std::min(seed1, seed2);- }-};-} // namespace std/boost--#endif // header guard+/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +/** + * @file + * Utilities and helpers + */ + +#ifndef CDT_obwOaxOTdAWcLNTlNnaq +#define CDT_obwOaxOTdAWcLNTlNnaq + +#ifdef CDT_DONT_USE_BOOST_RTREE +// CDT_DONT_USE_BOOST_RTREE was replaced with CDT_USE_BOOST +typedef char CDT_DONT_USE_BOOST_RTREE__was__replaced__with__CDT_USE_BOOST[-1]; +#endif + +// #define CDT_USE_STRONG_TYPING // strong type checks on indices + +// check if c++11 is supported +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) +#define CDT_CXX11_IS_SUPPORTED +#elif !defined(__cplusplus) && !defined(_MSC_VER) +typedef char couldnt_parse_cxx_standard[-1]; ///< Error: couldn't parse standard +#endif + +// Functions defined outside the class need to be 'inline' +// if CDT is configured to be used as header-only library: +// single-definition rule is violated otherwise +#ifdef CDT_USE_AS_COMPILED_LIBRARY +#define CDT_INLINE_IF_HEADER_ONLY +#include "cdt_export.h" // automatically generated by CMake +#else +/** + * Macro for inlining non-template functions when in header-only mode to + * avoid multiple declaration errors. + */ +#define CDT_INLINE_IF_HEADER_ONLY inline +/// Export not needed in header-only mode +#define CDT_EXPORT +#endif + +#include <algorithm> +#include <cassert> +#include <cmath> +#include <limits> +#include <vector> + +#ifdef CDT_USE_STRONG_TYPING +#include <boost/serialization/strong_typedef.hpp> +#endif + +// use fall-backs for c++11 features +#ifdef CDT_CXX11_IS_SUPPORTED + +#include <array> +#include <functional> +#include <tuple> +#include <unordered_map> +#include <unordered_set> +namespace CDT +{ +using std::array; +using std::get; +using std::make_tuple; +using std::tie; +using std::tuple; +using std::unordered_map; +using std::unordered_set; +} // namespace CDT + +#else +#include <boost/array.hpp> +#include <boost/functional/hash.hpp> +#include <boost/tuple/tuple.hpp> +#include <boost/unordered_map.hpp> +#include <boost/unordered_set.hpp> +namespace CDT +{ +using boost::array; +using boost::get; +using boost::make_tuple; +using boost::tie; +using boost::tuple; +using boost::unordered_map; +using boost::unordered_set; +} // namespace CDT +#endif + +namespace CDT +{ + +/// 2D vector +template <typename T> +struct CDT_EXPORT V2d +{ + T x; ///< X-coordinate + T y; ///< Y-coordinate + + /// Create vector from X and Y coordinates + static V2d make(T x, T y); +}; + +/// X- coordinate getter for V2d +template <typename T> +const T& getX_V2d(const V2d<T>& v) +{ + return v.x; +} + +/// Y-coordinate getter for V2d +template <typename T> +const T& getY_V2d(const V2d<T>& v) +{ + return v.y; +} + +/// If two 2D vectors are exactly equal +template <typename T> +bool operator==(const CDT::V2d<T>& lhs, const CDT::V2d<T>& rhs) +{ + return lhs.x == rhs.x && lhs.y == rhs.y; +} + +#ifdef CDT_USE_64_BIT_INDEX_TYPE +typedef unsigned long long IndexSizeType; +#else +typedef unsigned int IndexSizeType; +#endif + +#ifdef CDT_USE_STRONG_TYPING +/// Index in triangle +BOOST_STRONG_TYPEDEF(unsigned char, Index); +/// Vertex index +BOOST_STRONG_TYPEDEF(IndexSizeType, VertInd); +/// Triangle index +BOOST_STRONG_TYPEDEF(IndexSizeType, TriInd); +#else +/// Index in triangle +typedef unsigned char Index; +/// Vertex index +typedef IndexSizeType VertInd; +/// Triangle index +typedef IndexSizeType TriInd; +#endif + +/// Constant representing no valid value for index +const static IndexSizeType + invalidIndex(std::numeric_limits<IndexSizeType>::max()); +/// Constant representing no valid neighbor for a triangle +const static TriInd noNeighbor(invalidIndex); +/// Constant representing no valid vertex for a triangle +const static VertInd noVertex(invalidIndex); + +typedef std::vector<TriInd> TriIndVec; ///< Vector of triangle indices +typedef array<VertInd, 3> VerticesArr3; ///< array of three vertex indices +typedef array<TriInd, 3> NeighborsArr3; ///< array of three neighbors + +/// 2D bounding box +template <typename T> +struct CDT_EXPORT Box2d +{ + V2d<T> min; ///< min box corner + V2d<T> max; ///< max box corner + + /// Envelop box around a point + void envelopPoint(const V2d<T>& p) + { + envelopPoint(p.x, p.y); + } + /// Envelop box around a point with given coordinates + void envelopPoint(const T x, const T y) + { + min.x = std::min(x, min.x); + max.x = std::max(x, max.x); + min.y = std::min(y, min.y); + max.y = std::max(y, max.y); + } +}; + +/// Bounding box of a collection of custom 2D points given coordinate getters +template < + typename T, + typename TVertexIter, + typename TGetVertexCoordX, + typename TGetVertexCoordY> +Box2d<T> envelopBox( + TVertexIter first, + TVertexIter last, + TGetVertexCoordX getX, + TGetVertexCoordY getY) +{ + const T max = std::numeric_limits<T>::max(); + Box2d<T> box = {{max, max}, {-max, -max}}; + for(; first != last; ++first) + { + box.envelopPoint(getX(*first), getY(*first)); + } + return box; +} + +/// Bounding box of a collection of 2D points +template <typename T> +CDT_EXPORT Box2d<T> envelopBox(const std::vector<V2d<T> >& vertices); + +/// Edge connecting two vertices: vertex with smaller index is always first +/// \note: hash Edge is specialized at the bottom +struct CDT_EXPORT Edge +{ + /// Constructor + Edge(VertInd iV1, VertInd iV2); + /// Equals operator + bool operator==(const Edge& other) const; + /// Not-equals operator + bool operator!=(const Edge& other) const; + /// V1 getter + VertInd v1() const; + /// V2 getter + VertInd v2() const; + /// Edges' vertices + const std::pair<VertInd, VertInd>& verts() const; + +private: + std::pair<VertInd, VertInd> m_vertices; +}; + +/// Get edge first vertex +inline VertInd edge_get_v1(const Edge& e) +{ + return e.v1(); +} + +/// Get edge second vertex +inline VertInd edge_get_v2(const Edge& e) +{ + return e.v2(); +} + +/// Get edge second vertex +inline Edge edge_make(VertInd iV1, VertInd iV2) +{ + return Edge(iV1, iV2); +} + +typedef std::vector<Edge> EdgeVec; ///< Vector of edges +typedef unordered_set<Edge> EdgeUSet; ///< Hash table of edges +typedef unordered_set<TriInd> TriIndUSet; ///< Hash table of triangles +typedef unordered_map<TriInd, TriInd> TriIndUMap; ///< Triangle hash map + +/// Triangulation triangle (counter-clockwise winding) +/* + * v3 + * /\ + * n3/ \n2 + * /____\ + * v1 n1 v2 + */ +struct CDT_EXPORT Triangle +{ + VerticesArr3 vertices; ///< triangle's three vertices + NeighborsArr3 neighbors; ///< triangle's three neighbors + + /** + * Factory method + * @note needed for c++03 compatibility (no uniform initialization + * available) + */ + static Triangle + make(const array<VertInd, 3>& vertices, const array<TriInd, 3>& neighbors) + { + Triangle t = {vertices, neighbors}; + return t; + } + + /// Next triangle adjacent to a vertex (clockwise) + /// @returns pair of next triangle and the other vertex of a common edge + std::pair<TriInd, VertInd> next(const VertInd i) const + { + assert(vertices[0] == i || vertices[1] == i || vertices[2] == i); + if(vertices[0] == i) + { + return std::make_pair(neighbors[0], vertices[1]); + } + if(vertices[1] == i) + { + return std::make_pair(neighbors[1], vertices[2]); + } + return std::make_pair(neighbors[2], vertices[0]); + } + /// Previous triangle adjacent to a vertex (counter-clockwise) + /// @returns pair of previous triangle and the other vertex of a common edge + std::pair<TriInd, VertInd> prev(const VertInd i) const + { + assert(vertices[0] == i || vertices[1] == i || vertices[2] == i); + if(vertices[0] == i) + return std::make_pair(neighbors[2], vertices[2]); + if(vertices[1] == i) + return std::make_pair(neighbors[0], vertices[0]); + return std::make_pair(neighbors[1], vertices[1]); + } + + bool containsVertex(const VertInd i) const + { + return std::find(vertices.begin(), vertices.end(), i) != vertices.end(); + } +}; + +typedef std::vector<Triangle> TriangleVec; ///< Vector of triangles + +/// Advance vertex or neighbor index counter-clockwise +CDT_EXPORT Index ccw(Index i); + +/// Advance vertex or neighbor index clockwise +CDT_EXPORT Index cw(Index i); + +/// Location of point on a triangle +struct CDT_EXPORT PtTriLocation +{ + /// Enum + enum Enum + { + Inside, + Outside, + OnEdge1, + OnEdge2, + OnEdge3, + }; +}; + +/// Check if location is classified as on any of three edges +CDT_EXPORT bool isOnEdge(PtTriLocation::Enum location); + +/// Neighbor index from a on-edge location +/// \note Call only if located on the edge! +CDT_EXPORT Index edgeNeighbor(PtTriLocation::Enum location); + +/// Relative location of point to a line +struct CDT_EXPORT PtLineLocation +{ + /// Enum + enum Enum + { + Left, + Right, + OnLine, + }; +}; + +/// Orient p against line v1-v2 2D: robust geometric predicate +template <typename T> +CDT_EXPORT T orient2D(const V2d<T>& p, const V2d<T>& v1, const V2d<T>& v2); + +/// Check if point lies to the left of, to the right of, or on a line +template <typename T> +CDT_EXPORT PtLineLocation::Enum locatePointLine( + const V2d<T>& p, + const V2d<T>& v1, + const V2d<T>& v2, + T orientationTolerance = T(0)); + +/// Classify value of orient2d predicate +template <typename T> +CDT_EXPORT PtLineLocation::Enum +classifyOrientation(T orientation, T orientationTolerance = T(0)); + +/// Check if point a lies inside of, outside of, or on an edge of a triangle +template <typename T> +CDT_EXPORT PtTriLocation::Enum locatePointTriangle( + const V2d<T>& p, + const V2d<T>& v1, + const V2d<T>& v2, + const V2d<T>& v3); + +/// Opposed neighbor index from vertex index +CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index opoNbr(Index vertIndex); + +/// Opposed vertex index from neighbor index +CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index opoVrt(Index neighborIndex); + +/// Index of triangle's neighbor opposed to a vertex +CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index +opposedTriangleInd(const VerticesArr3& vv, VertInd iVert); + +/// Index of triangle's neighbor opposed to an edge +CDT_INLINE_IF_HEADER_ONLY Index +edgeNeighborInd(const VerticesArr3& vv, VertInd iVedge1, VertInd iVedge2); + +/// Index of triangle's vertex opposed to a triangle +CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index +opposedVertexInd(const NeighborsArr3& nn, TriInd iTopo); + +/// If triangle has a given vertex return vertex-index +CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index +vertexInd(const VerticesArr3& vv, VertInd iV); + +/// Given triangle and a vertex find opposed triangle +CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY TriInd +opposedTriangle(const Triangle& tri, VertInd iVert); + +/// Given triangle and an edge find neighbor sharing the edge +CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY TriInd +edgeNeighbor(const Triangle& tri, VertInd iVedge1, VertInd iVedge2); + +/// Given two triangles, return vertex of first triangle opposed to the second +CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY VertInd +opposedVertex(const Triangle& tri, TriInd iTopo); + +/// Test if point lies in a circumscribed circle of a triangle +template <typename T> +CDT_EXPORT bool isInCircumcircle( + const V2d<T>& p, + const V2d<T>& v1, + const V2d<T>& v2, + const V2d<T>& v3); + +/// Test if two vertices share at least one common triangle +CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY bool +verticesShareEdge(const TriIndVec& aTris, const TriIndVec& bTris); + +/// Distance between two 2D points +template <typename T> +CDT_EXPORT T distance(const V2d<T>& a, const V2d<T>& b); + +/// Squared distance between two 2D points +template <typename T> +CDT_EXPORT T distanceSquared(const V2d<T>& a, const V2d<T>& b); + +/// Check if any of triangle's vertices belongs to a super-triangle +CDT_INLINE_IF_HEADER_ONLY bool touchesSuperTriangle(const Triangle& t); + +} // namespace CDT + +#ifndef CDT_USE_AS_COMPILED_LIBRARY +#include "CDTUtils.hpp" +#endif + +//***************************************************************************** +// Specialize hash functions +//***************************************************************************** +#ifdef CDT_CXX11_IS_SUPPORTED +namespace std +#else +namespace boost +#endif +{ + +#ifdef CDT_USE_STRONG_TYPING + +/// Vertex index hasher +template <> +struct hash<CDT::VertInd> +{ + /// Hash operator + std::size_t operator()(const CDT::VertInd& vi) const + { + return std::hash<std::size_t>()(vi.t); + } +}; + +/// Triangle index hasher +template <> +struct hash<CDT::TriInd> +{ + /// Hash operator + std::size_t operator()(const CDT::TriInd& vi) const + { + return std::hash<std::size_t>()(vi.t); + } +}; + +#endif // CDT_USE_STRONG_TYPING + +/// Edge hasher +template <> +struct hash<CDT::Edge> +{ + /// Hash operator + std::size_t operator()(const CDT::Edge& e) const + { + return hashEdge(e); + } + +private: + static void hashCombine(std::size_t& seed, const CDT::VertInd& key) + { +#ifdef CDT_CXX11_IS_SUPPORTED + typedef std::hash<CDT::VertInd> Hasher; +#else + typedef boost::hash<CDT::VertInd> Hasher; +#endif + seed ^= Hasher()(key) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } + static std::size_t hashEdge(const CDT::Edge& e) + { + const std::pair<CDT::VertInd, CDT::VertInd>& vv = e.verts(); + std::size_t seed1(0); + hashCombine(seed1, vv.first); + hashCombine(seed1, vv.second); + std::size_t seed2(0); + hashCombine(seed2, vv.second); + hashCombine(seed2, vv.first); + return std::min(seed1, seed2); + } +}; +} // namespace std/boost + +#endif // header guard
cpp/CDTUtils.hpp view
@@ -1,279 +1,308 @@-/* This Source Code Form is subject to the terms of the Mozilla Public- * License, v. 2.0. If a copy of the MPL was not distributed with this- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */--/**- * @file- * Utilities and helpers - implementation- */--#include "CDTUtils.h"--#include "predicates.h" // robust predicates: orient, in-circle--#include <stdexcept>--namespace CDT-{--//*****************************************************************************-// V2d-//*****************************************************************************-template <typename T>-V2d<T> V2d<T>::make(const T x, const T y)-{- V2d<T> out = {x, y};- return out;-}--//*****************************************************************************-// Box2d-//*****************************************************************************-template <typename T>-Box2d<T> envelopBox(const std::vector<V2d<T> >& vertices)-{- return envelopBox<T>(- vertices.begin(), vertices.end(), getX_V2d<T>, getY_V2d<T>);-}--//*****************************************************************************-// Edge-//*****************************************************************************-CDT_INLINE_IF_HEADER_ONLY Edge::Edge(VertInd iV1, VertInd iV2)- : m_vertices(- iV1 < iV2 ? std::make_pair(iV1, iV2) : std::make_pair(iV2, iV1))-{}--CDT_INLINE_IF_HEADER_ONLY bool Edge::operator==(const Edge& other) const-{- return m_vertices == other.m_vertices;-}--CDT_INLINE_IF_HEADER_ONLY bool Edge::operator!=(const Edge& other) const-{- return !(this->operator==(other));-}--CDT_INLINE_IF_HEADER_ONLY VertInd Edge::v1() const-{- return m_vertices.first;-}--CDT_INLINE_IF_HEADER_ONLY VertInd Edge::v2() const-{- return m_vertices.second;-}--CDT_INLINE_IF_HEADER_ONLY const std::pair<VertInd, VertInd>& Edge::verts() const-{- return m_vertices;-}--//*****************************************************************************-// Utility functions-//*****************************************************************************-CDT_INLINE_IF_HEADER_ONLY Index ccw(Index i)-{- return Index((i + 1) % 3);-}--CDT_INLINE_IF_HEADER_ONLY Index cw(Index i)-{- return Index((i + 2) % 3);-}--CDT_INLINE_IF_HEADER_ONLY bool isOnEdge(const PtTriLocation::Enum location)-{- return location > PtTriLocation::Outside;-}--CDT_INLINE_IF_HEADER_ONLY Index edgeNeighbor(const PtTriLocation::Enum location)-{- assert(location >= PtTriLocation::OnEdge1);- return static_cast<Index>(location - PtTriLocation::OnEdge1);-}--template <typename T>-T orient2D(const V2d<T>& p, const V2d<T>& v1, const V2d<T>& v2)-{- return predicates::adaptive::orient2d(v1.x, v1.y, v2.x, v2.y, p.x, p.y);-}--template <typename T>-PtLineLocation::Enum locatePointLine(- const V2d<T>& p,- const V2d<T>& v1,- const V2d<T>& v2,- const T orientationTolerance)-{- return classifyOrientation(orient2D(p, v1, v2), orientationTolerance);-}--template <typename T>-PtLineLocation::Enum-classifyOrientation(const T orientation, const T orientationTolerance)-{- if(orientation < -orientationTolerance)- return PtLineLocation::Right;- if(orientation > orientationTolerance)- return PtLineLocation::Left;- return PtLineLocation::OnLine;-}--template <typename T>-PtTriLocation::Enum locatePointTriangle(- const V2d<T>& p,- const V2d<T>& v1,- const V2d<T>& v2,- const V2d<T>& v3)-{- using namespace predicates::adaptive;- PtTriLocation::Enum result = PtTriLocation::Inside;- PtLineLocation::Enum edgeCheck = locatePointLine(p, v1, v2);- if(edgeCheck == PtLineLocation::Right)- return PtTriLocation::Outside;- if(edgeCheck == PtLineLocation::OnLine)- result = PtTriLocation::OnEdge1;- edgeCheck = locatePointLine(p, v2, v3);- if(edgeCheck == PtLineLocation::Right)- return PtTriLocation::Outside;- if(edgeCheck == PtLineLocation::OnLine)- result = PtTriLocation::OnEdge2;- edgeCheck = locatePointLine(p, v3, v1);- if(edgeCheck == PtLineLocation::Right)- return PtTriLocation::Outside;- if(edgeCheck == PtLineLocation::OnLine)- result = PtTriLocation::OnEdge3;- return result;-}--CDT_INLINE_IF_HEADER_ONLY Index opoNbr(const Index vertIndex)-{- if(vertIndex == Index(0))- return Index(1);- if(vertIndex == Index(1))- return Index(2);- if(vertIndex == Index(2))- return Index(0);- throw std::runtime_error("Invalid vertex index");-}--CDT_INLINE_IF_HEADER_ONLY Index opoVrt(const Index neighborIndex)-{- if(neighborIndex == Index(0))- return Index(2);- if(neighborIndex == Index(1))- return Index(0);- if(neighborIndex == Index(2))- return Index(1);- throw std::runtime_error("Invalid neighbor index");-}--CDT_INLINE_IF_HEADER_ONLY Index-opposedTriangleInd(const Triangle& tri, const VertInd iVert)-{- for(Index vi = Index(0); vi < Index(3); ++vi)- if(iVert == tri.vertices[vi])- return opoNbr(vi);- throw std::runtime_error("Could not find opposed triangle index");-}--CDT_INLINE_IF_HEADER_ONLY Index opposedTriangleInd(- const Triangle& tri,- const VertInd iVedge1,- const VertInd iVedge2)-{- for(Index vi = Index(0); vi < Index(3); ++vi)- {- const VertInd iVert = tri.vertices[vi];- if(iVert != iVedge1 && iVert != iVedge2)- return opoNbr(vi);- }- throw std::runtime_error("Could not find opposed-to-edge triangle index");-}--CDT_INLINE_IF_HEADER_ONLY Index-opposedVertexInd(const Triangle& tri, const TriInd iTopo)-{- for(Index ni = Index(0); ni < Index(3); ++ni)- if(iTopo == tri.neighbors[ni])- return opoVrt(ni);- throw std::runtime_error("Could not find opposed vertex index");-}--CDT_INLINE_IF_HEADER_ONLY Index-neighborInd(const Triangle& tri, const TriInd iTnbr)-{- for(Index ni = Index(0); ni < Index(3); ++ni)- if(iTnbr == tri.neighbors[ni])- return ni;- throw std::runtime_error("Could not find neighbor triangle index");-}--CDT_INLINE_IF_HEADER_ONLY Index vertexInd(const Triangle& tri, const VertInd iV)-{- for(Index i = Index(0); i < Index(3); ++i)- if(iV == tri.vertices[i])- return i;- throw std::runtime_error("Could not find vertex index in triangle");-}--CDT_INLINE_IF_HEADER_ONLY TriInd-opposedTriangle(const Triangle& tri, const VertInd iVert)-{- return tri.neighbors[opposedTriangleInd(tri, iVert)];-}--CDT_INLINE_IF_HEADER_ONLY VertInd-opposedVertex(const Triangle& tri, const TriInd iTopo)-{- return tri.vertices[opposedVertexInd(tri, iTopo)];-}--template <typename T>-bool isInCircumcircle(- const V2d<T>& p,- const V2d<T>& v1,- const V2d<T>& v2,- const V2d<T>& v3)-{- using namespace predicates::adaptive;- return incircle(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, p.x, p.y) > T(0);-}--CDT_INLINE_IF_HEADER_ONLY-bool verticesShareEdge(const TriIndVec& aTris, const TriIndVec& bTris)-{- for(TriIndVec::const_iterator it = aTris.begin(); it != aTris.end(); ++it)- if(std::find(bTris.begin(), bTris.end(), *it) != bTris.end())- return true;- return false;-}--template <typename T>-T distanceSquared(const T ax, const T ay, const T bx, const T by)-{- const T dx = bx - ax;- const T dy = by - ay;- return dx * dx + dy * dy;-}--template <typename T>-T distance(const T ax, const T ay, const T bx, const T by)-{- return std::sqrt(distanceSquared(ax, ay, bx, by));-}--template <typename T>-T distance(const V2d<T>& a, const V2d<T>& b)-{- return distance(a.x, a.y, b.x, b.y);-}--template <typename T>-T distanceSquared(const V2d<T>& a, const V2d<T>& b)-{- return distanceSquared(a.x, a.y, b.x, b.y);-}--} // namespace CDT+/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +/** + * @file + * Utilities and helpers - implementation + */ + +#include "CDTUtils.h" + +#include "predicates.h" // robust predicates: orient, in-circle + +#include <stdexcept> + +namespace CDT +{ + +//***************************************************************************** +// V2d +//***************************************************************************** +template <typename T> +V2d<T> V2d<T>::make(const T x, const T y) +{ + V2d<T> out = {x, y}; + return out; +} + +//***************************************************************************** +// Box2d +//***************************************************************************** +template <typename T> +Box2d<T> envelopBox(const std::vector<V2d<T> >& vertices) +{ + return envelopBox<T>( + vertices.begin(), vertices.end(), getX_V2d<T>, getY_V2d<T>); +} + +//***************************************************************************** +// Edge +//***************************************************************************** +CDT_INLINE_IF_HEADER_ONLY Edge::Edge(VertInd iV1, VertInd iV2) + : m_vertices( + iV1 < iV2 ? std::make_pair(iV1, iV2) : std::make_pair(iV2, iV1)) +{} + +CDT_INLINE_IF_HEADER_ONLY bool Edge::operator==(const Edge& other) const +{ + return m_vertices == other.m_vertices; +} + +CDT_INLINE_IF_HEADER_ONLY bool Edge::operator!=(const Edge& other) const +{ + return !(this->operator==(other)); +} + +CDT_INLINE_IF_HEADER_ONLY VertInd Edge::v1() const +{ + return m_vertices.first; +} + +CDT_INLINE_IF_HEADER_ONLY VertInd Edge::v2() const +{ + return m_vertices.second; +} + +CDT_INLINE_IF_HEADER_ONLY const std::pair<VertInd, VertInd>& Edge::verts() const +{ + return m_vertices; +} + +//***************************************************************************** +// Utility functions +//***************************************************************************** +CDT_INLINE_IF_HEADER_ONLY Index ccw(Index i) +{ + return Index((i + 1) % 3); +} + +CDT_INLINE_IF_HEADER_ONLY Index cw(Index i) +{ + return Index((i + 2) % 3); +} + +CDT_INLINE_IF_HEADER_ONLY bool isOnEdge(const PtTriLocation::Enum location) +{ + return location > PtTriLocation::Outside; +} + +CDT_INLINE_IF_HEADER_ONLY Index edgeNeighbor(const PtTriLocation::Enum location) +{ + assert(location >= PtTriLocation::OnEdge1); + return static_cast<Index>(location - PtTriLocation::OnEdge1); +} + +template <typename T> +T orient2D(const V2d<T>& p, const V2d<T>& v1, const V2d<T>& v2) +{ + return predicates::adaptive::orient2d(v1.x, v1.y, v2.x, v2.y, p.x, p.y); +} + +template <typename T> +PtLineLocation::Enum locatePointLine( + const V2d<T>& p, + const V2d<T>& v1, + const V2d<T>& v2, + const T orientationTolerance) +{ + return classifyOrientation(orient2D(p, v1, v2), orientationTolerance); +} + +template <typename T> +PtLineLocation::Enum +classifyOrientation(const T orientation, const T orientationTolerance) +{ + if(orientation < -orientationTolerance) + return PtLineLocation::Right; + if(orientation > orientationTolerance) + return PtLineLocation::Left; + return PtLineLocation::OnLine; +} + +template <typename T> +PtTriLocation::Enum locatePointTriangle( + const V2d<T>& p, + const V2d<T>& v1, + const V2d<T>& v2, + const V2d<T>& v3) +{ + using namespace predicates::adaptive; + PtTriLocation::Enum result = PtTriLocation::Inside; + PtLineLocation::Enum edgeCheck = locatePointLine(p, v1, v2); + if(edgeCheck == PtLineLocation::Right) + return PtTriLocation::Outside; + if(edgeCheck == PtLineLocation::OnLine) + result = PtTriLocation::OnEdge1; + edgeCheck = locatePointLine(p, v2, v3); + if(edgeCheck == PtLineLocation::Right) + return PtTriLocation::Outside; + if(edgeCheck == PtLineLocation::OnLine) + result = PtTriLocation::OnEdge2; + edgeCheck = locatePointLine(p, v3, v1); + if(edgeCheck == PtLineLocation::Right) + return PtTriLocation::Outside; + if(edgeCheck == PtLineLocation::OnLine) + result = PtTriLocation::OnEdge3; + return result; +} + +CDT_INLINE_IF_HEADER_ONLY Index opoNbr(const Index vertIndex) +{ + if(vertIndex == Index(0)) + return Index(1); + if(vertIndex == Index(1)) + return Index(2); + if(vertIndex == Index(2)) + return Index(0); + throw std::runtime_error("Invalid vertex index"); +} + +CDT_INLINE_IF_HEADER_ONLY Index opoVrt(const Index neighborIndex) +{ + if(neighborIndex == Index(0)) + return Index(2); + if(neighborIndex == Index(1)) + return Index(0); + if(neighborIndex == Index(2)) + return Index(1); + throw std::runtime_error("Invalid neighbor index"); +} + +CDT_INLINE_IF_HEADER_ONLY Index +opposedTriangleInd(const VerticesArr3& vv, const VertInd iVert) +{ + assert(vv[0] == iVert || vv[1] == iVert || vv[2] == iVert); + if(vv[0] == iVert) + return Index(1); + if(vv[1] == iVert) + return Index(2); + return Index(0); +} + +CDT_INLINE_IF_HEADER_ONLY Index edgeNeighborInd( + const VerticesArr3& vv, + const VertInd iVedge1, + const VertInd iVedge2) +{ + assert(vv[0] == iVedge1 || vv[1] == iVedge1 || vv[2] == iVedge1); + assert(vv[0] == iVedge2 || vv[1] == iVedge2 || vv[2] == iVedge2); + assert( + (vv[0] != iVedge1 && vv[0] != iVedge2) || + (vv[1] != iVedge1 && vv[1] != iVedge2) || + (vv[2] != iVedge1 && vv[2] != iVedge2)); + /* + * vv[2] + * /\ + * n[2]/ \n[1] + * /____\ + * vv[0] n[0] vv[1] + */ + if(vv[0] == iVedge1) + { + if(vv[1] == iVedge2) + return Index(0); + return Index(2); + } + if(vv[0] == iVedge2) + { + if(vv[1] == iVedge1) + return Index(0); + return Index(2); + } + return Index(1); +} + +CDT_INLINE_IF_HEADER_ONLY Index +opposedVertexInd(const NeighborsArr3& nn, const TriInd iTopo) +{ + assert(nn[0] == iTopo || nn[1] == iTopo || nn[2] == iTopo); + if(nn[0] == iTopo) + return Index(2); + if(nn[1] == iTopo) + return Index(0); + return Index(1); +} + +CDT_INLINE_IF_HEADER_ONLY Index +vertexInd(const VerticesArr3& vv, const VertInd iV) +{ + assert(vv[0] == iV || vv[1] == iV || vv[2] == iV); + if(vv[0] == iV) + return Index(0); + if(vv[1] == iV) + return Index(1); + return Index(2); +} + +CDT_INLINE_IF_HEADER_ONLY TriInd +opposedTriangle(const Triangle& tri, const VertInd iVert) +{ + return tri.neighbors[opposedTriangleInd(tri.vertices, iVert)]; +} + +CDT_INLINE_IF_HEADER_ONLY VertInd +opposedVertex(const Triangle& tri, const TriInd iTopo) +{ + return tri.vertices[opposedVertexInd(tri.neighbors, iTopo)]; +} + +/// Given triangle and an edge find neighbor sharing the edge +CDT_INLINE_IF_HEADER_ONLY TriInd +edgeNeighbor(const Triangle& tri, VertInd iVedge1, VertInd iVedge2) +{ + return tri.neighbors[edgeNeighborInd(tri.vertices, iVedge1, iVedge2)]; +} + +template <typename T> +bool isInCircumcircle( + const V2d<T>& p, + const V2d<T>& v1, + const V2d<T>& v2, + const V2d<T>& v3) +{ + using namespace predicates::adaptive; + return incircle(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, p.x, p.y) > T(0); +} + +CDT_INLINE_IF_HEADER_ONLY +bool verticesShareEdge(const TriIndVec& aTris, const TriIndVec& bTris) +{ + for(TriIndVec::const_iterator it = aTris.begin(); it != aTris.end(); ++it) + if(std::find(bTris.begin(), bTris.end(), *it) != bTris.end()) + return true; + return false; +} + +template <typename T> +T distanceSquared(const T ax, const T ay, const T bx, const T by) +{ + const T dx = bx - ax; + const T dy = by - ay; + return dx * dx + dy * dy; +} + +template <typename T> +T distance(const T ax, const T ay, const T bx, const T by) +{ + return std::sqrt(distanceSquared(ax, ay, bx, by)); +} + +template <typename T> +T distance(const V2d<T>& a, const V2d<T>& b) +{ + return distance(a.x, a.y, b.x, b.y); +} + +template <typename T> +T distanceSquared(const V2d<T>& a, const V2d<T>& b) +{ + return distanceSquared(a.x, a.y, b.x, b.y); +} + +bool touchesSuperTriangle(const Triangle& t) +{ + return t.vertices[0] < 3 || t.vertices[1] < 3 || t.vertices[2] < 3; +} + +} // namespace CDT
cpp/KDTree.h view
@@ -1,399 +1,412 @@-/// This Source Code Form is subject to the terms of the Mozilla Public-/// License, v. 2.0. If a copy of the MPL was not distributed with this-/// file, You can obtain one at https://mozilla.org/MPL/2.0/.-/// Contribution of original implementation:-/// Andre Fecteau <andre.fecteau1@gmail.com>--#ifndef KDTREE_KDTREE_H-#define KDTREE_KDTREE_H--#include "CDTUtils.h"--#include <cassert>-#include <limits>--namespace KDTree-{--struct NodeSplitDirection-{- enum Enum- {- X,- Y,- };-};--/// Simple tree structure with alternating half splitting nodes-/// @details Simple tree structure-/// - Tree to incrementally add points to the structure.-/// - Get the nearest point to a given input.-/// - Does not check for duplicates, expect unique points.-/// @tparam TCoordType type used for storing point coordinate.-/// @tparam NumVerticesInLeaf The number of points per leaf.-/// @tparam InitialStackDepth initial size of stack depth for nearest query.-/// Should be at least 1.-/// @tparam StackDepthIncrement increment of stack depth for nearest query when-/// stack depth is reached.-template <- typename TCoordType,- size_t NumVerticesInLeaf,- size_t InitialStackDepth,- size_t StackDepthIncrement>-class KDTree-{-public:- typedef TCoordType coord_type;- typedef CDT::V2d<coord_type> point_type;- typedef CDT::VertInd point_index;- typedef std::pair<point_type, point_index> value_type;- typedef std::vector<point_index> point_data_vec;- typedef point_data_vec::const_iterator pd_cit;- typedef CDT::VertInd node_index;- typedef CDT::array<node_index, 2> children_type;-- /// Stores kd-tree node data- struct Node- {- children_type children; ///< two children if not leaf; {0,0} if leaf- point_data_vec data; ///< points' data if leaf- /// Create empty leaf- Node()- {- setChildren(0, 0);- data.reserve(NumVerticesInLeaf);- }- /// Children setter for convenience- void setChildren(const node_index c1, const node_index c2)- {- children[0] = c1;- children[1] = c2;- }- /// Check if node is a leaf (has no valid children)- bool isLeaf() const- {- return children[0] == children[1];- }- };-- /// Default constructor- KDTree()- : m_rootDir(NodeSplitDirection::X)- , m_min(point_type::make(- -std::numeric_limits<coord_type>::max(),- -std::numeric_limits<coord_type>::max()))- , m_max(point_type::make(- std::numeric_limits<coord_type>::max(),- std::numeric_limits<coord_type>::max()))- , m_isRootBoxInitialized(false)- , m_tasksStack(InitialStackDepth, NearestTask())- {- m_root = addNewNode();- }-- /// Constructor with bounding box known in advance- KDTree(const point_type& min, const point_type& max)- : m_rootDir(NodeSplitDirection::X)- , m_min(min)- , m_max(max)- , m_isRootBoxInitialized(true)- , m_tasksStack(InitialStackDepth, NearestTask())- {- m_root = addNewNode();- }-- /// Insert a point into kd-tree- /// @note external point-buffer is used to reduce kd-tree's memory footprint- /// @param iPoint index of point in external point-buffer- /// @param points external point-buffer- void- insert(const point_index& iPoint, const std::vector<point_type>& points)- {- // if point is outside root, extend tree by adding new roots- const point_type& pos = points[iPoint];- while(!isInsideBox(pos, m_min, m_max))- {- extendTree(pos);- }- // now insert the point into the tree- node_index node = m_root;- point_type min = m_min;- point_type max = m_max;- NodeSplitDirection::Enum dir = m_rootDir;-- // below: initialized only to suppress warnings- NodeSplitDirection::Enum newDir(NodeSplitDirection::X);- coord_type mid(0);- point_type newMin, newMax;- while(true)- {- if(m_nodes[node].isLeaf())- {- // add point if capacity is not reached- point_data_vec& pd = m_nodes[node].data;- if(pd.size() < NumVerticesInLeaf)- {- pd.push_back(iPoint);- return;- }- // initialize bbox first time the root capacity is reached- if(!m_isRootBoxInitialized)- {- initializeRootBox(points);- min = m_min;- max = m_max;- }- // split a full leaf node- calcSplitInfo(min, max, dir, mid, newDir, newMin, newMax);- const node_index c1 = addNewNode(), c2 = addNewNode();- Node& n = m_nodes[node];- n.setChildren(c1, c2);- point_data_vec& c1data = m_nodes[c1].data;- point_data_vec& c2data = m_nodes[c2].data;- // move node's points to children- for(pd_cit it = n.data.begin(); it != n.data.end(); ++it)- {- whichChild(points[*it], mid, dir) == 0- ? c1data.push_back(*it)- : c2data.push_back(*it);- }- n.data = point_data_vec();- }- else- {- calcSplitInfo(min, max, dir, mid, newDir, newMin, newMax);- }- // add the point to a child- const std::size_t iChild = whichChild(points[iPoint], mid, dir);- iChild == 0 ? max = newMax : min = newMin;- node = m_nodes[node].children[iChild];- dir = newDir;- }- }-- /// Query kd-tree for a nearest neighbor point- /// @note external point-buffer is used to reduce kd-tree's memory footprint- /// @param point query point position- /// @param points external point-buffer- value_type nearest(- const point_type& point,- const std::vector<point_type>& points) const- {- value_type out;- int iTask = -1;- coord_type minDistSq = std::numeric_limits<coord_type>::max();- m_tasksStack[++iTask] =- NearestTask(m_root, m_min, m_max, m_rootDir, minDistSq);- while(iTask != -1)- {- const NearestTask t = m_tasksStack[iTask--];- if(t.distSq > minDistSq)- continue;- const Node& n = m_nodes[t.node];- if(n.isLeaf())- {- for(pd_cit it = n.data.begin(); it != n.data.end(); ++it)- {- const point_type& p = points[*it];- const coord_type distSq = CDT::distanceSquared(point, p);- if(distSq < minDistSq)- {- minDistSq = distSq;- out.first = p;- out.second = *it;- }- }- }- else- {- coord_type mid(0);- NodeSplitDirection::Enum newDir;- point_type newMin, newMax;- calcSplitInfo(t.min, t.max, t.dir, mid, newDir, newMin, newMax);-- const coord_type distToMid = t.dir == NodeSplitDirection::X- ? (point.x - mid)- : (point.y - mid);- const coord_type toMidSq = distToMid * distToMid;-- const std::size_t iChild = whichChild(point, mid, t.dir);- if(iTask + 2 >= static_cast<int>(m_tasksStack.size()))- {- m_tasksStack.resize(- m_tasksStack.size() + StackDepthIncrement);- }- // node containing point should end up on top of the stack- if(iChild == 0)- {- m_tasksStack[++iTask] = NearestTask(- n.children[1], newMin, t.max, newDir, toMidSq);- m_tasksStack[++iTask] = NearestTask(- n.children[0], t.min, newMax, newDir, toMidSq);- }- else- {- m_tasksStack[++iTask] = NearestTask(- n.children[0], t.min, newMax, newDir, toMidSq);- m_tasksStack[++iTask] = NearestTask(- n.children[1], newMin, t.max, newDir, toMidSq);- }- }- }- return out;- }--private:- /// Add a new node and return it's index in nodes buffer- node_index addNewNode()- {- const node_index newNodeIndex = static_cast<node_index>(m_nodes.size());- m_nodes.push_back(Node());- return newNodeIndex;- }-- /// Test which child point belongs to after the split- /// @returns 0 if first child, 1 if second child- std::size_t whichChild(- const point_type& point,- const coord_type& split,- const NodeSplitDirection::Enum dir) const- {- return static_cast<size_t>(- dir == NodeSplitDirection::X ? point.x > split : point.y > split);- }-- /// Calculate split location, direction, and children boxes- static void calcSplitInfo(- const point_type& min,- const point_type& max,- const NodeSplitDirection::Enum dir,- coord_type& midOut,- NodeSplitDirection::Enum& newDirOut,- point_type& newMinOut,- point_type& newMaxOut)- {- newMaxOut = max;- newMinOut = min;- switch(dir)- {- case NodeSplitDirection::X:- midOut = (min.x + max.x) / coord_type(2);- newDirOut = NodeSplitDirection::Y;- newMinOut.x = midOut;- newMaxOut.x = midOut;- return;- case NodeSplitDirection::Y:- midOut = (min.y + max.y) / coord_type(2);- newDirOut = NodeSplitDirection::X;- newMinOut.y = midOut;- newMaxOut.y = midOut;- return;- }- }-- /// Test if point is inside a box- static bool isInsideBox(- const point_type& p,- const point_type& min,- const point_type& max)- {- return p.x >= min.x && p.x <= max.x && p.y >= min.y && p.y <= max.y;- }-- /// Extend a tree by creating new root with old root and a new node as- /// children- void extendTree(const point_type& point)- {- const node_index newRoot = addNewNode();- const node_index newLeaf = addNewNode();- switch(m_rootDir)- {- case NodeSplitDirection::X:- m_rootDir = NodeSplitDirection::Y;- point.y < m_min.y ? m_nodes[newRoot].setChildren(newLeaf, m_root)- : m_nodes[newRoot].setChildren(m_root, newLeaf);- if(point.y < m_min.y)- m_min.y -= m_max.y - m_min.y;- else if(point.y > m_max.y)- m_max.y += m_max.y - m_min.y;- break;- case NodeSplitDirection::Y:- m_rootDir = NodeSplitDirection::X;- point.x < m_min.x ? m_nodes[newRoot].setChildren(newLeaf, m_root)- : m_nodes[newRoot].setChildren(m_root, newLeaf);- if(point.x < m_min.x)- m_min.x -= m_max.x - m_min.x;- else if(point.x > m_max.x)- m_max.x += m_max.x - m_min.x;- break;- }- m_root = newRoot;- }-- /// Calculate root's box enclosing all root points- void initializeRootBox(const std::vector<point_type>& points)- {- const point_data_vec& data = m_nodes[m_root].data;- m_min = points[data.front()];- m_max = m_min;- for(pd_cit it = data.begin(); it != data.end(); ++it)- {- const point_type& p = points[*it];- m_min = point_type::make(- std::min(m_min.x, p.x), std::min(m_min.y, p.y));- m_max = point_type::make(- std::max(m_max.x, p.x), std::max(m_max.y, p.y));- }- // Make sure bounding box does not have a zero size by adding padding:- // zero-size bounding box cannot be extended properly- const TCoordType padding(1);- if(m_min.x == m_max.x)- {- m_min.x -= padding;- m_max.x += padding;- }- if(m_min.y == m_max.y)- {- m_min.y -= padding;- m_max.y += padding;- }- m_isRootBoxInitialized = true;- }--private:- node_index m_root;- std::vector<Node> m_nodes;- NodeSplitDirection::Enum m_rootDir;- point_type m_min;- point_type m_max;- bool m_isRootBoxInitialized;-- // used for nearest query- struct NearestTask- {- node_index node;- point_type min, max;- NodeSplitDirection::Enum dir;- coord_type distSq;- NearestTask()- {}- NearestTask(- const node_index node,- const point_type& min,- const point_type& max,- const NodeSplitDirection::Enum dir,- const coord_type distSq)- : node(node)- , min(min)- , max(max)- , dir(dir)- , distSq(distSq)- {}- };- // allocated in class (not in the 'nearest' method) for better performance- mutable std::vector<NearestTask> m_tasksStack;-};--} // namespace KDTree--#endif // header guard+/// This Source Code Form is subject to the terms of the Mozilla Public +/// License, v. 2.0. If a copy of the MPL was not distributed with this +/// file, You can obtain one at https://mozilla.org/MPL/2.0/. +/// Contribution of original implementation: +/// Andre Fecteau <andre.fecteau1@gmail.com> + +#ifndef KDTREE_KDTREE_H +#define KDTREE_KDTREE_H + +#include "CDTUtils.h" + +#include <cassert> +#include <limits> + +namespace CDT +{ +namespace KDTree +{ + +struct NodeSplitDirection +{ + enum Enum + { + X, + Y, + }; +}; + +/// Simple tree structure with alternating half splitting nodes +/// @details Simple tree structure +/// - Tree to incrementally add points to the structure. +/// - Get the nearest point to a given input. +/// - Does not check for duplicates, expect unique points. +/// @tparam TCoordType type used for storing point coordinate. +/// @tparam NumVerticesInLeaf The number of points per leaf. +/// @tparam InitialStackDepth initial size of stack depth for nearest query. +/// Should be at least 1. +/// @tparam StackDepthIncrement increment of stack depth for nearest query when +/// stack depth is reached. +template < + typename TCoordType, + size_t NumVerticesInLeaf, + size_t InitialStackDepth, + size_t StackDepthIncrement> +class KDTree +{ +public: + typedef TCoordType coord_type; + typedef CDT::V2d<coord_type> point_type; + typedef CDT::VertInd point_index; + typedef std::pair<point_type, point_index> value_type; + typedef std::vector<point_index> point_data_vec; + typedef point_data_vec::const_iterator pd_cit; + typedef CDT::VertInd node_index; + typedef CDT::array<node_index, 2> children_type; + + /// Stores kd-tree node data + struct Node + { + children_type children; ///< two children if not leaf; {0,0} if leaf + point_data_vec data; ///< points' data if leaf + /// Create empty leaf + Node() + { + setChildren(node_index(0), node_index(0)); + data.reserve(NumVerticesInLeaf); + } + /// Children setter for convenience + void setChildren(const node_index c1, const node_index c2) + { + children[0] = c1; + children[1] = c2; + } + /// Check if node is a leaf (has no valid children) + bool isLeaf() const + { + return children[0] == children[1]; + } + }; + + /// Default constructor + KDTree() + : m_rootDir(NodeSplitDirection::X) + , m_min(point_type::make( + -std::numeric_limits<coord_type>::max(), + -std::numeric_limits<coord_type>::max())) + , m_max(point_type::make( + std::numeric_limits<coord_type>::max(), + std::numeric_limits<coord_type>::max())) + , m_size(0) + , m_isRootBoxInitialized(false) + , m_tasksStack(InitialStackDepth, NearestTask()) + { + m_root = addNewNode(); + } + + /// Constructor with bounding box known in advance + KDTree(const point_type& min, const point_type& max) + : m_rootDir(NodeSplitDirection::X) + , m_min(min) + , m_max(max) + , m_size(0) + , m_isRootBoxInitialized(true) + , m_tasksStack(InitialStackDepth, NearestTask()) + { + m_root = addNewNode(); + } + + CDT::VertInd size() const + { + return m_size; + } + + /// Insert a point into kd-tree + /// @note external point-buffer is used to reduce kd-tree's memory footprint + /// @param iPoint index of point in external point-buffer + /// @param points external point-buffer + void + insert(const point_index& iPoint, const std::vector<point_type>& points) + { + ++m_size; + // if point is outside root, extend tree by adding new roots + const point_type& pos = points[iPoint]; + while(!isInsideBox(pos, m_min, m_max)) + { + extendTree(pos); + } + // now insert the point into the tree + node_index node = m_root; + point_type min = m_min; + point_type max = m_max; + NodeSplitDirection::Enum dir = m_rootDir; + + // below: initialized only to suppress warnings + NodeSplitDirection::Enum newDir(NodeSplitDirection::X); + coord_type mid(0); + point_type newMin, newMax; + while(true) + { + if(m_nodes[node].isLeaf()) + { + // add point if capacity is not reached + point_data_vec& pd = m_nodes[node].data; + if(pd.size() < NumVerticesInLeaf) + { + pd.push_back(iPoint); + return; + } + // initialize bbox first time the root capacity is reached + if(!m_isRootBoxInitialized) + { + initializeRootBox(points); + min = m_min; + max = m_max; + } + // split a full leaf node + calcSplitInfo(min, max, dir, mid, newDir, newMin, newMax); + const node_index c1 = addNewNode(), c2 = addNewNode(); + Node& n = m_nodes[node]; + n.setChildren(c1, c2); + point_data_vec& c1data = m_nodes[c1].data; + point_data_vec& c2data = m_nodes[c2].data; + // move node's points to children + for(pd_cit it = n.data.begin(); it != n.data.end(); ++it) + { + whichChild(points[*it], mid, dir) == 0 + ? c1data.push_back(*it) + : c2data.push_back(*it); + } + n.data = point_data_vec(); + } + else + { + calcSplitInfo(min, max, dir, mid, newDir, newMin, newMax); + } + // add the point to a child + const std::size_t iChild = whichChild(points[iPoint], mid, dir); + iChild == 0 ? max = newMax : min = newMin; + node = m_nodes[node].children[iChild]; + dir = newDir; + } + } + + /// Query kd-tree for a nearest neighbor point + /// @note external point-buffer is used to reduce kd-tree's memory footprint + /// @param point query point position + /// @param points external point-buffer + value_type nearest( + const point_type& point, + const std::vector<point_type>& points) const + { + value_type out; + int iTask = -1; + coord_type minDistSq = std::numeric_limits<coord_type>::max(); + m_tasksStack[++iTask] = + NearestTask(m_root, m_min, m_max, m_rootDir, minDistSq); + while(iTask != -1) + { + const NearestTask t = m_tasksStack[iTask--]; + if(t.distSq > minDistSq) + continue; + const Node& n = m_nodes[t.node]; + if(n.isLeaf()) + { + for(pd_cit it = n.data.begin(); it != n.data.end(); ++it) + { + const point_type& p = points[*it]; + const coord_type distSq = CDT::distanceSquared(point, p); + if(distSq < minDistSq) + { + minDistSq = distSq; + out.first = p; + out.second = *it; + } + } + } + else + { + coord_type mid(0); + NodeSplitDirection::Enum newDir; + point_type newMin, newMax; + calcSplitInfo(t.min, t.max, t.dir, mid, newDir, newMin, newMax); + + const coord_type distToMid = t.dir == NodeSplitDirection::X + ? (point.x - mid) + : (point.y - mid); + const coord_type toMidSq = distToMid * distToMid; + + const std::size_t iChild = whichChild(point, mid, t.dir); + if(iTask + 2 >= static_cast<int>(m_tasksStack.size())) + { + m_tasksStack.resize( + m_tasksStack.size() + StackDepthIncrement); + } + // node containing point should end up on top of the stack + if(iChild == 0) + { + m_tasksStack[++iTask] = NearestTask( + n.children[1], newMin, t.max, newDir, toMidSq); + m_tasksStack[++iTask] = NearestTask( + n.children[0], t.min, newMax, newDir, toMidSq); + } + else + { + m_tasksStack[++iTask] = NearestTask( + n.children[0], t.min, newMax, newDir, toMidSq); + m_tasksStack[++iTask] = NearestTask( + n.children[1], newMin, t.max, newDir, toMidSq); + } + } + } + return out; + } + +private: + /// Add a new node and return it's index in nodes buffer + node_index addNewNode() + { + const node_index newNodeIndex = static_cast<node_index>(m_nodes.size()); + m_nodes.push_back(Node()); + return newNodeIndex; + } + + /// Test which child point belongs to after the split + /// @returns 0 if first child, 1 if second child + std::size_t whichChild( + const point_type& point, + const coord_type& split, + const NodeSplitDirection::Enum dir) const + { + return static_cast<size_t>( + dir == NodeSplitDirection::X ? point.x > split : point.y > split); + } + + /// Calculate split location, direction, and children boxes + static void calcSplitInfo( + const point_type& min, + const point_type& max, + const NodeSplitDirection::Enum dir, + coord_type& midOut, + NodeSplitDirection::Enum& newDirOut, + point_type& newMinOut, + point_type& newMaxOut) + { + newMaxOut = max; + newMinOut = min; + switch(dir) + { + case NodeSplitDirection::X: + midOut = (min.x + max.x) / coord_type(2); + newDirOut = NodeSplitDirection::Y; + newMinOut.x = midOut; + newMaxOut.x = midOut; + return; + case NodeSplitDirection::Y: + midOut = (min.y + max.y) / coord_type(2); + newDirOut = NodeSplitDirection::X; + newMinOut.y = midOut; + newMaxOut.y = midOut; + return; + } + } + + /// Test if point is inside a box + static bool isInsideBox( + const point_type& p, + const point_type& min, + const point_type& max) + { + return p.x >= min.x && p.x <= max.x && p.y >= min.y && p.y <= max.y; + } + + /// Extend a tree by creating new root with old root and a new node as + /// children + void extendTree(const point_type& point) + { + const node_index newRoot = addNewNode(); + const node_index newLeaf = addNewNode(); + switch(m_rootDir) + { + case NodeSplitDirection::X: + m_rootDir = NodeSplitDirection::Y; + point.y < m_min.y ? m_nodes[newRoot].setChildren(newLeaf, m_root) + : m_nodes[newRoot].setChildren(m_root, newLeaf); + if(point.y < m_min.y) + m_min.y -= m_max.y - m_min.y; + else if(point.y > m_max.y) + m_max.y += m_max.y - m_min.y; + break; + case NodeSplitDirection::Y: + m_rootDir = NodeSplitDirection::X; + point.x < m_min.x ? m_nodes[newRoot].setChildren(newLeaf, m_root) + : m_nodes[newRoot].setChildren(m_root, newLeaf); + if(point.x < m_min.x) + m_min.x -= m_max.x - m_min.x; + else if(point.x > m_max.x) + m_max.x += m_max.x - m_min.x; + break; + } + m_root = newRoot; + } + + /// Calculate root's box enclosing all root points + void initializeRootBox(const std::vector<point_type>& points) + { + const point_data_vec& data = m_nodes[m_root].data; + m_min = points[data.front()]; + m_max = m_min; + for(pd_cit it = data.begin(); it != data.end(); ++it) + { + const point_type& p = points[*it]; + m_min = point_type::make( + std::min(m_min.x, p.x), std::min(m_min.y, p.y)); + m_max = point_type::make( + std::max(m_max.x, p.x), std::max(m_max.y, p.y)); + } + // Make sure bounding box does not have a zero size by adding padding: + // zero-size bounding box cannot be extended properly + const TCoordType padding(1); + if(m_min.x == m_max.x) + { + m_min.x -= padding; + m_max.x += padding; + } + if(m_min.y == m_max.y) + { + m_min.y -= padding; + m_max.y += padding; + } + m_isRootBoxInitialized = true; + } + +private: + node_index m_root; + std::vector<Node> m_nodes; + NodeSplitDirection::Enum m_rootDir; + point_type m_min; + point_type m_max; + CDT::VertInd m_size; + + bool m_isRootBoxInitialized; + + // used for nearest query + struct NearestTask + { + node_index node; + point_type min, max; + NodeSplitDirection::Enum dir; + coord_type distSq; + NearestTask() + {} + NearestTask( + const node_index node_, + const point_type& min_, + const point_type& max_, + const NodeSplitDirection::Enum dir_, + const coord_type distSq_) + : node(node_) + , min(min_) + , max(max_) + , dir(dir_) + , distSq(distSq_) + {} + }; + // allocated in class (not in the 'nearest' method) for better performance + mutable std::vector<NearestTask> m_tasksStack; +}; + +} // namespace KDTree +} // namespace CDT + +#endif // header guard
cpp/LocatorKDTree.h view
@@ -1,71 +1,81 @@-/* This Source Code Form is subject to the terms of the Mozilla Public- * License, v. 2.0. If a copy of the MPL was not distributed with this- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */--/**- * @file- * Adapter between for KDTree and CDT- */--#ifndef CDT_POINTKDTREE_H-#define CDT_POINTKDTREE_H--#include "CDTUtils.h"-#include "KDTree.h"--namespace CDT-{--/// KD-tree holding points-template <- typename TCoordType,- size_t NumVerticesInLeaf = 32,- size_t InitialStackDepth = 32,- size_t StackDepthIncrement = 32>-class LocatorKDTree-{-public:- /// Initialize KD-tree with points- void initialize(const std::vector<V2d<TCoordType> >& points)- {- typedef V2d<TCoordType> V2d_t;- V2d_t min = points.front();- V2d_t max = min;- typedef typename std::vector<V2d_t>::const_iterator Cit;- for(Cit it = points.begin(); it != points.end(); ++it)- {- min = V2d_t::make(std::min(min.x, it->x), std::min(min.y, it->y));- max = V2d_t::make(std::max(max.x, it->x), std::max(max.y, it->y));- }- m_kdTree = KDTree_t(min, max);- for(VertInd i = 0; i < points.size(); ++i)- {- m_kdTree.insert(i, points);- }- }- /// Add point to KD-tree- void addPoint(const VertInd i, const std::vector<V2d<TCoordType> >& points)- {- m_kdTree.insert(i, points);- }- /// Find nearest point using R-tree- VertInd nearPoint(- const V2d<TCoordType>& pos,- const std::vector<V2d<TCoordType> >& points) const- {- return m_kdTree.nearest(pos, points).second;- }--private:- typedef KDTree::KDTree<- TCoordType,- NumVerticesInLeaf,- InitialStackDepth,- StackDepthIncrement>- KDTree_t;- KDTree_t m_kdTree;-};--} // namespace CDT--#endif+/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +/** + * @file + * Adapter between for KDTree and CDT + */ + +#ifndef CDT_POINTKDTREE_H +#define CDT_POINTKDTREE_H + +#include "CDTUtils.h" +#include "KDTree.h" + +namespace CDT +{ + +/// KD-tree holding points +template < + typename TCoordType, + size_t NumVerticesInLeaf = 32, + size_t InitialStackDepth = 32, + size_t StackDepthIncrement = 32> +class LocatorKDTree +{ +public: + /// Initialize KD-tree with points + void initialize(const std::vector<V2d<TCoordType> >& points) + { + typedef V2d<TCoordType> V2d_t; + V2d_t min = points.front(); + V2d_t max = min; + typedef typename std::vector<V2d_t>::const_iterator Cit; + for(Cit it = points.begin(); it != points.end(); ++it) + { + min = V2d_t::make(std::min(min.x, it->x), std::min(min.y, it->y)); + max = V2d_t::make(std::max(max.x, it->x), std::max(max.y, it->y)); + } + m_kdTree = KDTree_t(min, max); + for(VertInd i(0); i < points.size(); ++i) + { + m_kdTree.insert(i, points); + } + } + /// Add point to KD-tree + void addPoint(const VertInd i, const std::vector<V2d<TCoordType> >& points) + { + m_kdTree.insert(i, points); + } + /// Find nearest point using R-tree + VertInd nearPoint( + const V2d<TCoordType>& pos, + const std::vector<V2d<TCoordType> >& points) const + { + return m_kdTree.nearest(pos, points).second; + } + + CDT::VertInd size() const + { + return m_kdTree.size(); + } + + bool empty() const + { + return !size(); + } + +private: + typedef KDTree::KDTree< + TCoordType, + NumVerticesInLeaf, + InitialStackDepth, + StackDepthIncrement> + KDTree_t; + KDTree_t m_kdTree; +}; + +} // namespace CDT + +#endif
cpp/Triangulation.h view
@@ -1,617 +1,779 @@-/* This Source Code Form is subject to the terms of the Mozilla Public- * License, v. 2.0. If a copy of the MPL was not distributed with this- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */--/**- * @file- * Triangulation class- */--#ifndef CDT_vW1vZ0lO8rS4gY4uI4fB-#define CDT_vW1vZ0lO8rS4gY4uI4fB--#include "CDTUtils.h"-#include "LocatorKDTree.h"--#include <algorithm>-#include <cstdlib>-#include <iterator>-#include <stack>-#include <stdexcept>-#include <utility>-#include <vector>--/// Namespace containing triangulation functionality-namespace CDT-{--/// @addtogroup API-/// @{--/**- * Enum of strategies specifying order in which a range of vertices is inserted- * @note VertexInsertionOrder::Randomized will only randomize order of- * inserting in triangulation, vertex indices will be preserved as they were- * specified in the final triangulation- */-struct CDT_EXPORT VertexInsertionOrder-{- /**- * The Enum itself- * @note needed to pre c++11 compilers that don't support 'class enum'- */- enum Enum- {- Randomized, ///< vertices will be inserted in random order- AsProvided, ///< vertices will be inserted in the same order as provided- };-};--/// Enum of what type of geometry used to embed triangulation into-struct CDT_EXPORT SuperGeometryType-{- /**- * The Enum itself- * @note needed to pre c++11 compilers that don't support 'class enum'- */- enum Enum- {- SuperTriangle, ///< conventional super-triangle- Custom, ///< user-specified custom geometry (e.g., grid)- };-};--/**- * Enum of strategies for treating intersecting constraint edges- */-struct CDT_EXPORT IntersectingConstraintEdges-{- /**- * The Enum itself- * @note needed to pre c++11 compilers that don't support 'class enum'- */- enum Enum- {- Ignore, ///< constraint edge intersections are not checked- Resolve, ///< constraint edge intersections are resolved- };-};--/**- * Type used for storing layer depths for triangles- * @note LayerDepth should support 60K+ layers, which could be to much or- * too little for some use cases. Feel free to re-define this typedef.- */-typedef unsigned short LayerDepth;-typedef LayerDepth BoundaryOverlapCount;--/// Triangles by vertex index-typedef std::vector<TriIndVec> VerticesTriangles;--/**- * @defgroup Triangulation Triangulation Class- * Class performing triangulations.- */-/// @{--/**- * Data structure representing a 2D constrained Delaunay triangulation- *- * @tparam T type of vertex coordinates (e.g., float, double)- * @tparam TNearPointLocator class providing locating near point for efficiently- * inserting new points. Provides methods: 'addPoint(vPos, iV)' and- * 'nearPoint(vPos) -> iV'- */-template <typename T, typename TNearPointLocator = LocatorKDTree<T> >-class CDT_EXPORT Triangulation-{-public:- typedef std::vector<V2d<T> > V2dVec; ///< Vertices vector- V2dVec vertices; ///< triangulation's vertices- TriangleVec triangles; ///< triangulation's triangles- EdgeUSet fixedEdges; ///< triangulation's constraints (fixed edges)- /**- * triangles adjacent to each vertex- * @note will be reset to empty when super-triangle is removed and- * triangulation is finalized. To re-calculate adjacent triangles use- * CDT::calculateTrianglesByVertex helper- */- VerticesTriangles vertTris;-- /** Stores count of overlapping boundaries for a fixed edge. If no entry is- * present for an edge: no boundaries overlap.- * @note map only has entries for fixed for edges that represent overlapping- * boundaries- * @note needed for handling depth calculations and hole-removel in case of- * overlapping boundaries- */- unordered_map<Edge, BoundaryOverlapCount> overlapCount;-- /** Stores list of original edges represented by a given fixed edge- * @note map only has entries for edges where multiple original fixed edges- * overlap or where a fixed edge is a part of original edge created by- * conforming Delaunay triangulation vertex insertion- */- unordered_map<Edge, EdgeVec> pieceToOriginals;-- /*____ API _____*/- /// Default constructor- Triangulation();- /**- * Constructor- * @param vertexInsertionOrder strategy used for ordering vertex insertions- */- Triangulation(VertexInsertionOrder::Enum vertexInsertionOrder);- /**- * Constructor- * @param vertexInsertionOrder strategy used for ordering vertex insertions- * @param intersectingEdgesStrategy strategy for treating intersecting- * constraint edges- * @param minDistToConstraintEdge distance within which point is considered- * to be lying on a constraint edge. Used when adding constraints to the- * triangulation.- */- Triangulation(- VertexInsertionOrder::Enum vertexInsertionOrder,- IntersectingConstraintEdges::Enum intersectingEdgesStrategy,- T minDistToConstraintEdge);- /**- * Constructor- * @param vertexInsertionOrder strategy used for ordering vertex insertions- * @param nearPtLocator class providing locating near point for efficiently- * inserting new points- * @param intersectingEdgesStrategy strategy for treating intersecting- * constraint edges- * @param minDistToConstraintEdge distance within which point is considered- * to be lying on a constraint edge. Used when adding constraints to the- * triangulation.- */- Triangulation(- VertexInsertionOrder::Enum vertexInsertionOrder,- const TNearPointLocator& nearPtLocator,- IntersectingConstraintEdges::Enum intersectingEdgesStrategy,- T minDistToConstraintEdge);- /**- * Insert custom point-types specified by iterator range and X/Y-getters- * @tparam TVertexIter iterator that dereferences to custom point type- * @tparam TGetVertexCoordX function object getting x coordinate from- * vertex. Getter signature: const TVertexIter::value_type& -> T- * @tparam TGetVertexCoordY function object getting y coordinate from- * vertex. Getter signature: const TVertexIter::value_type& -> T- * @param first beginning of the range of vertices to add- * @param last end of the range of vertices to add- * @param getX getter of X-coordinate- * @param getY getter of Y-coordinate- */- template <- typename TVertexIter,- typename TGetVertexCoordX,- typename TGetVertexCoordY>- void insertVertices(- TVertexIter first,- TVertexIter last,- TGetVertexCoordX getX,- TGetVertexCoordY getY);- /**- * Insert vertices into triangulation- * @param vertices vector of vertices to insert- */- void insertVertices(const std::vector<V2d<T> >& vertices);- /**- * Insert constraints (custom-type fixed edges) into triangulation- * @note Each fixed edge is inserted by deleting the triangles it crosses,- * followed by the triangulation of the polygons on each side of the edge.- * <b> No new vertices are inserted.</b>- * @note If some edge appears more than once in the input this means that- * multiple boundaries overlap at the edge and impacts how hole detection- * algorithm of Triangulation::eraseOuterTrianglesAndHoles works.- * <b>Make sure there are no erroneous duplicates.</b>- * @tparam TEdgeIter iterator that dereferences to custom edge type- * @tparam TGetEdgeVertexStart function object getting start vertex index- * from an edge.- * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd- * @tparam TGetEdgeVertexEnd function object getting end vertex index from- * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd- * @param first beginning of the range of edges to add- * @param last end of the range of edges to add- * @param getStart getter of edge start vertex index- * @param getEnd getter of edge end vertex index- */- template <- typename TEdgeIter,- typename TGetEdgeVertexStart,- typename TGetEdgeVertexEnd>- void insertEdges(- TEdgeIter first,- TEdgeIter last,- TGetEdgeVertexStart getStart,- TGetEdgeVertexEnd getEnd);- /**- * Insert constraint edges into triangulation- * @note Each fixed edge is inserted by deleting the triangles it crosses,- * followed by the triangulation of the polygons on each side of the edge.- * <b> No new vertices are inserted.</b>- * @note If some edge appears more than once in the input this means that- * multiple boundaries overlap at the edge and impacts how hole detection- * algorithm of Triangulation::eraseOuterTrianglesAndHoles works.- * <b>Make sure there are no erroneous duplicates.</b>- * @tparam edges constraint edges- */- void insertEdges(const std::vector<Edge>& edges);- /**- * Ensure that triangulation conforms to constraints (fixed edges)- * @note For each fixed edge that is not present in the triangulation its- * midpoint is recursively added until the original edge is represented by a- * sequence of its pieces. <b> New vertices are inserted.</b>- * @note If some edge appears more than once the input this- * means that multiple boundaries overlap at the edge and impacts how hole- * detection algorithm of Triangulation::eraseOuterTrianglesAndHoles works.- * <b>Make sure there are no erroneous duplicates.</b>- * @tparam TEdgeIter iterator that dereferences to custom edge type- * @tparam TGetEdgeVertexStart function object getting start vertex index- * from an edge.- * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd- * @tparam TGetEdgeVertexEnd function object getting end vertex index from- * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd- * @param first beginning of the range of edges to add- * @param last end of the range of edges to add- * @param getStart getter of edge start vertex index- * @param getEnd getter of edge end vertex index- */- template <- typename TEdgeIter,- typename TGetEdgeVertexStart,- typename TGetEdgeVertexEnd>- void conformToEdges(- TEdgeIter first,- TEdgeIter last,- TGetEdgeVertexStart getStart,- TGetEdgeVertexEnd getEnd);- /**- * Ensure that triangulation conforms to constraints (fixed edges)- * @note For each fixed edge that is not present in the triangulation its- * midpoint is recursively added until the original edge is represented by a- * sequence of its pieces. <b> New vertices are inserted.</b>- * @note If some edge appears more than once the input this- * means that multiple boundaries overlap at the edge and impacts how hole- * detection algorithm of Triangulation::eraseOuterTrianglesAndHoles works.- * <b>Make sure there are no erroneous duplicates.</b>- * @tparam edges edges to conform to- */- void conformToEdges(const std::vector<Edge>& edges);- /**- * Erase triangles adjacent to super triangle- *- * @note does nothing if custom geometry is used- */- void eraseSuperTriangle();- /// Erase triangles outside of constrained boundary using growing- void eraseOuterTriangles();- /**- * Erase triangles outside of constrained boundary and auto-detected holes- *- * @note detecting holes relies on layer peeling based on layer depth- * @note supports overlapping or touching boundaries- */- void eraseOuterTrianglesAndHoles();- /**- * Call this method after directly setting custom super-geometry via- * vertices and triangles members- */- void initializedWithCustomSuperGeometry();-- /**- * Check if the triangulation was finalized with `erase...` method and- * super-triangle was removed.- * @return true if triangulation is finalized, false otherwise- */- bool isFinalized() const;-- /**- * Calculate depth of each triangle in constraint triangulation. Supports- * overlapping boundaries.- *- * Perform depth peeling from super triangle to outermost boundary,- * then to next boundary and so on until all triangles are traversed.@n- * For example depth is:- * - 0 for triangles outside outermost boundary- * - 1 for triangles inside boundary but outside hole- * - 2 for triangles in hole- * - 3 for triangles in island and so on...- * @return vector where element at index i stores depth of i-th triangle- */- std::vector<LayerDepth> calculateTriangleDepths() const;-- /**- * @defgroup Advanced Advanced Triangulation Methods- * Advanced methods for manually modifying the triangulation from- * outside. Please only use them when you know what you are doing.- */- /// @{-- /**- * Flip an edge between two triangle.- * @note Advanced method for manually modifying the triangulation from- * outside. Please call it when you know what you are doing.- * @param iT first triangle- * @param iTopo second triangle-- */- void flipEdge(TriInd iT, TriInd iTopo);-- /**- * Remove triangles with specified indices.- * Adjust internal triangulation state accordingly.- * @param removedTriangles indices of triangles to remove- */- void removeTriangles(const TriIndUSet& removedTriangles);- /// @}--private:- /*____ Detail __*/- void addSuperTriangle(const Box2d<T>& box);- void addNewVertex(const V2d<T>& pos, const TriIndVec& tris);- void insertVertex(VertInd iVert);- void ensureDelaunayByEdgeFlips(- const V2d<T>& v,- VertInd iVert,- std::stack<TriInd>& triStack);- /// Flip fixed edges and return a list of flipped fixed edges- std::vector<Edge> insertVertex_FlipFixedEdges(VertInd iVert);- /**- * Insert an edge into constraint Delaunay triangulation- * @param edge edge to insert- * @param originalEdge original edge inserted edge is part of- */- void insertEdge(Edge edge, Edge originalEdge);- /**- * Conform Delaunay triangulation to a fixed edge by recursively inserting- * mid point of the edge and then conforming to its halves- * @param edge fixed edge to conform to- * @param originalEdges original edges that new edge is piece of- * @param overlaps count of overlapping boundaries at the edge. Only used- * when re-introducing edge with overlaps > 0- * @param orientationTolerance tolerance for orient2d predicate,- * values [-tolerance,+tolerance] are considered as 0.- */- void conformToEdge(- Edge edge,- EdgeVec originalEdges,- BoundaryOverlapCount overlaps);- tuple<TriInd, VertInd, VertInd> intersectedTriangle(- VertInd iA,- const std::vector<TriInd>& candidates,- const V2d<T>& a,- const V2d<T>& b,- T orientationTolerance = T(0)) const;- /// Returns indices of three resulting triangles- std::stack<TriInd> insertPointInTriangle(VertInd v, TriInd iT);- /// Returns indices of four resulting triangles- std::stack<TriInd> insertPointOnEdge(VertInd v, TriInd iT1, TriInd iT2);- array<TriInd, 2> trianglesAt(const V2d<T>& pos) const;- array<TriInd, 2> walkingSearchTrianglesAt(const V2d<T>& pos) const;- TriInd walkTriangles(VertInd startVertex, const V2d<T>& pos) const;- bool isFlipNeeded(- const V2d<T>& v,- VertInd iV,- VertInd iV1,- VertInd iV2,- VertInd iV3) const;- bool- isFlipNeeded(const V2d<T>& v, TriInd iT, TriInd iTopo, VertInd iVert) const;- void changeNeighbor(TriInd iT, TriInd oldNeighbor, TriInd newNeighbor);- void changeNeighbor(- TriInd iT,- VertInd iVedge1,- VertInd iVedge2,- TriInd newNeighbor);- void addAdjacentTriangle(VertInd iVertex, TriInd iTriangle);- void- addAdjacentTriangles(VertInd iVertex, TriInd iT1, TriInd iT2, TriInd iT3);- void addAdjacentTriangles(- VertInd iVertex,- TriInd iT1,- TriInd iT2,- TriInd iT3,- TriInd iT4);- void removeAdjacentTriangle(VertInd iVertex, TriInd iTriangle);- TriInd triangulatePseudopolygon(- VertInd ia,- VertInd ib,- std::vector<VertInd>::const_iterator pointsFirst,- std::vector<VertInd>::const_iterator pointsLast);- VertInd findDelaunayPoint(- VertInd ia,- VertInd ib,- std::vector<VertInd>::const_iterator pointsFirst,- std::vector<VertInd>::const_iterator pointsLast) const;- TriInd pseudopolyOuterTriangle(VertInd ia, VertInd ib) const;- TriInd addTriangle(const Triangle& t); // note: invalidates iterators!- TriInd addTriangle(); // note: invalidates triangle iterators!- /**- * Remove super-triangle (if used) and triangles with specified indices.- * Adjust internal triangulation state accordingly.- * @removedTriangles indices of triangles to remove- */- void finalizeTriangulation(const TriIndUSet& removedTriangles);- TriIndUSet growToBoundary(std::stack<TriInd> seeds) const;- void fixEdge(const Edge& edge, BoundaryOverlapCount overlaps);- void fixEdge(const Edge& edge);- void fixEdge(const Edge& edge, const Edge& originalEdge);- /**- * Flag triangle as dummy- * @note Advanced method for manually modifying the triangulation from- * outside. Please call it when you know what you are doing.- * @param iT index of a triangle to flag- */- void makeDummy(TriInd iT);- /**- * Erase all dummy triangles- * @note Advanced method for manually modifying the triangulation from- * outside. Please call it when you know what you are doing.- */- void eraseDummies();- /**- * Depth-peel a layer in triangulation, used when calculating triangle- * depths- *- * It takes starting seed triangles, traverses neighboring triangles, and- * assigns given layer depth to the traversed triangles. Traversal is- * blocked by constraint edges. Triangles behind constraint edges are- * recorded as seeds of next layer and returned from the function.- *- * @param seeds indices of seed triangles- * @param layerDepth current layer's depth to mark triangles with- * @param[in, out] triDepths depths of triangles- * @return triangles of the deeper layers that are adjacent to the peeled- * layer. To be used as seeds when peeling deeper layers.- */- unordered_map<TriInd, LayerDepth> peelLayer(- std::stack<TriInd> seeds,- LayerDepth layerDepth,- std::vector<LayerDepth>& triDepths) const;-- std::vector<TriInd> m_dummyTris;- TNearPointLocator m_nearPtLocator;- std::size_t m_nTargetVerts;- SuperGeometryType::Enum m_superGeomType;- VertexInsertionOrder::Enum m_vertexInsertionOrder;- IntersectingConstraintEdges::Enum m_intersectingEdgesStrategy;- T m_minDistToConstraintEdge;-};--/// @}-/// @}--namespace detail-{--static mt19937 randGenerator(9001);--template <class RandomIt>-void random_shuffle(RandomIt first, RandomIt last)-{- typename std::iterator_traits<RandomIt>::difference_type i, n;- n = last - first;- for(i = n - 1; i > 0; --i)- {- std::swap(first[i], first[randGenerator() % (i + 1)]);- }-}--} // namespace detail--//------------------------// Triangulation methods-//------------------------template <typename T, typename TNearPointLocator>-template <- typename TVertexIter,- typename TGetVertexCoordX,- typename TGetVertexCoordY>-void Triangulation<T, TNearPointLocator>::insertVertices(- const TVertexIter first,- const TVertexIter last,- TGetVertexCoordX getX,- TGetVertexCoordY getY)-{- if(isFinalized())- {- throw std::runtime_error(- "Triangulation was finalized with 'erase...' method. Inserting new "- "vertices is not possible");- }- detail::randGenerator.seed(9001); // ensure deterministic behavior- if(vertices.empty())- {- addSuperTriangle(envelopBox<T>(first, last, getX, getY));- }-- const std::size_t nExistingVerts = vertices.size();-- vertices.reserve(nExistingVerts + std::distance(first, last));- for(TVertexIter it = first; it != last; ++it)- addNewVertex(V2d<T>::make(getX(*it), getY(*it)), TriIndVec());-- switch(m_vertexInsertionOrder)- {- case VertexInsertionOrder::AsProvided:- for(TVertexIter it = first; it != last; ++it)- insertVertex(VertInd(nExistingVerts + std::distance(first, it)));- break;- case VertexInsertionOrder::Randomized:- std::vector<VertInd> ii(std::distance(first, last));- typedef std::vector<VertInd>::iterator Iter;- VertInd value = static_cast<VertInd>(nExistingVerts);- for(Iter it = ii.begin(); it != ii.end(); ++it, ++value)- *it = value;- detail::random_shuffle(ii.begin(), ii.end());- for(Iter it = ii.begin(); it != ii.end(); ++it)- insertVertex(*it);- break;- }-}--template <typename T, typename TNearPointLocator>-template <- typename TEdgeIter,- typename TGetEdgeVertexStart,- typename TGetEdgeVertexEnd>-void Triangulation<T, TNearPointLocator>::insertEdges(- TEdgeIter first,- const TEdgeIter last,- TGetEdgeVertexStart getStart,- TGetEdgeVertexEnd getEnd)-{- if(isFinalized())- {- throw std::runtime_error(- "Triangulation was finalized with 'erase...' method. Inserting new "- "edges is not possible");- }- for(; first != last; ++first)- {- // +3 to account for super-triangle vertices- const Edge edge(- VertInd(getStart(*first) + m_nTargetVerts),- VertInd(getEnd(*first) + m_nTargetVerts));- insertEdge(edge, edge);- }- eraseDummies();-}--template <typename T, typename TNearPointLocator>-template <- typename TEdgeIter,- typename TGetEdgeVertexStart,- typename TGetEdgeVertexEnd>-void Triangulation<T, TNearPointLocator>::conformToEdges(- TEdgeIter first,- const TEdgeIter last,- TGetEdgeVertexStart getStart,- TGetEdgeVertexEnd getEnd)-{- if(isFinalized())- {- throw std::runtime_error(- "Triangulation was finalized with 'erase...' method. Conforming to "- "new edges is not possible");- }- for(; first != last; ++first)- {- // +3 to account for super-triangle vertices- const Edge e(- VertInd(getStart(*first) + m_nTargetVerts),- VertInd(getEnd(*first) + m_nTargetVerts));- conformToEdge(e, EdgeVec(1, e), 0);- }- eraseDummies();-}--} // namespace CDT--#ifndef CDT_USE_AS_COMPILED_LIBRARY-#include "Triangulation.hpp"-#endif--#endif // header-guard+/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +/** + * @file + * Triangulation class + */ + +#ifndef CDT_vW1vZ0lO8rS4gY4uI4fB +#define CDT_vW1vZ0lO8rS4gY4uI4fB + +#include "CDTUtils.h" +#include "LocatorKDTree.h" + +#include <algorithm> +#include <cstdlib> +#include <iterator> +#include <stack> +#include <stdexcept> +#include <utility> +#include <vector> + +/// Namespace containing triangulation functionality +namespace CDT +{ + +/// @addtogroup API +/// @{ + +/** + * Enum of strategies specifying order in which a range of vertices is inserted + * @note VertexInsertionOrder::Randomized will only randomize order of + * inserting in triangulation, vertex indices will be preserved as they were + * specified in the final triangulation + */ +struct CDT_EXPORT VertexInsertionOrder +{ + /** + * The Enum itself + * @note needed to pre c++11 compilers that don't support 'class enum' + */ + enum Enum + { + /** + * Automatic insertion order optimized for better performance + * @details breadth-first traversal of a Kd-tree for initial bulk-load, + * randomized for subsequent insertions + */ + Auto, + /// insert vertices in same order they are provided + AsProvided, + }; +}; + +/// Enum of what type of geometry used to embed triangulation into +struct CDT_EXPORT SuperGeometryType +{ + /** + * The Enum itself + * @note needed to pre c++11 compilers that don't support 'class enum' + */ + enum Enum + { + SuperTriangle, ///< conventional super-triangle + Custom, ///< user-specified custom geometry (e.g., grid) + }; +}; + +/** + * Enum of strategies for treating intersecting constraint edges + */ +struct CDT_EXPORT IntersectingConstraintEdges +{ + /** + * The Enum itself + * @note needed to pre c++11 compilers that don't support 'class enum' + */ + enum Enum + { + Ignore, ///< constraint edge intersections are not checked + Resolve, ///< constraint edge intersections are resolved + }; +}; + +/** + * Type used for storing layer depths for triangles + * @note LayerDepth should support 60K+ layers, which could be to much or + * too little for some use cases. Feel free to re-define this typedef. + */ +typedef unsigned short LayerDepth; +typedef LayerDepth BoundaryOverlapCount; + +/** + * @defgroup Triangulation Triangulation Class + * Class performing triangulations. + */ +/// @{ + +/** + * Data structure representing a 2D constrained Delaunay triangulation + * + * @tparam T type of vertex coordinates (e.g., float, double) + * @tparam TNearPointLocator class providing locating near point for efficiently + * inserting new points. Provides methods: 'addPoint(vPos, iV)' and + * 'nearPoint(vPos) -> iV' + */ +template <typename T, typename TNearPointLocator = LocatorKDTree<T> > +class CDT_EXPORT Triangulation +{ +public: + typedef std::vector<V2d<T> > V2dVec; ///< Vertices vector + V2dVec vertices; ///< triangulation's vertices + TriangleVec triangles; ///< triangulation's triangles + EdgeUSet fixedEdges; ///< triangulation's constraints (fixed edges) + + /** Stores count of overlapping boundaries for a fixed edge. If no entry is + * present for an edge: no boundaries overlap. + * @note map only has entries for fixed for edges that represent overlapping + * boundaries + * @note needed for handling depth calculations and hole-removel in case of + * overlapping boundaries + */ + unordered_map<Edge, BoundaryOverlapCount> overlapCount; + + /** Stores list of original edges represented by a given fixed edge + * @note map only has entries for edges where multiple original fixed edges + * overlap or where a fixed edge is a part of original edge created by + * conforming Delaunay triangulation vertex insertion + */ + unordered_map<Edge, EdgeVec> pieceToOriginals; + + /*____ API _____*/ + /// Default constructor + Triangulation(); + /** + * Constructor + * @param vertexInsertionOrder strategy used for ordering vertex insertions + */ + explicit Triangulation(VertexInsertionOrder::Enum vertexInsertionOrder); + /** + * Constructor + * @param vertexInsertionOrder strategy used for ordering vertex insertions + * @param intersectingEdgesStrategy strategy for treating intersecting + * constraint edges + * @param minDistToConstraintEdge distance within which point is considered + * to be lying on a constraint edge. Used when adding constraints to the + * triangulation. + */ + Triangulation( + VertexInsertionOrder::Enum vertexInsertionOrder, + IntersectingConstraintEdges::Enum intersectingEdgesStrategy, + T minDistToConstraintEdge); + /** + * Constructor + * @param vertexInsertionOrder strategy used for ordering vertex insertions + * @param nearPtLocator class providing locating near point for efficiently + * inserting new points + * @param intersectingEdgesStrategy strategy for treating intersecting + * constraint edges + * @param minDistToConstraintEdge distance within which point is considered + * to be lying on a constraint edge. Used when adding constraints to the + * triangulation. + */ + Triangulation( + VertexInsertionOrder::Enum vertexInsertionOrder, + const TNearPointLocator& nearPtLocator, + IntersectingConstraintEdges::Enum intersectingEdgesStrategy, + T minDistToConstraintEdge); + /** + * Insert custom point-types specified by iterator range and X/Y-getters + * @tparam TVertexIter iterator that dereferences to custom point type + * @tparam TGetVertexCoordX function object getting x coordinate from + * vertex. Getter signature: const TVertexIter::value_type& -> T + * @tparam TGetVertexCoordY function object getting y coordinate from + * vertex. Getter signature: const TVertexIter::value_type& -> T + * @param first beginning of the range of vertices to add + * @param last end of the range of vertices to add + * @param getX getter of X-coordinate + * @param getY getter of Y-coordinate + */ + template < + typename TVertexIter, + typename TGetVertexCoordX, + typename TGetVertexCoordY> + void insertVertices( + TVertexIter first, + TVertexIter last, + TGetVertexCoordX getX, + TGetVertexCoordY getY); + /** + * Insert vertices into triangulation + * @param vertices vector of vertices to insert + */ + void insertVertices(const std::vector<V2d<T> >& vertices); + /** + * Insert constraints (custom-type fixed edges) into triangulation + * @note Each fixed edge is inserted by deleting the triangles it crosses, + * followed by the triangulation of the polygons on each side of the edge. + * <b> No new vertices are inserted.</b> + * @note If some edge appears more than once in the input this means that + * multiple boundaries overlap at the edge and impacts how hole detection + * algorithm of Triangulation::eraseOuterTrianglesAndHoles works. + * <b>Make sure there are no erroneous duplicates.</b> + * @tparam TEdgeIter iterator that dereferences to custom edge type + * @tparam TGetEdgeVertexStart function object getting start vertex index + * from an edge. + * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd + * @tparam TGetEdgeVertexEnd function object getting end vertex index from + * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd + * @param first beginning of the range of edges to add + * @param last end of the range of edges to add + * @param getStart getter of edge start vertex index + * @param getEnd getter of edge end vertex index + */ + template < + typename TEdgeIter, + typename TGetEdgeVertexStart, + typename TGetEdgeVertexEnd> + void insertEdges( + TEdgeIter first, + TEdgeIter last, + TGetEdgeVertexStart getStart, + TGetEdgeVertexEnd getEnd); + /** + * Insert constraint edges into triangulation + * @note Each fixed edge is inserted by deleting the triangles it crosses, + * followed by the triangulation of the polygons on each side of the edge. + * <b> No new vertices are inserted.</b> + * @note If some edge appears more than once in the input this means that + * multiple boundaries overlap at the edge and impacts how hole detection + * algorithm of Triangulation::eraseOuterTrianglesAndHoles works. + * <b>Make sure there are no erroneous duplicates.</b> + * @tparam edges constraint edges + */ + void insertEdges(const std::vector<Edge>& edges); + /** + * Ensure that triangulation conforms to constraints (fixed edges) + * @note For each fixed edge that is not present in the triangulation its + * midpoint is recursively added until the original edge is represented by a + * sequence of its pieces. <b> New vertices are inserted.</b> + * @note If some edge appears more than once the input this + * means that multiple boundaries overlap at the edge and impacts how hole + * detection algorithm of Triangulation::eraseOuterTrianglesAndHoles works. + * <b>Make sure there are no erroneous duplicates.</b> + * @tparam TEdgeIter iterator that dereferences to custom edge type + * @tparam TGetEdgeVertexStart function object getting start vertex index + * from an edge. + * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd + * @tparam TGetEdgeVertexEnd function object getting end vertex index from + * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd + * @param first beginning of the range of edges to add + * @param last end of the range of edges to add + * @param getStart getter of edge start vertex index + * @param getEnd getter of edge end vertex index + */ + template < + typename TEdgeIter, + typename TGetEdgeVertexStart, + typename TGetEdgeVertexEnd> + void conformToEdges( + TEdgeIter first, + TEdgeIter last, + TGetEdgeVertexStart getStart, + TGetEdgeVertexEnd getEnd); + /** + * Ensure that triangulation conforms to constraints (fixed edges) + * @note For each fixed edge that is not present in the triangulation its + * midpoint is recursively added until the original edge is represented by a + * sequence of its pieces. <b> New vertices are inserted.</b> + * @note If some edge appears more than once the input this + * means that multiple boundaries overlap at the edge and impacts how hole + * detection algorithm of Triangulation::eraseOuterTrianglesAndHoles works. + * <b>Make sure there are no erroneous duplicates.</b> + * @tparam edges edges to conform to + */ + void conformToEdges(const std::vector<Edge>& edges); + /** + * Erase triangles adjacent to super triangle + * + * @note does nothing if custom geometry is used + */ + void eraseSuperTriangle(); + /// Erase triangles outside of constrained boundary using growing + void eraseOuterTriangles(); + /** + * Erase triangles outside of constrained boundary and auto-detected holes + * + * @note detecting holes relies on layer peeling based on layer depth + * @note supports overlapping or touching boundaries + */ + void eraseOuterTrianglesAndHoles(); + /** + * Call this method after directly setting custom super-geometry via + * vertices and triangles members + */ + void initializedWithCustomSuperGeometry(); + + /** + * Check if the triangulation was finalized with `erase...` method and + * super-triangle was removed. + * @return true if triangulation is finalized, false otherwise + */ + bool isFinalized() const; + + /** + * Calculate depth of each triangle in constraint triangulation. Supports + * overlapping boundaries. + * + * Perform depth peeling from super triangle to outermost boundary, + * then to next boundary and so on until all triangles are traversed.@n + * For example depth is: + * - 0 for triangles outside outermost boundary + * - 1 for triangles inside boundary but outside hole + * - 2 for triangles in hole + * - 3 for triangles in island and so on... + * @return vector where element at index i stores depth of i-th triangle + */ + std::vector<LayerDepth> calculateTriangleDepths() const; + + /** + * @defgroup Advanced Advanced Triangulation Methods + * Advanced methods for manually modifying the triangulation from + * outside. Please only use them when you know what you are doing. + */ + /// @{ + + /** + * Flip an edge between two triangle. + * @note Advanced method for manually modifying the triangulation from + * outside. Please call it when you know what you are doing. + * @param iT first triangle + * @param iTopo second triangle + */ + void flipEdge(TriInd iT, TriInd iTopo); + + void flipEdge( + TriInd iT, + TriInd iTopo, + VertInd v1, + VertInd v2, + VertInd v3, + VertInd v4, + TriInd n1, + TriInd n2, + TriInd n3, + TriInd n4); + + /** + * Remove triangles with specified indices. + * Adjust internal triangulation state accordingly. + * @param removedTriangles indices of triangles to remove + */ + void removeTriangles(const TriIndUSet& removedTriangles); + + /// Access internal vertex adjacent triangles + TriIndVec& VertTrisInternal(); + /// Access internal vertex adjacent triangles + const TriIndVec& VertTrisInternal() const; + /// @} + +private: + /*____ Detail __*/ + void addSuperTriangle(const Box2d<T>& box); + void addNewVertex(const V2d<T>& pos, TriInd iT); + void insertVertex(VertInd iVert); + void insertVertex(VertInd iVert, VertInd walkStart); + void ensureDelaunayByEdgeFlips( + const V2d<T>& v1, + VertInd iV1, + std::stack<TriInd>& triStack); + /// Flip fixed edges and return a list of flipped fixed edges + std::vector<Edge> insertVertex_FlipFixedEdges(VertInd iV1); + + /// State for an iteration of triangulate pseudo-polygon + typedef tuple<IndexSizeType, IndexSizeType, TriInd, TriInd, Index> + TriangulatePseudopolygonTask; + + /** + * Insert an edge into constraint Delaunay triangulation + * @param edge edge to insert + * @param originalEdge original edge inserted edge is part of + * @param[in,out] remaining parts of the edge that still need to + * be inserted + * @param[in,out] tppIterations stack to be used for storing iterations of + * triangulating pseudo-polygon + * @note in-out state (@param remaining @param tppIterations) is shared + * between different runs for performance gains (reducing memory + * allocations) + */ + void insertEdge( + Edge edge, + Edge originalEdge, + EdgeVec& remaining, + std::vector<TriangulatePseudopolygonTask>& tppIterations); + + /** + * Insert an edge or its part into constraint Delaunay triangulation + * @param edge edge to insert + * @param originalEdge original edge inserted edge is part of + * @param[in,out] remainingStack parts of the edge that still need to + * be inserted + * @param[in,out] tppIterations stack to be used for storing iterations of + * triangulating pseudo-polygon + * @note in-out state (@param remaining @param tppIterations) is shared + * between different runs for performance gains (reducing memory + * allocations) + */ + void insertEdgeIteration( + Edge edge, + Edge originalEdge, + EdgeVec& remaining, + std::vector<TriangulatePseudopolygonTask>& tppIterations); + + /// State for iteration of conforming to edge + typedef tuple<Edge, EdgeVec, BoundaryOverlapCount> ConformToEdgeTask; + + /** + * Conform Delaunay triangulation to a fixed edge by recursively inserting + * mid point of the edge and then conforming to its halves + * @param edge fixed edge to conform to + * @param originals original edges that new edge is piece of + * @param overlaps count of overlapping boundaries at the edge. Only used + * when re-introducing edge with overlaps > 0 + * @param[in,out] remaining remaining edge parts to be conformed to + * @note in-out state (@param remaining @param reintroduce) is shared + * between different runs for performance gains (reducing memory + * allocations) + */ + void conformToEdge( + Edge edge, + EdgeVec originals, + BoundaryOverlapCount overlaps, + std::vector<ConformToEdgeTask>& remaining); + + /** + * Iteration of conform to fixed edge. + * @param edge fixed edge to conform to + * @param originals original edges that new edge is piece of + * @param overlaps count of overlapping boundaries at the edge. Only used + * when re-introducing edge with overlaps > 0 + * @param[in,out] remaining remaining edge parts + * @note in-out state (@param remaining @param reintroduce) is shared + * between different runs for performance gains (reducing memory + * allocations) + */ + void conformToEdgeIteration( + Edge edge, + const EdgeVec& originals, + BoundaryOverlapCount overlaps, + std::vector<ConformToEdgeTask>& remaining); + + tuple<TriInd, VertInd, VertInd> intersectedTriangle( + VertInd iA, + const V2d<T>& a, + const V2d<T>& b, + T orientationTolerance = T(0)) const; + /// Returns indices of three resulting triangles + std::stack<TriInd> insertVertexInsideTriangle(VertInd v, TriInd iT); + /// Returns indices of four resulting triangles + std::stack<TriInd> insertVertexOnEdge(VertInd v, TriInd iT1, TriInd iT2); + array<TriInd, 2> trianglesAt(const V2d<T>& pos) const; + array<TriInd, 2> + walkingSearchTrianglesAt(const V2d<T>& pos, VertInd startVertex) const; + TriInd walkTriangles(VertInd startVertex, const V2d<T>& pos) const; + /// Given triangle and its vertex find opposite triangle and the other three + /// vertices and surrounding neighbors + void edgeFlipInfo( + TriInd iT, + VertInd iV1, + TriInd& iTopo, + VertInd& iV2, + VertInd& iV3, + VertInd& iV4, + TriInd& n1, + TriInd& n2, + TriInd& n3, + TriInd& n4); + bool isFlipNeeded( + const V2d<T>& v, + VertInd iV1, + VertInd iV2, + VertInd iV3, + VertInd iV4) const; + void changeNeighbor(TriInd iT, TriInd oldNeighbor, TriInd newNeighbor); + void changeNeighbor( + TriInd iT, + VertInd iVedge1, + VertInd iVedge2, + TriInd newNeighbor); + void triangulatePseudopolygon( + const std::vector<VertInd>& poly, + const std::vector<TriInd>& outerTris, + TriInd iT, + TriInd iN, + std::vector<TriangulatePseudopolygonTask>& iterations); + void triangulatePseudopolygonIteration( + const std::vector<VertInd>& poly, + const std::vector<TriInd>& outerTris, + std::vector<TriangulatePseudopolygonTask>& iterations); + IndexSizeType findDelaunayPoint( + const std::vector<VertInd>& poly, + IndexSizeType iA, + IndexSizeType iB) const; + TriInd addTriangle(const Triangle& t); // note: invalidates iterators! + TriInd addTriangle(); // note: invalidates triangle iterators! + /** + * Remove super-triangle (if used) and triangles with specified indices. + * Adjust internal triangulation state accordingly. + * @removedTriangles indices of triangles to remove + */ + void finalizeTriangulation(const TriIndUSet& removedTriangles); + TriIndUSet growToBoundary(std::stack<TriInd> seeds) const; + void fixEdge(const Edge& edge); + void fixEdge(const Edge& edge, const Edge& originalEdge); + /** + * Split existing constraint (fixed) edge + * @param edge fixed edge to split + * @param iSplitVert index of the vertex to be used as a split vertex + */ + void splitFixedEdge(const Edge& edge, const VertInd iSplitVert); + /** + * Add a vertex that splits an edge into the triangulation + * @param splitVert position of split vertex + * @param iT index of a first triangle adjacent to the split edge + * @param iTopo index of a second triangle adjacent to the split edge + * (opposed to the first triangle) + * @return index of a newly added split vertex + */ + VertInd addSplitEdgeVertex( + const V2d<T>& splitVert, + const TriInd iT, + const TriInd iTopo); + /** + * Split fixed edge and add a split vertex into the triangulation + * @param edge fixed edge to split + * @param splitVert position of split vertex + * @param iT index of a first triangle adjacent to the split edge + * @param iTopo index of a second triangle adjacent to the split edge + * (opposed to the first triangle) + * @return index of a newly added split vertex + */ + VertInd splitFixedEdgeAt( + const Edge& edge, + const V2d<T>& splitVert, + const TriInd iT, + const TriInd iTopo); + /** + * Flag triangle as dummy + * @note Advanced method for manually modifying the triangulation from + * outside. Please call it when you know what you are doing. + * @param iT index of a triangle to flag + */ + void makeDummy(TriInd iT); + /** + * Erase all dummy triangles + * @note Advanced method for manually modifying the triangulation from + * outside. Please call it when you know what you are doing. + */ + void eraseDummies(); + /** + * Depth-peel a layer in triangulation, used when calculating triangle + * depths + * + * It takes starting seed triangles, traverses neighboring triangles, and + * assigns given layer depth to the traversed triangles. Traversal is + * blocked by constraint edges. Triangles behind constraint edges are + * recorded as seeds of next layer and returned from the function. + * + * @param seeds indices of seed triangles + * @param layerDepth current layer's depth to mark triangles with + * @param[in, out] triDepths depths of triangles + * @return triangles of the deeper layers that are adjacent to the peeled + * layer. To be used as seeds when peeling deeper layers. + */ + unordered_map<TriInd, LayerDepth> peelLayer( + std::stack<TriInd> seeds, + LayerDepth layerDepth, + std::vector<LayerDepth>& triDepths) const; + + void insertVertices_AsProvided(VertInd superGeomVertCount); + void insertVertices_Randomized(VertInd superGeomVertCount); + void insertVertices_KDTreeBFS( + VertInd superGeomVertCount, + V2d<T> boxMin, + V2d<T> boxMax); + std::pair<TriInd, TriInd> edgeTriangles(VertInd a, VertInd b) const; + bool hasEdge(VertInd a, VertInd b) const; + void setAdjacentTriangle(const VertInd v, const TriInd t); + void pivotVertexTriangleCW(VertInd v); + /// Add vertex to nearest-point locator if locator is initialized + void tryAddVertexToLocator(const VertInd v); + /// Perform lazy initialization of nearest-point locator after the Kd-tree + /// BFS bulk load if necessary + void tryInitNearestPointLocator(); + + std::vector<TriInd> m_dummyTris; + TNearPointLocator m_nearPtLocator; + std::size_t m_nTargetVerts; + SuperGeometryType::Enum m_superGeomType; + VertexInsertionOrder::Enum m_vertexInsertionOrder; + IntersectingConstraintEdges::Enum m_intersectingEdgesStrategy; + T m_minDistToConstraintEdge; + TriIndVec m_vertTris; /// one triangle adjacent to each vertex +}; + +/// @} +/// @} + +namespace detail +{ + +/// SplitMix64 pseudo-random number generator +struct SplitMix64RandGen +{ + typedef unsigned long long uint64; + uint64 m_state; + explicit SplitMix64RandGen(uint64 state) + : m_state(state) + {} + explicit SplitMix64RandGen() + : m_state(0) + {} + uint64 operator()() + { + uint64 z = (m_state += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); + } +}; + +template <class RandomIt> +void random_shuffle(RandomIt first, RandomIt last) +{ + detail::SplitMix64RandGen prng; + typename std::iterator_traits<RandomIt>::difference_type i, n; + n = last - first; + for(i = n - 1; i > 0; --i) + { + std::swap(first[i], first[prng() % (i + 1)]); + } +} + +// backport from c++11 +template <class ForwardIt, class T> +void iota(ForwardIt first, ForwardIt last, T value) +{ + while(first != last) + { + *first++ = value; + ++value; + } +} + +} // namespace detail + +//----------------------- +// Triangulation methods +//----------------------- +template <typename T, typename TNearPointLocator> +template < + typename TVertexIter, + typename TGetVertexCoordX, + typename TGetVertexCoordY> +void Triangulation<T, TNearPointLocator>::insertVertices( + const TVertexIter first, + const TVertexIter last, + TGetVertexCoordX getX, + TGetVertexCoordY getY) +{ + if(isFinalized()) + { + throw std::runtime_error( + "Triangulation was finalized with 'erase...' method. Inserting new " + "vertices is not possible"); + } + + const bool isFirstTime = vertices.empty(); + const T max = std::numeric_limits<T>::max(); + Box2d<T> box = {{max, max}, {-max, -max}}; + if(vertices.empty()) // called first time + { + box = envelopBox<T>(first, last, getX, getY); + addSuperTriangle(box); + } + tryInitNearestPointLocator(); + + const VertInd nExistingVerts = static_cast<VertInd>(vertices.size()); + const VertInd nVerts = + static_cast<VertInd>(nExistingVerts + std::distance(first, last)); + // optimization, try to pre-allocate tris + triangles.reserve(triangles.size() + 2 * nVerts); + vertices.reserve(nVerts); + m_vertTris.reserve(nVerts); + for(TVertexIter it = first; it != last; ++it) + addNewVertex(V2d<T>::make(getX(*it), getY(*it)), noNeighbor); + + switch(m_vertexInsertionOrder) + { + case VertexInsertionOrder::AsProvided: + insertVertices_AsProvided(nExistingVerts); + break; + case VertexInsertionOrder::Auto: + isFirstTime ? insertVertices_KDTreeBFS(nExistingVerts, box.min, box.max) + : insertVertices_Randomized(nExistingVerts); + break; + } +} + +template <typename T, typename TNearPointLocator> +template < + typename TEdgeIter, + typename TGetEdgeVertexStart, + typename TGetEdgeVertexEnd> +void Triangulation<T, TNearPointLocator>::insertEdges( + TEdgeIter first, + const TEdgeIter last, + TGetEdgeVertexStart getStart, + TGetEdgeVertexEnd getEnd) +{ + // state shared between different runs for performance gains + std::vector<TriangulatePseudopolygonTask> tppIterations; + EdgeVec remaining; + if(isFinalized()) + { + throw std::runtime_error( + "Triangulation was finalized with 'erase...' method. Inserting new " + "edges is not possible"); + } + for(; first != last; ++first) + { + // +3 to account for super-triangle vertices + const Edge edge( + VertInd(getStart(*first) + m_nTargetVerts), + VertInd(getEnd(*first) + m_nTargetVerts)); + insertEdge(edge, edge, remaining, tppIterations); + } + eraseDummies(); +} + +template <typename T, typename TNearPointLocator> +template < + typename TEdgeIter, + typename TGetEdgeVertexStart, + typename TGetEdgeVertexEnd> +void Triangulation<T, TNearPointLocator>::conformToEdges( + TEdgeIter first, + const TEdgeIter last, + TGetEdgeVertexStart getStart, + TGetEdgeVertexEnd getEnd) +{ + if(isFinalized()) + { + throw std::runtime_error( + "Triangulation was finalized with 'erase...' method. Conforming to " + "new edges is not possible"); + } + tryInitNearestPointLocator(); + // state shared between different runs for performance gains + std::vector<ConformToEdgeTask> remaining; + for(; first != last; ++first) + { + // +3 to account for super-triangle vertices + const Edge e( + VertInd(getStart(*first) + m_nTargetVerts), + VertInd(getEnd(*first) + m_nTargetVerts)); + conformToEdge(e, EdgeVec(1, e), 0, remaining); + } + eraseDummies(); +} + +} // namespace CDT + +#ifndef CDT_USE_AS_COMPILED_LIBRARY +#include "Triangulation.hpp" +#endif + +#endif // header-guard
cpp/Triangulation.hpp view
@@ -1,1558 +1,2041 @@-/* This Source Code Form is subject to the terms of the Mozilla Public- * License, v. 2.0. If a copy of the MPL was not distributed with this- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */--/**- * @file- * Triangulation class - implementation- */--#include "Triangulation.h"--#include <algorithm>-#include <cassert>-#include <deque>-#include <stdexcept>--namespace CDT-{--typedef std::deque<TriInd> TriDeque;--namespace detail-{--/// Needed for c++03 compatibility (no uniform initialization available)-template <typename T>-array<T, 3> arr3(const T& v0, const T& v1, const T& v2)-{- const array<T, 3> out = {v0, v1, v2};- return out;-}--namespace defaults-{--const std::size_t nTargetVerts = 0;-const SuperGeometryType::Enum superGeomType = SuperGeometryType::SuperTriangle;-const VertexInsertionOrder::Enum vertexInsertionOrder =- VertexInsertionOrder::Randomized;-const IntersectingConstraintEdges::Enum intersectingEdgesStrategy =- IntersectingConstraintEdges::Ignore;-const float minDistToConstraintEdge(0);--} // namespace defaults--} // namespace detail--template <typename T, typename TNearPointLocator>-Triangulation<T, TNearPointLocator>::Triangulation()- : m_nTargetVerts(detail::defaults::nTargetVerts)- , m_superGeomType(detail::defaults::superGeomType)- , m_vertexInsertionOrder(detail::defaults::vertexInsertionOrder)- , m_intersectingEdgesStrategy(detail::defaults::intersectingEdgesStrategy)- , m_minDistToConstraintEdge(detail::defaults::minDistToConstraintEdge)-{}--template <typename T, typename TNearPointLocator>-Triangulation<T, TNearPointLocator>::Triangulation(- const VertexInsertionOrder::Enum vertexInsertionOrder)- : m_nTargetVerts(detail::defaults::nTargetVerts)- , m_superGeomType(detail::defaults::superGeomType)- , m_vertexInsertionOrder(vertexInsertionOrder)- , m_intersectingEdgesStrategy(detail::defaults::intersectingEdgesStrategy)- , m_minDistToConstraintEdge(detail::defaults::minDistToConstraintEdge)-{}--template <typename T, typename TNearPointLocator>-Triangulation<T, TNearPointLocator>::Triangulation(- const VertexInsertionOrder::Enum vertexInsertionOrder,- const IntersectingConstraintEdges::Enum intersectingEdgesStrategy,- const T minDistToConstraintEdge)- : m_nTargetVerts(detail::defaults::nTargetVerts)- , m_superGeomType(detail::defaults::superGeomType)- , m_vertexInsertionOrder(vertexInsertionOrder)- , m_intersectingEdgesStrategy(intersectingEdgesStrategy)- , m_minDistToConstraintEdge(minDistToConstraintEdge)-{}--template <typename T, typename TNearPointLocator>-Triangulation<T, TNearPointLocator>::Triangulation(- const VertexInsertionOrder::Enum vertexInsertionOrder,- const TNearPointLocator& nearPtLocator,- const IntersectingConstraintEdges::Enum intersectingEdgesStrategy,- const T minDistToConstraintEdge)- : m_nTargetVerts(detail::defaults::nTargetVerts)- , m_nearPtLocator(nearPtLocator)- , m_superGeomType(detail::defaults::superGeomType)- , m_vertexInsertionOrder(vertexInsertionOrder)- , m_intersectingEdgesStrategy(intersectingEdgesStrategy)- , m_minDistToConstraintEdge(minDistToConstraintEdge)-{}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::changeNeighbor(- const TriInd iT,- const VertInd iVedge1,- const VertInd iVedge2,- const TriInd newNeighbor)-{- Triangle& t = triangles[iT];- t.neighbors[opposedTriangleInd(t, iVedge1, iVedge2)] = newNeighbor;-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::eraseDummies()-{- if(m_dummyTris.empty())- return;- const TriIndUSet dummySet(m_dummyTris.begin(), m_dummyTris.end());- TriIndUMap triIndMap;- triIndMap[noNeighbor] = noNeighbor;- for(TriInd iT(0), iTnew(0); iT < TriInd(triangles.size()); ++iT)- {- if(dummySet.count(iT))- continue;- triIndMap[iT] = iTnew;- triangles[iTnew] = triangles[iT];- iTnew++;- }- triangles.erase(triangles.end() - dummySet.size(), triangles.end());-- // remap adjacent triangle indices for vertices- typedef typename VerticesTriangles::iterator VertTrisIt;- for(VertTrisIt vTris = vertTris.begin(); vTris != vertTris.end(); ++vTris)- {- for(TriIndVec::iterator iT = vTris->begin(); iT != vTris->end(); ++iT)- *iT = triIndMap[*iT];- }- // remap neighbor indices for triangles- for(TriangleVec::iterator t = triangles.begin(); t != triangles.end(); ++t)- {- NeighborsArr3& nn = t->neighbors;- for(NeighborsArr3::iterator iN = nn.begin(); iN != nn.end(); ++iN)- *iN = triIndMap[*iN];- }- // clear dummy triangles- m_dummyTris = std::vector<TriInd>();-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::eraseSuperTriangle()-{- if(m_superGeomType != SuperGeometryType::SuperTriangle)- return;- // find triangles adjacent to super-triangle's vertices- TriIndUSet toErase;- toErase.reserve(- vertTris[0].size() + vertTris[1].size() + vertTris[2].size());- for(TriInd iT(0); iT < TriInd(triangles.size()); ++iT)- {- Triangle& t = triangles[iT];- if(t.vertices[0] < 3 || t.vertices[1] < 3 || t.vertices[2] < 3)- toErase.insert(iT);- }- finalizeTriangulation(toErase);-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::eraseOuterTriangles()-{- // make dummy triangles adjacent to super-triangle's vertices- const std::stack<TriInd> seed(std::deque<TriInd>(1, vertTris[0].front()));- const TriIndUSet toErase = growToBoundary(seed);- finalizeTriangulation(toErase);-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::eraseOuterTrianglesAndHoles()-{- const std::vector<LayerDepth> triDepths = calculateTriangleDepths();- TriIndUSet toErase;- toErase.reserve(triangles.size());- for(std::size_t iT = 0; iT != triangles.size(); ++iT)- {- if(triDepths[iT] % 2 == 0)- toErase.insert(static_cast<TriInd>(iT));- }- finalizeTriangulation(toErase);-}--/// Remap removing super-triangle: subtract 3 from vertices-inline Edge RemapNoSuperTriangle(const Edge& e)-{- return Edge(e.v1() - 3, e.v2() - 3);-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::removeTriangles(- const TriIndUSet& removedTriangles)-{- if(removedTriangles.empty())- return;- // remove triangles and calculate triangle index mapping- TriIndUMap triIndMap;- for(TriInd iT(0), iTnew(0); iT < TriInd(triangles.size()); ++iT)- {- if(removedTriangles.count(iT))- continue;- triIndMap[iT] = iTnew;- triangles[iTnew] = triangles[iT];- iTnew++;- }- triangles.erase(triangles.end() - removedTriangles.size(), triangles.end());- // adjust triangles' neighbors- vertTris = VerticesTriangles();- for(TriInd iT = 0; iT < triangles.size(); ++iT)- {- Triangle& t = triangles[iT];- // update neighbors to account for removed triangles- NeighborsArr3& nn = t.neighbors;- for(NeighborsArr3::iterator n = nn.begin(); n != nn.end(); ++n)- {- if(removedTriangles.count(*n))- {- *n = noNeighbor;- }- else if(*n != noNeighbor)- {- *n = triIndMap[*n];- }- }- }-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::finalizeTriangulation(- const TriIndUSet& removedTriangles)-{- eraseDummies();- // remove super-triangle- if(m_superGeomType == SuperGeometryType::SuperTriangle)- {- vertices.erase(vertices.begin(), vertices.begin() + 3);- if(removedTriangles.empty())- vertTris.erase(vertTris.begin(), vertTris.begin() + 3);- // Edge re-mapping- { // fixed edges- EdgeUSet updatedFixedEdges;- typedef CDT::EdgeUSet::const_iterator It;- for(It e = fixedEdges.begin(); e != fixedEdges.end(); ++e)- {- updatedFixedEdges.insert(RemapNoSuperTriangle(*e));- }- fixedEdges = updatedFixedEdges;- }- { // overlap count- unordered_map<Edge, BoundaryOverlapCount> updatedOverlapCount;- typedef unordered_map<Edge, BoundaryOverlapCount>::const_iterator- It;- for(It it = overlapCount.begin(); it != overlapCount.end(); ++it)- {- updatedOverlapCount.insert(std::make_pair(- RemapNoSuperTriangle(it->first), it->second));- }- overlapCount = updatedOverlapCount;- }- { // split edges mapping- unordered_map<Edge, EdgeVec> updatedPieceToOriginals;- typedef unordered_map<Edge, EdgeVec>::const_iterator It;- for(It it = pieceToOriginals.begin(); it != pieceToOriginals.end();- ++it)- {- EdgeVec ee = it->second;- for(EdgeVec::iterator eeIt = ee.begin(); eeIt != ee.end();- ++eeIt)- {- *eeIt = RemapNoSuperTriangle(*eeIt);- }- updatedPieceToOriginals.insert(- std::make_pair(RemapNoSuperTriangle(it->first), ee));- }- pieceToOriginals = updatedPieceToOriginals;- }- }- // remove other triangles- removeTriangles(removedTriangles);- // adjust triangle vertices: account for removed super-triangle- if(m_superGeomType == SuperGeometryType::SuperTriangle)- {- for(TriangleVec::iterator t = triangles.begin(); t != triangles.end();- ++t)- {- VerticesArr3& vv = t->vertices;- for(VerticesArr3::iterator v = vv.begin(); v != vv.end(); ++v)- {- *v -= 3;- }- }- }-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::initializedWithCustomSuperGeometry()-{- m_nearPtLocator.initialize(vertices);- m_nTargetVerts = vertices.size();- m_superGeomType = SuperGeometryType::Custom;-}--template <typename T, typename TNearPointLocator>-TriIndUSet Triangulation<T, TNearPointLocator>::growToBoundary(- std::stack<TriInd> seeds) const-{- TriIndUSet traversed;- while(!seeds.empty())- {- const TriInd iT = seeds.top();- seeds.pop();- traversed.insert(iT);- const Triangle& t = triangles[iT];- for(Index i(0); i < Index(3); ++i)- {- const Edge opEdge(t.vertices[ccw(i)], t.vertices[cw(i)]);- if(fixedEdges.count(opEdge))- continue;- const TriInd iN = t.neighbors[opoNbr(i)];- if(iN != noNeighbor && traversed.count(iN) == 0)- seeds.push(iN);- }- }- return traversed;-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::makeDummy(const TriInd iT)-{- const Triangle& t = triangles[iT];-- typedef VerticesArr3::const_iterator VCit;- for(VCit iV = t.vertices.begin(); iV != t.vertices.end(); ++iV)- removeAdjacentTriangle(*iV, iT);-- typedef NeighborsArr3::const_iterator NCit;- for(NCit iTn = t.neighbors.begin(); iTn != t.neighbors.end(); ++iTn)- changeNeighbor(*iTn, iT, noNeighbor);-- m_dummyTris.push_back(iT);-}--template <typename T, typename TNearPointLocator>-TriInd Triangulation<T, TNearPointLocator>::addTriangle(const Triangle& t)-{- if(m_dummyTris.empty())- {- triangles.push_back(t);- return TriInd(triangles.size() - 1);- }- const TriInd nxtDummy = m_dummyTris.back();- m_dummyTris.pop_back();- triangles[nxtDummy] = t;- return nxtDummy;-}--template <typename T, typename TNearPointLocator>-TriInd Triangulation<T, TNearPointLocator>::addTriangle()-{- if(m_dummyTris.empty())- {- const Triangle dummy = {- {noVertex, noVertex, noVertex},- {noNeighbor, noNeighbor, noNeighbor}};- triangles.push_back(dummy);- return TriInd(triangles.size() - 1);- }- const TriInd nxtDummy = m_dummyTris.back();- m_dummyTris.pop_back();- return nxtDummy;-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::insertEdges(- const std::vector<Edge>& edges)-{- insertEdges(edges.begin(), edges.end(), edge_get_v1, edge_get_v2);-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::conformToEdges(- const std::vector<Edge>& edges)-{- conformToEdges(edges.begin(), edges.end(), edge_get_v1, edge_get_v2);-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::fixEdge(const Edge& edge)-{- if(!fixedEdges.insert(edge).second)- {- ++overlapCount[edge]; // if edge is already fixed increment the counter- }-}--namespace detail-{--// add element to 'to' if not already in 'to'-template <typename T, typename Allocator1>-void insert_unique(std::vector<T, Allocator1>& to, const T& elem)-{- if(std::find(to.begin(), to.end(), elem) == to.end())- {- to.push_back(elem);- }-}--// add elements of 'from' that are not present in 'to' to 'to'-template <typename T, typename Allocator1, typename Allocator2>-void insert_unique(- std::vector<T, Allocator1>& to,- const std::vector<T, Allocator2>& from)-{- typedef typename std::vector<T, Allocator2>::const_iterator Cit;- to.reserve(to.size() + from.size());- for(Cit cit = from.begin(); cit != from.end(); ++cit)- {- insert_unique(to, *cit);- }-}--} // namespace detail--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::fixEdge(- const Edge& edge,- const Edge& originalEdge)-{- fixEdge(edge);- if(edge != originalEdge)- detail::insert_unique(pieceToOriginals[edge], originalEdge);-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::fixEdge(- const Edge& edge,- const BoundaryOverlapCount overlaps)-{- fixedEdges.insert(edge);- overlapCount[edge] = overlaps; // override overlap counter-}--namespace detail-{--template <typename T>-T lerp(const T& a, const T& b, const T t)-{- return (T(1) - t) * a + t * b;-}--// Precondition: ab and cd intersect normally-template <typename T>-V2d<T> intersectionPosition(- const V2d<T>& a,- const V2d<T>& b,- const V2d<T>& c,- const V2d<T>& d)-{- using namespace predicates::adaptive;- // interpolate point on the shorter segment- if(distanceSquared(a, b) < distanceSquared(c, d))- {- const T a_cd = orient2d(c.x, c.y, d.x, d.y, a.x, a.y);- const T b_cd = orient2d(c.x, c.y, d.x, d.y, b.x, b.y);- const T t = a_cd / (a_cd - b_cd);- return V2d<T>::make(lerp(a.x, b.x, t), lerp(a.y, b.y, t));- }- else- {- const T c_ab = orient2d(a.x, a.y, b.x, b.y, c.x, c.y);- const T d_ab = orient2d(a.x, a.y, b.x, b.y, d.x, d.y);- const T t = c_ab / (c_ab - d_ab);- return V2d<T>::make(lerp(c.x, d.x, t), lerp(c.y, d.y, t));- }-}--} // namespace detail--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::insertEdge(- const Edge edge,- const Edge originalEdge)-{- const VertInd iA = edge.v1();- VertInd iB = edge.v2();- if(iA == iB) // edge connects a vertex to itself- return;- const TriIndVec& aTris = vertTris[iA];- const TriIndVec& bTris = vertTris[iB];- const V2d<T>& a = vertices[iA];- const V2d<T>& b = vertices[iB];- if(verticesShareEdge(aTris, bTris))- {- fixEdge(edge, originalEdge);- return;- }-- const T distanceTolerance =- m_minDistToConstraintEdge == T(0)- ? T(0)- : m_minDistToConstraintEdge * distance(a, b);-- TriInd iT;- VertInd iVleft, iVright;- tie(iT, iVleft, iVright) =- intersectedTriangle(iA, aTris, a, b, distanceTolerance);- // if one of the triangle vertices is on the edge, move edge start- if(iT == noNeighbor)- {- const Edge edgePart(iA, iVleft);- fixEdge(edgePart, originalEdge);- return insertEdge(Edge(iVleft, iB), originalEdge);- }- std::vector<TriInd> intersected(1, iT);- std::vector<VertInd> ptsLeft(1, iVleft);- std::vector<VertInd> ptsRight(1, iVright);- VertInd iV = iA;- Triangle t = triangles[iT];- while(std::find(t.vertices.begin(), t.vertices.end(), iB) ==- t.vertices.end())- {- const TriInd iTopo = opposedTriangle(t, iV);- const Triangle& tOpo = triangles[iTopo];- const VertInd iVopo = opposedVertex(tOpo, iT);- const V2d<T> vOpo = vertices[iVopo];-- // Resolve intersection between two constraint edges if needed- if(m_intersectingEdgesStrategy ==- IntersectingConstraintEdges::Resolve &&- fixedEdges.count(Edge(iVleft, iVright)))- {- const VertInd iNewVert = static_cast<VertInd>(vertices.size());-- // split constraint edge that already exists in triangulation- const Edge splitEdge(iVleft, iVright);- const Edge half1(iVleft, iNewVert);- const Edge half2(iNewVert, iVright);- const BoundaryOverlapCount overlaps = overlapCount[splitEdge];- // remove the edge that will be split- fixedEdges.erase(splitEdge);- overlapCount.erase(splitEdge);- // add split edge's halves- fixEdge(half1, overlaps);- fixEdge(half2, overlaps);- // maintain piece-to-original mapping- EdgeVec newOriginals(1, splitEdge);- const unordered_map<Edge, EdgeVec>::const_iterator originalsIt =- pieceToOriginals.find(splitEdge);- if(originalsIt != pieceToOriginals.end())- { // edge being split was split before: pass-through originals- newOriginals = originalsIt->second;- pieceToOriginals.erase(originalsIt);- }- detail::insert_unique(pieceToOriginals[half1], newOriginals);- detail::insert_unique(pieceToOriginals[half2], newOriginals);-- // add a new point at the intersection of two constraint edges- const V2d<T> newV = detail::intersectionPosition(- vertices[iA],- vertices[iB],- vertices[iVleft],- vertices[iVright]);- addNewVertex(newV, TriIndVec());- std::stack<TriInd> triStack =- insertPointOnEdge(iNewVert, iT, iTopo);- ensureDelaunayByEdgeFlips(newV, iNewVert, triStack);- // TODO: is it's possible to re-use pseudo-polygons- // for inserting [iA, iNewVert] edge half?- insertEdge(Edge(iA, iNewVert), originalEdge);- insertEdge(Edge(iNewVert, iB), originalEdge);- return;- }-- intersected.push_back(iTopo);- iT = iTopo;- t = triangles[iT];-- const PtLineLocation::Enum loc =- locatePointLine(vOpo, a, b, distanceTolerance);- if(loc == PtLineLocation::Left)- {- ptsLeft.push_back(iVopo);- iV = iVleft;- iVleft = iVopo;- }- else if(loc == PtLineLocation::Right)- {- ptsRight.push_back(iVopo);- iV = iVright;- iVright = iVopo;- }- else // encountered point on the edge- iB = iVopo;- }- // Remove intersected triangles- typedef std::vector<TriInd>::const_iterator TriIndCit;- for(TriIndCit it = intersected.begin(); it != intersected.end(); ++it)- makeDummy(*it);- // Triangulate pseudo-polygons on both sides- const TriInd iTleft =- triangulatePseudopolygon(iA, iB, ptsLeft.begin(), ptsLeft.end());- std::reverse(ptsRight.begin(), ptsRight.end());- const TriInd iTright =- triangulatePseudopolygon(iB, iA, ptsRight.begin(), ptsRight.end());- changeNeighbor(iTleft, noNeighbor, iTright);- changeNeighbor(iTright, noNeighbor, iTleft);-- if(iB != edge.v2()) // encountered point on the edge- {- // fix edge part- const Edge edgePart(iA, iB);- fixEdge(edgePart, originalEdge);- return insertEdge(Edge(iB, edge.v2()), originalEdge);- }- else- {- fixEdge(edge, originalEdge);- }-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::conformToEdge(- const Edge edge,- EdgeVec originalEdges,- const BoundaryOverlapCount overlaps)-{- const VertInd iA = edge.v1();- VertInd iB = edge.v2();- if(iA == iB) // edge connects a vertex to itself- return;- const TriIndVec& aTris = vertTris[iA];- const TriIndVec& bTris = vertTris[iB];- const V2d<T>& a = vertices[iA];- const V2d<T>& b = vertices[iB];- if(verticesShareEdge(aTris, bTris))- {- overlaps > 0 ? fixEdge(edge, overlaps) : fixEdge(edge);- // avoid marking edge as a part of itself- if(!originalEdges.empty() && edge != originalEdges.front())- {- detail::insert_unique(pieceToOriginals[edge], originalEdges);- }- return;- }-- const T distanceTolerance =- m_minDistToConstraintEdge == T(0)- ? T(0)- : m_minDistToConstraintEdge * distance(a, b);- TriInd iT;- VertInd iVleft, iVright;- tie(iT, iVleft, iVright) =- intersectedTriangle(iA, aTris, a, b, distanceTolerance);- // if one of the triangle vertices is on the edge, move edge start- if(iT == noNeighbor)- {- const Edge edgePart(iA, iVleft);- overlaps > 0 ? fixEdge(edgePart, overlaps) : fixEdge(edgePart);- detail::insert_unique(pieceToOriginals[edgePart], originalEdges);- return conformToEdge(Edge(iVleft, iB), originalEdges, overlaps);- }-- VertInd iV = iA;- Triangle t = triangles[iT];- while(std::find(t.vertices.begin(), t.vertices.end(), iB) ==- t.vertices.end())- {- const TriInd iTopo = opposedTriangle(t, iV);- const Triangle& tOpo = triangles[iTopo];- const VertInd iVopo = opposedVertex(tOpo, iT);- const V2d<T> vOpo = vertices[iVopo];-- // Resolve intersection between two constraint edges if needed- if(m_intersectingEdgesStrategy ==- IntersectingConstraintEdges::Resolve &&- fixedEdges.count(Edge(iVleft, iVright)))- {- const VertInd iNewVert = static_cast<VertInd>(vertices.size());-- // split constraint edge that already exists in triangulation- const Edge splitEdge(iVleft, iVright);- const Edge half1(iVleft, iNewVert);- const Edge half2(iNewVert, iVright);- const BoundaryOverlapCount overlaps = overlapCount[splitEdge];- // remove the edge that will be split- fixedEdges.erase(splitEdge);- overlapCount.erase(splitEdge);- // add split edge's halves- fixEdge(half1, overlaps);- fixEdge(half2, overlaps);- // maintain piece-to-original mapping- EdgeVec newOriginals(1, splitEdge);- const unordered_map<Edge, EdgeVec>::const_iterator originalsIt =- pieceToOriginals.find(splitEdge);- if(originalsIt != pieceToOriginals.end())- { // edge being split was split before: pass-through originals- newOriginals = originalsIt->second;- pieceToOriginals.erase(originalsIt);- }- detail::insert_unique(pieceToOriginals[half1], newOriginals);- detail::insert_unique(pieceToOriginals[half2], newOriginals);-- // add a new point at the intersection of two constraint edges- const V2d<T> newV = detail::intersectionPosition(- vertices[iA],- vertices[iB],- vertices[iVleft],- vertices[iVright]);- addNewVertex(newV, TriIndVec());- std::stack<TriInd> triStack =- insertPointOnEdge(iNewVert, iT, iTopo);- ensureDelaunayByEdgeFlips(newV, iNewVert, triStack);- conformToEdge(Edge(iA, iNewVert), originalEdges, overlaps);- conformToEdge(Edge(iNewVert, iB), originalEdges, overlaps);- return;- }-- iT = iTopo;- t = triangles[iT];-- const PtLineLocation::Enum loc =- locatePointLine(vOpo, a, b, distanceTolerance);- if(loc == PtLineLocation::Left)- {- iV = iVleft;- iVleft = iVopo;- }- else if(loc == PtLineLocation::Right)- {- iV = iVright;- iVright = iVopo;- }- else // encountered point on the edge- iB = iVopo;- }- /**/-- // add mid-point to triangulation- const VertInd iMid = static_cast<VertInd>(vertices.size());- const V2d<T>& start = vertices[iA];- const V2d<T>& end = vertices[iB];- addNewVertex(- V2d<T>::make((start.x + end.x) / T(2), (start.y + end.y) / T(2)),- TriIndVec());- const std::vector<Edge> flippedFixedEdges =- insertVertex_FlipFixedEdges(iMid);-- conformToEdge(Edge(iA, iMid), originalEdges, overlaps);- conformToEdge(Edge(iMid, iB), originalEdges, overlaps);- // re-introduce fixed edges that were flipped- // and make sure overlap count is preserved- for(std::vector<Edge>::const_iterator it = flippedFixedEdges.begin();- it != flippedFixedEdges.end();- ++it)- {- fixedEdges.erase(*it);-- BoundaryOverlapCount prevOverlaps = 0;- const unordered_map<Edge, BoundaryOverlapCount>::const_iterator- overlapsIt = overlapCount.find(*it);- if(overlapsIt != overlapCount.end())- {- prevOverlaps = overlapsIt->second;- overlapCount.erase(overlapsIt);- }- // override overlapping boundaries count when re-inserting an edge- EdgeVec prevOriginals(1, *it);- const unordered_map<Edge, EdgeVec>::const_iterator originalsIt =- pieceToOriginals.find(*it);- if(originalsIt != pieceToOriginals.end())- {- prevOriginals = originalsIt->second;- }- conformToEdge(*it, prevOriginals, prevOverlaps);- }- if(iB != edge.v2())- conformToEdge(Edge(iB, edge.v2()), originalEdges, overlaps);-}--/*!- * Returns:- * - intersected triangle index- * - index of point on the left of the line- * - index of point on the right of the line- * If left point is right on the line: no triangle is intersected:- * - triangle index is no-neighbor (invalid)- * - index of point on the line- * - index of point on the right of the line- */-template <typename T, typename TNearPointLocator>-tuple<TriInd, VertInd, VertInd>-Triangulation<T, TNearPointLocator>::intersectedTriangle(- const VertInd iA,- const std::vector<TriInd>& candidates,- const V2d<T>& a,- const V2d<T>& b,- const T orientationTolerance) const-{- typedef std::vector<TriInd>::const_iterator TriIndCit;- for(TriIndCit it = candidates.begin(); it != candidates.end(); ++it)- {- const TriInd iT = *it;- const Triangle t = triangles[iT];- const Index i = vertexInd(t, iA);- const VertInd iP2 = t.vertices[ccw(i)];- const T orientP2 = orient2D(vertices[iP2], a, b);- const PtLineLocation::Enum locP2 = classifyOrientation(orientP2);- if(locP2 == PtLineLocation::Right)- {- const VertInd iP1 = t.vertices[cw(i)];- const T orientP1 = orient2D(vertices[iP1], a, b);- const PtLineLocation::Enum locP1 = classifyOrientation(orientP1);- if(locP1 == PtLineLocation::OnLine)- {- return make_tuple(noNeighbor, iP1, iP1);- }- if(locP1 == PtLineLocation::Left)- {- if(orientationTolerance)- {- T closestOrient;- VertInd iClosestP;- if(std::abs(orientP1) <= std::abs(orientP2))- {- closestOrient = orientP1;- iClosestP = iP1;- }- else- {- closestOrient = orientP2;- iClosestP = iP2;- }- if(classifyOrientation(- closestOrient, orientationTolerance) ==- PtLineLocation::OnLine)- {- return make_tuple(noNeighbor, iClosestP, iClosestP);- }- }- return make_tuple(iT, iP1, iP2);- }- }- }- throw std::runtime_error("Could not find vertex triangle intersected by "- "edge. Note: can be caused by duplicate points.");-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::addSuperTriangle(const Box2d<T>& box)-{- m_nTargetVerts = 3;- m_superGeomType = SuperGeometryType::SuperTriangle;-- const V2d<T> center = {- (box.min.x + box.max.x) / T(2), (box.min.y + box.max.y) / T(2)};- const T w = box.max.x - box.min.x;- const T h = box.max.y - box.min.y;- T r = std::sqrt(w * w + h * h) / T(2); // incircle radius- r *= T(1.1);- const T R = T(2) * r; // excircle radius- const T shiftX = R * std::sqrt(T(3)) / T(2); // R * cos(30 deg)- const V2d<T> posV1 = {center.x - shiftX, center.y - r};- const V2d<T> posV2 = {center.x + shiftX, center.y - r};- const V2d<T> posV3 = {center.x, center.y + R};- addNewVertex(posV1, TriIndVec(1, TriInd(0)));- addNewVertex(posV2, TriIndVec(1, TriInd(0)));- addNewVertex(posV3, TriIndVec(1, TriInd(0)));- const Triangle superTri = {- {VertInd(0), VertInd(1), VertInd(2)},- {noNeighbor, noNeighbor, noNeighbor}};- addTriangle(superTri);- m_nearPtLocator.initialize(vertices);-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::addNewVertex(- const V2d<T>& pos,- const TriIndVec& tris)-{- vertices.push_back(pos);- vertTris.push_back(tris);-}--template <typename T, typename TNearPointLocator>-std::vector<Edge>-Triangulation<T, TNearPointLocator>::insertVertex_FlipFixedEdges(- const VertInd iVert)-{- std::vector<Edge> flippedFixedEdges;-- const V2d<T>& v = vertices[iVert];- array<TriInd, 2> trisAt = walkingSearchTrianglesAt(v);- std::stack<TriInd> triStack =- trisAt[1] == noNeighbor- ? insertPointInTriangle(iVert, trisAt[0])- : insertPointOnEdge(iVert, trisAt[0], trisAt[1]);- while(!triStack.empty())- {- const TriInd iT = triStack.top();- triStack.pop();-- const Triangle& t = triangles[iT];- const TriInd iTopo = opposedTriangle(t, iVert);- if(iTopo == noNeighbor)- continue;-- /*- * v3 original edge: (v1, v3)- * /|\ flip-candidate edge: (v, v2)- * / | \- * / | \- * / | \- * new vertex--> v | v2- * \ | /- * \ | /- * \ | /- * \|/- * v1- */- const Triangle& tOpo = triangles[iTopo];- const Index i = opposedVertexInd(tOpo, iT);- const VertInd iV2 = tOpo.vertices[i];- const VertInd iV1 = tOpo.vertices[cw(i)];- const VertInd iV3 = tOpo.vertices[ccw(i)];-- if(isFlipNeeded(v, iVert, iV1, iV2, iV3))- {- // if flipped edge is fixed, remember it- const Edge flippedEdge(iV1, iV3);- if(fixedEdges.count(flippedEdge))- flippedFixedEdges.push_back(flippedEdge);-- flipEdge(iT, iTopo);- triStack.push(iT);- triStack.push(iTopo);- }- }-- m_nearPtLocator.addPoint(iVert, vertices);- return flippedFixedEdges;-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::insertVertex(const VertInd iVert)-{- const V2d<T>& v = vertices[iVert];- array<TriInd, 2> trisAt = walkingSearchTrianglesAt(v);- std::stack<TriInd> triStack =- trisAt[1] == noNeighbor- ? insertPointInTriangle(iVert, trisAt[0])- : insertPointOnEdge(iVert, trisAt[0], trisAt[1]);- ensureDelaunayByEdgeFlips(v, iVert, triStack);- m_nearPtLocator.addPoint(iVert, vertices);-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::ensureDelaunayByEdgeFlips(- const V2d<T>& v,- const VertInd iVert,- std::stack<TriInd>& triStack)-{- while(!triStack.empty())- {- const TriInd iT = triStack.top();- triStack.pop();-- const Triangle& t = triangles[iT];- const TriInd iTopo = opposedTriangle(t, iVert);- if(iTopo == noNeighbor)- continue;- if(isFlipNeeded(v, iT, iTopo, iVert))- {- flipEdge(iT, iTopo);- triStack.push(iT);- triStack.push(iTopo);- }- }-}--/*!- * Handles super-triangle vertices.- * Super-tri points are not infinitely far and influence the input points- * Three cases are possible:- * 1. If one of the opposed vertices is super-tri: no flip needed- * 2. One of the shared vertices is super-tri:- * check if on point is same side of line formed by non-super-tri- * vertices as the non-super-tri shared vertex- * 3. None of the vertices are super-tri: normal circumcircle test- */-/*- * v3 original edge: (v1, v3)- * /|\ flip-candidate edge: (v, v2)- * / | \- * / | \- * / | \- * new vertex--> v | v2- * \ | /- * \ | /- * \ | /- * \|/- * v1- */-template <typename T, typename TNearPointLocator>-bool Triangulation<T, TNearPointLocator>::isFlipNeeded(- const V2d<T>& v,- const VertInd iV,- const VertInd iV1,- const VertInd iV2,- const VertInd iV3) const-{- const V2d<T>& v1 = vertices[iV1];- const V2d<T>& v2 = vertices[iV2];- const V2d<T>& v3 = vertices[iV3];- if(m_superGeomType == SuperGeometryType::SuperTriangle)- {- // If flip-candidate edge touches super-triangle in-circumference- // test has to be replaced with orient2d test against the line- // formed by two non-artificial vertices (that don't belong to- // super-triangle)- if(iV < 3) // flip-candidate edge touches super-triangle- {- // does original edge also touch super-triangle?- if(iV1 < 3)- return locatePointLine(v1, v2, v3) ==- locatePointLine(v, v2, v3);- if(iV3 < 3)- return locatePointLine(v3, v1, v2) ==- locatePointLine(v, v1, v2);- return false; // original edge does not touch super-triangle- }- if(iV2 < 3) // flip-candidate edge touches super-triangle- {- // does original edge also touch super-triangle?- if(iV1 < 3)- return locatePointLine(v1, v, v3) == locatePointLine(v2, v, v3);- if(iV3 < 3)- return locatePointLine(v3, v1, v) == locatePointLine(v2, v1, v);- return false; // original edge does not touch super-triangle- }- // flip-candidate edge does not touch super-triangle- if(iV1 < 3)- return locatePointLine(v1, v2, v3) == locatePointLine(v, v2, v3);- if(iV3 < 3)- return locatePointLine(v3, v1, v2) == locatePointLine(v, v1, v2);- }- return isInCircumcircle(v, v1, v2, v3);-}--template <typename T, typename TNearPointLocator>-bool Triangulation<T, TNearPointLocator>::isFlipNeeded(- const V2d<T>& v,- const TriInd iT,- const TriInd iTopo,- const VertInd iV) const-{- /*- * v3 original edge: (v1, v3)- * /|\ flip-candidate edge: (v, v2)- * / | \- * / | \- * / | \- * new vertex--> v | v2- * \ | /- * \ | /- * \ | /- * \|/- * v1- */- const Triangle& tOpo = triangles[iTopo];- const Index i = opposedVertexInd(tOpo, iT);- const VertInd iV2 = tOpo.vertices[i];- const VertInd iV1 = tOpo.vertices[cw(i)];- const VertInd iV3 = tOpo.vertices[ccw(i)];-- // flip not needed if the original edge is fixed- if(fixedEdges.count(Edge(iV1, iV3)))- return false;-- return isFlipNeeded(v, iV, iV1, iV2, iV3);-}--/* Insert point into triangle: split into 3 triangles:- * - create 2 new triangles- * - re-use old triangle for the 3rd- * v3- * / | \- * / | \ <-- original triangle (t)- * / | \- * n3 / | \ n2- * /newT2|newT1\- * / v \- * / __/ \__ \- * / __/ \__ \- * / _/ t' \_ \- * v1 ___________________ v2- * n1- */-template <typename T, typename TNearPointLocator>-std::stack<TriInd> Triangulation<T, TNearPointLocator>::insertPointInTriangle(- const VertInd v,- const TriInd iT)-{- const TriInd iNewT1 = addTriangle();- const TriInd iNewT2 = addTriangle();-- Triangle& t = triangles[iT];- const array<VertInd, 3> vv = t.vertices;- const array<TriInd, 3> nn = t.neighbors;- const VertInd v1 = vv[0], v2 = vv[1], v3 = vv[2];- const TriInd n1 = nn[0], n2 = nn[1], n3 = nn[2];- // make two new triangles and convert current triangle to 3rd new- // triangle- using detail::arr3;- triangles[iNewT1] = Triangle::make(arr3(v2, v3, v), arr3(n2, iNewT2, iT));- triangles[iNewT2] = Triangle::make(arr3(v3, v1, v), arr3(n3, iT, iNewT1));- t = Triangle::make(arr3(v1, v2, v), arr3(n1, iNewT1, iNewT2));- // make and add a new vertex- addAdjacentTriangles(v, iT, iNewT1, iNewT2);- // adjust lists of adjacent triangles for v1, v2, v3- addAdjacentTriangle(v1, iNewT2);- addAdjacentTriangle(v2, iNewT1);- removeAdjacentTriangle(v3, iT);- addAdjacentTriangle(v3, iNewT1);- addAdjacentTriangle(v3, iNewT2);- // change triangle neighbor's neighbors to new triangles- changeNeighbor(n2, iT, iNewT1);- changeNeighbor(n3, iT, iNewT2);- // return newly added triangles- std::stack<TriInd> newTriangles;- newTriangles.push(iT);- newTriangles.push(iNewT1);- newTriangles.push(iNewT2);- return newTriangles;-}--/* Inserting a point on the edge between two triangles- * T1 (top) v1- * /|\- * n1 / | \ n4- * / | \- * / T1' | Tnew1\- * v2-------v-------v4- * \ Tnew2| T2' /- * \ | /- * n2 \ | / n3- * \|/- * T2 (bottom) v3- */-template <typename T, typename TNearPointLocator>-std::stack<TriInd> Triangulation<T, TNearPointLocator>::insertPointOnEdge(- const VertInd v,- const TriInd iT1,- const TriInd iT2)-{- const TriInd iTnew1 = addTriangle();- const TriInd iTnew2 = addTriangle();-- Triangle& t1 = triangles[iT1];- Triangle& t2 = triangles[iT2];- Index i = opposedVertexInd(t1, iT2);- const VertInd v1 = t1.vertices[i];- const VertInd v2 = t1.vertices[ccw(i)];- const TriInd n1 = t1.neighbors[i];- const TriInd n4 = t1.neighbors[cw(i)];- i = opposedVertexInd(t2, iT1);- const VertInd v3 = t2.vertices[i];- const VertInd v4 = t2.vertices[ccw(i)];- const TriInd n3 = t2.neighbors[i];- const TriInd n2 = t2.neighbors[cw(i)];- // add new triangles and change existing ones- using detail::arr3;- t1 = Triangle::make(arr3(v1, v2, v), arr3(n1, iTnew2, iTnew1));- t2 = Triangle::make(arr3(v3, v4, v), arr3(n3, iTnew1, iTnew2));- triangles[iTnew1] = Triangle::make(arr3(v1, v, v4), arr3(iT1, iT2, n4));- triangles[iTnew2] = Triangle::make(arr3(v3, v, v2), arr3(iT2, iT1, n2));- // make and add new vertex- addAdjacentTriangles(v, iT1, iTnew2, iT2, iTnew1);- // adjust neighboring triangles and vertices- changeNeighbor(n4, iT1, iTnew1);- changeNeighbor(n2, iT2, iTnew2);- addAdjacentTriangle(v1, iTnew1);- addAdjacentTriangle(v3, iTnew2);- removeAdjacentTriangle(v2, iT2);- addAdjacentTriangle(v2, iTnew2);- removeAdjacentTriangle(v4, iT1);- addAdjacentTriangle(v4, iTnew1);- // return newly added triangles- std::stack<TriInd> newTriangles;- newTriangles.push(iT1);- newTriangles.push(iTnew2);- newTriangles.push(iT2);- newTriangles.push(iTnew1);- return newTriangles;-}--template <typename T, typename TNearPointLocator>-array<TriInd, 2>-Triangulation<T, TNearPointLocator>::trianglesAt(const V2d<T>& pos) const-{- array<TriInd, 2> out = {noNeighbor, noNeighbor};- for(TriInd i = TriInd(0); i < TriInd(triangles.size()); ++i)- {- const Triangle& t = triangles[i];- const V2d<T>& v1 = vertices[t.vertices[0]];- const V2d<T>& v2 = vertices[t.vertices[1]];- const V2d<T>& v3 = vertices[t.vertices[2]];- const PtTriLocation::Enum loc = locatePointTriangle(pos, v1, v2, v3);- if(loc == PtTriLocation::Outside)- continue;- out[0] = i;- if(isOnEdge(loc))- out[1] = t.neighbors[edgeNeighbor(loc)];- return out;- }- throw std::runtime_error("No triangle was found at position");-}--template <typename T, typename TNearPointLocator>-TriInd Triangulation<T, TNearPointLocator>::walkTriangles(- const VertInd startVertex,- const V2d<T>& pos) const-{- // begin walk in search of triangle at pos- TriInd currTri = vertTris[startVertex][0];-#ifdef CDT_USE_BOOST- TriIndFlatUSet visited;-#else- TriIndUSet visited;-#endif- bool found = false;- while(!found)- {- const Triangle& t = triangles[currTri];- found = true;- // stochastic offset to randomize which edge we check first- const Index offset(detail::randGenerator() % 3);- for(Index i_(0); i_ < Index(3); ++i_)- {- const Index i((i_ + offset) % 3);- const V2d<T>& vStart = vertices[t.vertices[i]];- const V2d<T>& vEnd = vertices[t.vertices[ccw(i)]];- const PtLineLocation::Enum edgeCheck =- locatePointLine(pos, vStart, vEnd);- if(edgeCheck == PtLineLocation::Right &&- t.neighbors[i] != noNeighbor &&- visited.insert(t.neighbors[i]).second)- {- found = false;- currTri = t.neighbors[i];- break;- }- }- }- return currTri;-}--template <typename T, typename TNearPointLocator>-array<TriInd, 2> Triangulation<T, TNearPointLocator>::walkingSearchTrianglesAt(- const V2d<T>& pos) const-{- array<TriInd, 2> out = {noNeighbor, noNeighbor};- // Query for a vertex close to pos, to start the search- const VertInd startVertex = m_nearPtLocator.nearPoint(pos, vertices);- const TriInd iT = walkTriangles(startVertex, pos);- // Finished walk, locate point in current triangle- const Triangle& t = triangles[iT];- const V2d<T>& v1 = vertices[t.vertices[0]];- const V2d<T>& v2 = vertices[t.vertices[1]];- const V2d<T>& v3 = vertices[t.vertices[2]];- const PtTriLocation::Enum loc = locatePointTriangle(pos, v1, v2, v3);- if(loc == PtTriLocation::Outside)- throw std::runtime_error("No triangle was found at position");- out[0] = iT;- if(isOnEdge(loc))- out[1] = t.neighbors[edgeNeighbor(loc)];- return out;-}--/* Flip edge between T and Topo:- *- * v4 | - old edge- * /|\ ~ - new edge- * / | \- * n3 / T' \ n4- * / | \- * / | \- * T -> v1~~~~~~~~~v3 <- Topo- * \ | /- * \ | /- * n1 \Topo'/ n2- * \ | /- * \|/- * v2- */-template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::flipEdge(- const TriInd iT,- const TriInd iTopo)-{- Triangle& t = triangles[iT];- Triangle& tOpo = triangles[iTopo];- const array<TriInd, 3>& triNs = t.neighbors;- const array<TriInd, 3>& triOpoNs = tOpo.neighbors;- const array<VertInd, 3>& triVs = t.vertices;- const array<VertInd, 3>& triOpoVs = tOpo.vertices;- // find vertices and neighbors- Index i = opposedVertexInd(t, iTopo);- const VertInd v1 = triVs[i];- const VertInd v2 = triVs[ccw(i)];- const TriInd n1 = triNs[i];- const TriInd n3 = triNs[cw(i)];- i = opposedVertexInd(tOpo, iT);- const VertInd v3 = triOpoVs[i];- const VertInd v4 = triOpoVs[ccw(i)];- const TriInd n4 = triOpoNs[i];- const TriInd n2 = triOpoNs[cw(i)];- // change vertices and neighbors- using detail::arr3;- t = Triangle::make(arr3(v4, v1, v3), arr3(n3, iTopo, n4));- tOpo = Triangle::make(arr3(v2, v3, v1), arr3(n2, iT, n1));- // adjust neighboring triangles and vertices- changeNeighbor(n1, iT, iTopo);- changeNeighbor(n4, iTopo, iT);- // only adjust adjacent triangles if triangulation is not finalized:- // can happen when called from outside on an already finalized triangulation- if(!isFinalized())- {- addAdjacentTriangle(v1, iTopo);- addAdjacentTriangle(v3, iT);- removeAdjacentTriangle(v2, iT);- removeAdjacentTriangle(v4, iTopo);- }-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::changeNeighbor(- const TriInd iT,- const TriInd oldNeighbor,- const TriInd newNeighbor)-{- if(iT == noNeighbor)- return;- Triangle& t = triangles[iT];- t.neighbors[neighborInd(t, oldNeighbor)] = newNeighbor;-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::addAdjacentTriangle(- const VertInd iVertex,- const TriInd iTriangle)-{- vertTris[iVertex].push_back(iTriangle);-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::addAdjacentTriangles(- const VertInd iVertex,- const TriInd iT1,- const TriInd iT2,- const TriInd iT3)-{- TriIndVec& vTris = vertTris[iVertex];- vTris.reserve(vTris.size() + 3);- vTris.push_back(iT1);- vTris.push_back(iT2);- vTris.push_back(iT3);-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::addAdjacentTriangles(- const VertInd iVertex,- const TriInd iT1,- const TriInd iT2,- const TriInd iT3,- const TriInd iT4)-{- TriIndVec& vTris = vertTris[iVertex];- vTris.reserve(vTris.size() + 4);- vTris.push_back(iT1);- vTris.push_back(iT2);- vTris.push_back(iT3);- vTris.push_back(iT4);-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::removeAdjacentTriangle(- const VertInd iVertex,- const TriInd iTriangle)-{- std::vector<TriInd>& tris = vertTris[iVertex];- tris.erase(std::find(tris.begin(), tris.end(), iTriangle));-}--template <typename T, typename TNearPointLocator>-TriInd Triangulation<T, TNearPointLocator>::triangulatePseudopolygon(- const VertInd ia,- const VertInd ib,- const std::vector<VertInd>::const_iterator pointsFirst,- const std::vector<VertInd>::const_iterator pointsLast)-{- if(pointsFirst == pointsLast)- return pseudopolyOuterTriangle(ia, ib);- // Find delaunay point- const VertInd ic = findDelaunayPoint(ia, ib, pointsFirst, pointsLast);- // Find pseudopolygons split by the delaunay point- std::vector<VertInd>::const_iterator newLast = pointsFirst;- while(*newLast != ic)- ++newLast;- const std::vector<VertInd>::const_iterator newFirst = newLast + 1;- // triangulate splitted pseudo-polygons- const TriInd iT2 = triangulatePseudopolygon(ic, ib, newFirst, pointsLast);- const TriInd iT1 = triangulatePseudopolygon(ia, ic, pointsFirst, newLast);- // add new triangle- const Triangle t = {{ia, ib, ic}, {noNeighbor, iT2, iT1}};- const TriInd iT = addTriangle(t);- // adjust neighboring triangles and vertices- if(iT1 != noNeighbor)- {- if(pointsFirst == newLast)- changeNeighbor(iT1, ia, ic, iT);- else- triangles[iT1].neighbors[0] = iT;- }- if(iT2 != noNeighbor)- {- if(newFirst == pointsLast)- changeNeighbor(iT2, ic, ib, iT);- else- triangles[iT2].neighbors[0] = iT;- }- addAdjacentTriangle(ia, iT);- addAdjacentTriangle(ib, iT);- addAdjacentTriangle(ic, iT);-- return iT;-}--template <typename T, typename TNearPointLocator>-VertInd Triangulation<T, TNearPointLocator>::findDelaunayPoint(- const VertInd ia,- const VertInd ib,- const std::vector<VertInd>::const_iterator pointsFirst,- const std::vector<VertInd>::const_iterator pointsLast) const-{- assert(pointsFirst != pointsLast);- const V2d<T>& a = vertices[ia];- const V2d<T>& b = vertices[ib];- VertInd ic = *pointsFirst;- V2d<T> c = vertices[ic];- typedef std::vector<VertInd>::const_iterator CIt;- for(CIt it = pointsFirst + 1; it != pointsLast; ++it)- {- const V2d<T> v = vertices[*it];- if(!isInCircumcircle(v, a, b, c))- continue;- ic = *it;- c = vertices[ic];- }- return ic;-}--template <typename T, typename TNearPointLocator>-TriInd Triangulation<T, TNearPointLocator>::pseudopolyOuterTriangle(- const VertInd ia,- const VertInd ib) const-{- const std::vector<TriInd>& aTris = vertTris[ia];- const std::vector<TriInd>& bTris = vertTris[ib];- typedef std::vector<TriInd>::const_iterator TriIndCit;- for(TriIndCit it = aTris.begin(); it != aTris.end(); ++it)- if(std::find(bTris.begin(), bTris.end(), *it) != bTris.end())- return *it;- return noNeighbor;-}--template <typename T, typename TNearPointLocator>-void Triangulation<T, TNearPointLocator>::insertVertices(- const std::vector<V2d<T> >& newVertices)-{- return insertVertices(- newVertices.begin(), newVertices.end(), getX_V2d<T>, getY_V2d<T>);-}--template <typename T, typename TNearPointLocator>-bool Triangulation<T, TNearPointLocator>::isFinalized() const-{- return vertTris.empty() && !vertices.empty();-}--template <typename T, typename TNearPointLocator>-unordered_map<TriInd, LayerDepth>-Triangulation<T, TNearPointLocator>::peelLayer(- std::stack<TriInd> seeds,- const LayerDepth layerDepth,- std::vector<LayerDepth>& triDepths) const-{- unordered_map<TriInd, LayerDepth> behindBoundary;- while(!seeds.empty())- {- const TriInd iT = seeds.top();- seeds.pop();- triDepths[iT] = layerDepth;- behindBoundary.erase(iT);- const Triangle& t = triangles[iT];- for(Index i(0); i < Index(3); ++i)- {- const Edge opEdge(t.vertices[ccw(i)], t.vertices[cw(i)]);- const TriInd iN = t.neighbors[opoNbr(i)];- if(iN == noNeighbor || triDepths[iN] <= layerDepth)- continue;- if(fixedEdges.count(opEdge))- {- const unordered_map<Edge, LayerDepth>::const_iterator cit =- overlapCount.find(opEdge);- const LayerDepth triDepth = cit == overlapCount.end()- ? layerDepth + 1- : layerDepth + cit->second + 1;- behindBoundary[iN] = triDepth;- continue;- }- seeds.push(iN);- }- }- return behindBoundary;-}--template <typename T, typename TNearPointLocator>-std::vector<LayerDepth>-Triangulation<T, TNearPointLocator>::calculateTriangleDepths() const-{- std::vector<LayerDepth> triDepths(- triangles.size(), std::numeric_limits<LayerDepth>::max());- std::stack<TriInd> seeds(TriDeque(1, vertTris[0].front()));- LayerDepth layerDepth = 0;- LayerDepth deepestSeedDepth = 0;-- unordered_map<LayerDepth, TriIndUSet> seedsByDepth;- do- {- const unordered_map<TriInd, LayerDepth>& newSeeds =- peelLayer(seeds, layerDepth, triDepths);-- seedsByDepth.erase(layerDepth);- typedef unordered_map<TriInd, LayerDepth>::const_iterator Iter;- for(Iter it = newSeeds.begin(); it != newSeeds.end(); ++it)- {- deepestSeedDepth = std::max(deepestSeedDepth, it->second);- seedsByDepth[it->second].insert(it->first);- }- const TriIndUSet& nextLayerSeeds = seedsByDepth[layerDepth + 1];- seeds = std::stack<TriInd>(- TriDeque(nextLayerSeeds.begin(), nextLayerSeeds.end()));- ++layerDepth;- } while(!seeds.empty() || deepestSeedDepth > layerDepth);-- return triDepths;-}--} // namespace CDT+/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +/** + * @file + * Triangulation class - implementation + */ + +#include "Triangulation.h" +#include "portable_nth_element.hpp" + +#include <algorithm> +#include <cassert> +#include <cmath> +#include <deque> +#include <stdexcept> + +namespace CDT +{ + +typedef std::deque<TriInd> TriDeque; + +namespace detail +{ + +/// Needed for c++03 compatibility (no uniform initialization available) +template <typename T> +array<T, 3> arr3(const T& v0, const T& v1, const T& v2) +{ + const array<T, 3> out = {v0, v1, v2}; + return out; +} + +namespace defaults +{ + +const std::size_t nTargetVerts = 0; +const SuperGeometryType::Enum superGeomType = SuperGeometryType::SuperTriangle; +const VertexInsertionOrder::Enum vertexInsertionOrder = + VertexInsertionOrder::Auto; +const IntersectingConstraintEdges::Enum intersectingEdgesStrategy = + IntersectingConstraintEdges::Ignore; +const float minDistToConstraintEdge(0); + +} // namespace defaults + +} // namespace detail + +template <typename T, typename TNearPointLocator> +Triangulation<T, TNearPointLocator>::Triangulation() + : m_nTargetVerts(detail::defaults::nTargetVerts) + , m_superGeomType(detail::defaults::superGeomType) + , m_vertexInsertionOrder(detail::defaults::vertexInsertionOrder) + , m_intersectingEdgesStrategy(detail::defaults::intersectingEdgesStrategy) + , m_minDistToConstraintEdge(detail::defaults::minDistToConstraintEdge) +{} + +template <typename T, typename TNearPointLocator> +Triangulation<T, TNearPointLocator>::Triangulation( + const VertexInsertionOrder::Enum vertexInsertionOrder) + : m_nTargetVerts(detail::defaults::nTargetVerts) + , m_superGeomType(detail::defaults::superGeomType) + , m_vertexInsertionOrder(vertexInsertionOrder) + , m_intersectingEdgesStrategy(detail::defaults::intersectingEdgesStrategy) + , m_minDistToConstraintEdge(detail::defaults::minDistToConstraintEdge) +{} + +template <typename T, typename TNearPointLocator> +Triangulation<T, TNearPointLocator>::Triangulation( + const VertexInsertionOrder::Enum vertexInsertionOrder, + const IntersectingConstraintEdges::Enum intersectingEdgesStrategy, + const T minDistToConstraintEdge) + : m_nTargetVerts(detail::defaults::nTargetVerts) + , m_superGeomType(detail::defaults::superGeomType) + , m_vertexInsertionOrder(vertexInsertionOrder) + , m_intersectingEdgesStrategy(intersectingEdgesStrategy) + , m_minDistToConstraintEdge(minDistToConstraintEdge) +{} + +template <typename T, typename TNearPointLocator> +Triangulation<T, TNearPointLocator>::Triangulation( + const VertexInsertionOrder::Enum vertexInsertionOrder, + const TNearPointLocator& nearPtLocator, + const IntersectingConstraintEdges::Enum intersectingEdgesStrategy, + const T minDistToConstraintEdge) + : m_nearPtLocator(nearPtLocator) + , m_nTargetVerts(detail::defaults::nTargetVerts) + , m_superGeomType(detail::defaults::superGeomType) + , m_vertexInsertionOrder(vertexInsertionOrder) + , m_intersectingEdgesStrategy(intersectingEdgesStrategy) + , m_minDistToConstraintEdge(minDistToConstraintEdge) +{} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::eraseDummies() +{ + if(m_dummyTris.empty()) + return; + const TriIndUSet dummySet(m_dummyTris.begin(), m_dummyTris.end()); + TriIndUMap triIndMap; + triIndMap[noNeighbor] = noNeighbor; + for(TriInd iT(0), iTnew(0); iT < TriInd(triangles.size()); ++iT) + { + if(dummySet.count(iT)) + continue; + triIndMap[iT] = iTnew; + triangles[iTnew] = triangles[iT]; + iTnew++; + } + triangles.erase(triangles.end() - dummySet.size(), triangles.end()); + + // remap adjacent triangle indices for vertices + for(TriIndVec::iterator iT = m_vertTris.begin(); iT != m_vertTris.end(); + ++iT) + { + *iT = triIndMap[*iT]; + } + // remap neighbor indices for triangles + for(TriangleVec::iterator t = triangles.begin(); t != triangles.end(); ++t) + { + NeighborsArr3& nn = t->neighbors; + for(NeighborsArr3::iterator iN = nn.begin(); iN != nn.end(); ++iN) + *iN = triIndMap[*iN]; + } + // clear dummy triangles + m_dummyTris = std::vector<TriInd>(); +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::eraseSuperTriangle() +{ + if(m_superGeomType != SuperGeometryType::SuperTriangle) + return; + // find triangles adjacent to super-triangle's vertices + TriIndUSet toErase; + for(TriInd iT(0); iT < TriInd(triangles.size()); ++iT) + { + if(touchesSuperTriangle(triangles[iT])) + toErase.insert(iT); + } + finalizeTriangulation(toErase); +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::eraseOuterTriangles() +{ + // make dummy triangles adjacent to super-triangle's vertices + assert(m_vertTris[0] != noNeighbor); + const std::stack<TriInd> seed(std::deque<TriInd>(1, m_vertTris[0])); + const TriIndUSet toErase = growToBoundary(seed); + finalizeTriangulation(toErase); +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::eraseOuterTrianglesAndHoles() +{ + const std::vector<LayerDepth> triDepths = calculateTriangleDepths(); + TriIndUSet toErase; + toErase.reserve(triangles.size()); + for(std::size_t iT = 0; iT != triangles.size(); ++iT) + { + if(triDepths[iT] % 2 == 0) + toErase.insert(static_cast<TriInd>(iT)); + } + finalizeTriangulation(toErase); +} + +/// Remap removing super-triangle: subtract 3 from vertices +inline Edge RemapNoSuperTriangle(const Edge& e) +{ + return Edge(VertInd(e.v1() - 3), VertInd(e.v2() - 3)); +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::removeTriangles( + const TriIndUSet& removedTriangles) +{ + if(removedTriangles.empty()) + return; + // remove triangles and calculate triangle index mapping + TriIndUMap triIndMap; + for(TriInd iT(0), iTnew(0); iT < TriInd(triangles.size()); ++iT) + { + if(removedTriangles.count(iT)) + continue; + triIndMap[iT] = iTnew; + triangles[iTnew] = triangles[iT]; + iTnew++; + } + triangles.erase(triangles.end() - removedTriangles.size(), triangles.end()); + // adjust triangles' neighbors + for(TriInd iT(0); iT < triangles.size(); ++iT) + { + Triangle& t = triangles[iT]; + // update neighbors to account for removed triangles + NeighborsArr3& nn = t.neighbors; + for(NeighborsArr3::iterator n = nn.begin(); n != nn.end(); ++n) + { + if(removedTriangles.count(*n)) + { + *n = noNeighbor; + } + else if(*n != noNeighbor) + { + *n = triIndMap[*n]; + } + } + } +} + +template <typename T, typename TNearPointLocator> +TriIndVec& Triangulation<T, TNearPointLocator>::VertTrisInternal() +{ + return m_vertTris; +} + +template <typename T, typename TNearPointLocator> +const TriIndVec& Triangulation<T, TNearPointLocator>::VertTrisInternal() const +{ + return m_vertTris; +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::finalizeTriangulation( + const TriIndUSet& removedTriangles) +{ + eraseDummies(); + m_vertTris = TriIndVec(); + // remove super-triangle + if(m_superGeomType == SuperGeometryType::SuperTriangle) + { + vertices.erase(vertices.begin(), vertices.begin() + 3); + // Edge re-mapping + { // fixed edges + EdgeUSet updatedFixedEdges; + typedef CDT::EdgeUSet::const_iterator It; + for(It e = fixedEdges.begin(); e != fixedEdges.end(); ++e) + { + updatedFixedEdges.insert(RemapNoSuperTriangle(*e)); + } + fixedEdges = updatedFixedEdges; + } + { // overlap count + unordered_map<Edge, BoundaryOverlapCount> updatedOverlapCount; + typedef unordered_map<Edge, BoundaryOverlapCount>::const_iterator + It; + for(It it = overlapCount.begin(); it != overlapCount.end(); ++it) + { + updatedOverlapCount.insert(std::make_pair( + RemapNoSuperTriangle(it->first), it->second)); + } + overlapCount = updatedOverlapCount; + } + { // split edges mapping + unordered_map<Edge, EdgeVec> updatedPieceToOriginals; + typedef unordered_map<Edge, EdgeVec>::const_iterator It; + for(It it = pieceToOriginals.begin(); it != pieceToOriginals.end(); + ++it) + { + EdgeVec ee = it->second; + for(EdgeVec::iterator eeIt = ee.begin(); eeIt != ee.end(); + ++eeIt) + { + *eeIt = RemapNoSuperTriangle(*eeIt); + } + updatedPieceToOriginals.insert( + std::make_pair(RemapNoSuperTriangle(it->first), ee)); + } + pieceToOriginals = updatedPieceToOriginals; + } + } + // remove other triangles + removeTriangles(removedTriangles); + // adjust triangle vertices: account for removed super-triangle + if(m_superGeomType == SuperGeometryType::SuperTriangle) + { + for(TriangleVec::iterator t = triangles.begin(); t != triangles.end(); + ++t) + { + VerticesArr3& vv = t->vertices; + for(VerticesArr3::iterator v = vv.begin(); v != vv.end(); ++v) + { + *v -= 3; + } + } + } +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::initializedWithCustomSuperGeometry() +{ + m_nearPtLocator.initialize(vertices); + m_nTargetVerts = vertices.size(); + m_superGeomType = SuperGeometryType::Custom; +} + +template <typename T, typename TNearPointLocator> +TriIndUSet Triangulation<T, TNearPointLocator>::growToBoundary( + std::stack<TriInd> seeds) const +{ + TriIndUSet traversed; + while(!seeds.empty()) + { + const TriInd iT = seeds.top(); + seeds.pop(); + traversed.insert(iT); + const Triangle& t = triangles[iT]; + for(Index i(0); i < Index(3); ++i) + { + const Edge opEdge(t.vertices[ccw(i)], t.vertices[cw(i)]); + if(fixedEdges.count(opEdge)) + continue; + const TriInd iN = t.neighbors[opoNbr(i)]; + if(iN != noNeighbor && traversed.count(iN) == 0) + seeds.push(iN); + } + } + return traversed; +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::makeDummy(const TriInd iT) +{ + m_dummyTris.push_back(iT); +} + +template <typename T, typename TNearPointLocator> +TriInd Triangulation<T, TNearPointLocator>::addTriangle(const Triangle& t) +{ + if(m_dummyTris.empty()) + { + triangles.push_back(t); + return TriInd(triangles.size() - 1); + } + const TriInd nxtDummy = m_dummyTris.back(); + m_dummyTris.pop_back(); + triangles[nxtDummy] = t; + return nxtDummy; +} + +template <typename T, typename TNearPointLocator> +TriInd Triangulation<T, TNearPointLocator>::addTriangle() +{ + if(m_dummyTris.empty()) + { + const Triangle dummy = { + {noVertex, noVertex, noVertex}, + {noNeighbor, noNeighbor, noNeighbor}}; + triangles.push_back(dummy); + return TriInd(triangles.size() - 1); + } + const TriInd nxtDummy = m_dummyTris.back(); + m_dummyTris.pop_back(); + return nxtDummy; +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::insertEdges( + const std::vector<Edge>& edges) +{ + insertEdges(edges.begin(), edges.end(), edge_get_v1, edge_get_v2); +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::conformToEdges( + const std::vector<Edge>& edges) +{ + conformToEdges(edges.begin(), edges.end(), edge_get_v1, edge_get_v2); +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::fixEdge(const Edge& edge) +{ + if(!fixedEdges.insert(edge).second) + { + ++overlapCount[edge]; // if edge is already fixed increment the counter + } +} + +namespace detail +{ + +// add element to 'to' if not already in 'to' +template <typename T, typename Allocator1> +void insert_unique(std::vector<T, Allocator1>& to, const T& elem) +{ + if(std::find(to.begin(), to.end(), elem) == to.end()) + { + to.push_back(elem); + } +} + +// add elements of 'from' that are not present in 'to' to 'to' +template <typename T, typename Allocator1, typename Allocator2> +void insert_unique( + std::vector<T, Allocator1>& to, + const std::vector<T, Allocator2>& from) +{ + typedef typename std::vector<T, Allocator2>::const_iterator Cit; + to.reserve(to.size() + from.size()); + for(Cit cit = from.begin(); cit != from.end(); ++cit) + { + insert_unique(to, *cit); + } +} + +} // namespace detail + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::splitFixedEdge( + const Edge& edge, + const VertInd iSplitVert) +{ + // split constraint (fixed) edge that already exists in triangulation + const Edge half1(edge.v1(), iSplitVert); + const Edge half2(iSplitVert, edge.v2()); + // remove the edge that and add its halves + fixedEdges.erase(edge); + fixEdge(half1); + fixEdge(half2); + // maintain overlaps + typedef unordered_map<Edge, BoundaryOverlapCount>::const_iterator It; + const It overlapIt = overlapCount.find(edge); + if(overlapIt != overlapCount.end()) + { + overlapCount[half1] += overlapIt->second; + overlapCount[half2] += overlapIt->second; + overlapCount.erase(overlapIt); + } + // maintain piece-to-original mapping + EdgeVec newOriginals(1, edge); + const unordered_map<Edge, EdgeVec>::const_iterator originalsIt = + pieceToOriginals.find(edge); + if(originalsIt != pieceToOriginals.end()) + { // edge being split was split before: pass-through originals + newOriginals = originalsIt->second; + pieceToOriginals.erase(originalsIt); + } + detail::insert_unique(pieceToOriginals[half1], newOriginals); + detail::insert_unique(pieceToOriginals[half2], newOriginals); +} + +template <typename T, typename TNearPointLocator> +VertInd Triangulation<T, TNearPointLocator>::addSplitEdgeVertex( + const V2d<T>& splitVert, + const TriInd iT, + const TriInd iTopo) +{ + // add a new point on the edge that splits an edge in two + const VertInd iSplitVert = static_cast<VertInd>(vertices.size()); + addNewVertex(splitVert, noNeighbor); + std::stack<TriInd> triStack = insertVertexOnEdge(iSplitVert, iT, iTopo); + tryAddVertexToLocator(iSplitVert); + ensureDelaunayByEdgeFlips(splitVert, iSplitVert, triStack); + return iSplitVert; +} + +template <typename T, typename TNearPointLocator> +VertInd Triangulation<T, TNearPointLocator>::splitFixedEdgeAt( + const Edge& edge, + const V2d<T>& splitVert, + const TriInd iT, + const TriInd iTopo) +{ + const VertInd iSplitVert = addSplitEdgeVertex(splitVert, iT, iTopo); + splitFixedEdge(edge, iSplitVert); + return iSplitVert; +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::fixEdge( + const Edge& edge, + const Edge& originalEdge) +{ + fixEdge(edge); + if(edge != originalEdge) + detail::insert_unique(pieceToOriginals[edge], originalEdge); +} + +namespace detail +{ + +template <typename T> +T lerp(const T& a, const T& b, const T t) +{ + return (T(1) - t) * a + t * b; +} + +// Precondition: ab and cd intersect normally +template <typename T> +V2d<T> intersectionPosition( + const V2d<T>& a, + const V2d<T>& b, + const V2d<T>& c, + const V2d<T>& d) +{ + using namespace predicates::adaptive; + + // note: for better accuracy we interpolate x and y separately + // on a segment with the shortest x/y-projection correspondingly + const T a_cd = orient2d(c.x, c.y, d.x, d.y, a.x, a.y); + const T b_cd = orient2d(c.x, c.y, d.x, d.y, b.x, b.y); + const T t_ab = a_cd / (a_cd - b_cd); + + const T c_ab = orient2d(a.x, a.y, b.x, b.y, c.x, c.y); + const T d_ab = orient2d(a.x, a.y, b.x, b.y, d.x, d.y); + const T t_cd = c_ab / (c_ab - d_ab); + + return V2d<T>::make( + std::fabs(a.x - b.x) < std::fabs(c.x - d.x) ? lerp(a.x, b.x, t_ab) + : lerp(c.x, d.x, t_cd), + std::fabs(a.y - b.y) < std::fabs(c.y - d.y) ? lerp(a.y, b.y, t_ab) + : lerp(c.y, d.y, t_cd)); +} + +} // namespace detail + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::insertEdgeIteration( + const Edge edge, + const Edge originalEdge, + EdgeVec& remaining, + std::vector<TriangulatePseudopolygonTask>& tppIterations) +{ + const VertInd iA = edge.v1(); + VertInd iB = edge.v2(); + if(iA == iB) // edge connects a vertex to itself + return; + + if(hasEdge(iA, iB)) + { + fixEdge(edge, originalEdge); + return; + } + + const V2d<T>& a = vertices[iA]; + const V2d<T>& b = vertices[iB]; + const T distanceTolerance = + m_minDistToConstraintEdge == T(0) + ? T(0) + : m_minDistToConstraintEdge * distance(a, b); + + TriInd iT; + // Note: 'L' is left and 'R' is right of the inserted constraint edge + VertInd iVL, iVR; + tie(iT, iVL, iVR) = intersectedTriangle(iA, a, b, distanceTolerance); + // if one of the triangle vertices is on the edge, move edge start + if(iT == noNeighbor) + { + const Edge edgePart(iA, iVL); + fixEdge(edgePart, originalEdge); + remaining.push_back(Edge(iVL, iB)); + return; + } + Triangle t = triangles[iT]; + std::vector<TriInd> intersected(1, iT); + std::vector<VertInd> polyL, polyR; + std::vector<TriInd> outerTrisL, outerTrisR; + polyL.reserve(2); + polyL.push_back(iA); + polyL.push_back(iVL); + outerTrisL.push_back(edgeNeighbor(t, iA, iVL)); + polyR.reserve(2); + polyR.push_back(iA); + polyR.push_back(iVR); + outerTrisR.push_back(edgeNeighbor(t, iA, iVR)); + VertInd iV = iA; + IndexSizeType nChainedHangingEdgesL = 0; + IndexSizeType nChainedHangingEdgesR = 0; + + while(!t.containsVertex(iB)) + { + const TriInd iTopo = opposedTriangle(t, iV); + const Triangle& tOpo = triangles[iTopo]; + const VertInd iVopo = opposedVertex(tOpo, iT); + + // Resolve intersection between two constraint edges if needed + if(m_intersectingEdgesStrategy == + IntersectingConstraintEdges::Resolve && + fixedEdges.count(Edge(iVL, iVR))) + { + // split edge at the intersection of two constraint edges + const V2d<T> newV = detail::intersectionPosition( + vertices[iA], vertices[iB], vertices[iVL], vertices[iVR]); + const VertInd iNewVert = + splitFixedEdgeAt(Edge(iVL, iVR), newV, iT, iTopo); + // TODO: is it's possible to re-use pseudo-polygons + // for inserting [iA, iNewVert] edge half? + remaining.push_back(Edge(iA, iNewVert)); + remaining.push_back(Edge(iNewVert, iB)); + return; + } + + const PtLineLocation::Enum loc = + locatePointLine(vertices[iVopo], a, b, distanceTolerance); + if(loc == PtLineLocation::Left) + { + // hanging edge check + // previous entry of the vertex in poly if edge is hanging + const IndexSizeType prev = + (polyL.size() - 2) - 2 * nChainedHangingEdgesL; + if(iVopo == polyL[prev]) + { // hanging edge + ++nChainedHangingEdgesL; + outerTrisL[prev] = noNeighbor; + outerTrisL.push_back(noNeighbor); + } + else + { // normal case + nChainedHangingEdgesL = 0; + outerTrisL.push_back(edgeNeighbor(tOpo, polyL.back(), iVopo)); + } + polyL.push_back(iVopo); + iV = iVL; + iVL = iVopo; + } + else if(loc == PtLineLocation::Right) + { + // hanging edge check + // previous entry of the vertex in poly if edge is hanging + const IndexSizeType prev = + (polyR.size() - 2) - 2 * nChainedHangingEdgesR; + if(iVopo == polyR[prev]) + { // hanging edge + ++nChainedHangingEdgesR; + outerTrisR[prev] = noNeighbor; + outerTrisR.push_back(noNeighbor); + } + else + { // normal case + nChainedHangingEdgesR = 0; + outerTrisR.push_back(edgeNeighbor(tOpo, polyR.back(), iVopo)); + } + polyR.push_back(iVopo); + iV = iVR; + iVR = iVopo; + } + else // encountered point on the edge + iB = iVopo; + + intersected.push_back(iTopo); + iT = iTopo; + t = triangles[iT]; + } + outerTrisL.push_back(edgeNeighbor(t, polyL.back(), iB)); + outerTrisR.push_back(edgeNeighbor(t, polyR.back(), iB)); + polyL.push_back(iB); + polyR.push_back(iB); + + assert(!intersected.empty()); + // make sure start/end vertices have a valid adjacent triangle + // that is not intersected by an edge + if(m_vertTris[iA] == intersected.front()) + pivotVertexTriangleCW(iA); + if(m_vertTris[iB] == intersected.back()) + pivotVertexTriangleCW(iB); + // Remove intersected triangles + typedef std::vector<TriInd>::const_iterator TriIndCit; + for(TriIndCit it = intersected.begin(); it != intersected.end(); ++it) + makeDummy(*it); + { // Triangulate pseudo-polygons on both sides + std::reverse(polyR.begin(), polyR.end()); + std::reverse(outerTrisR.begin(), outerTrisR.end()); + const TriInd iTL = addTriangle(); + const TriInd iTR = addTriangle(); + triangulatePseudopolygon(polyL, outerTrisL, iTL, iTR, tppIterations); + triangulatePseudopolygon(polyR, outerTrisR, iTR, iTL, tppIterations); + } + + if(iB != edge.v2()) // encountered point on the edge + { + // fix edge part + const Edge edgePart(iA, iB); + fixEdge(edgePart, originalEdge); + remaining.push_back(Edge(iB, edge.v2())); + return; + } + else + { + fixEdge(edge, originalEdge); + } +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::insertEdge( + Edge edge, + const Edge originalEdge, + EdgeVec& remaining, + std::vector<TriangulatePseudopolygonTask>& tppIterations) +{ + // use iteration over recursion to avoid stack overflows + remaining.clear(); + remaining.push_back(edge); + while(!remaining.empty()) + { + edge = remaining.back(); + remaining.pop_back(); + insertEdgeIteration(edge, originalEdge, remaining, tppIterations); + } +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::conformToEdgeIteration( + Edge edge, + const EdgeVec& originals, + BoundaryOverlapCount overlaps, + std::vector<ConformToEdgeTask>& remaining) +{ + const VertInd iA = edge.v1(); + VertInd iB = edge.v2(); + if(iA == iB) // edge connects a vertex to itself + return; + + if(hasEdge(iA, iB)) + { + fixEdge(edge); + if(overlaps > 0) + overlapCount[edge] = overlaps; + // avoid marking edge as a part of itself + if(!originals.empty() && edge != originals.front()) + { + detail::insert_unique(pieceToOriginals[edge], originals); + } + return; + } + + const V2d<T>& a = vertices[iA]; + const V2d<T>& b = vertices[iB]; + const T distanceTolerance = + m_minDistToConstraintEdge == T(0) + ? T(0) + : m_minDistToConstraintEdge * distance(a, b); + TriInd iT; + VertInd iVleft, iVright; + tie(iT, iVleft, iVright) = intersectedTriangle(iA, a, b, distanceTolerance); + // if one of the triangle vertices is on the edge, move edge start + if(iT == noNeighbor) + { + const Edge edgePart(iA, iVleft); + fixEdge(edgePart); + if(overlaps > 0) + overlapCount[edgePart] = overlaps; + detail::insert_unique(pieceToOriginals[edgePart], originals); +#ifdef CDT_CXX11_IS_SUPPORTED + remaining.emplace_back(Edge(iVleft, iB), originals, overlaps); +#else + remaining.push_back(make_tuple(Edge(iVleft, iB), originals, overlaps)); +#endif + return; + } + + VertInd iV = iA; + Triangle t = triangles[iT]; + while(std::find(t.vertices.begin(), t.vertices.end(), iB) == + t.vertices.end()) + { + const TriInd iTopo = opposedTriangle(t, iV); + const Triangle& tOpo = triangles[iTopo]; + const VertInd iVopo = opposedVertex(tOpo, iT); + const V2d<T> vOpo = vertices[iVopo]; + + // Resolve intersection between two constraint edges if needed + if(m_intersectingEdgesStrategy == + IntersectingConstraintEdges::Resolve && + fixedEdges.count(Edge(iVleft, iVright))) + { + // split edge at the intersection of two constraint edges + const V2d<T> newV = detail::intersectionPosition( + vertices[iA], + vertices[iB], + vertices[iVleft], + vertices[iVright]); + const VertInd iNewVert = + splitFixedEdgeAt(Edge(iVleft, iVright), newV, iT, iTopo); +#ifdef CDT_CXX11_IS_SUPPORTED + remaining.emplace_back(Edge(iNewVert, iB), originals, overlaps); + remaining.emplace_back(Edge(iA, iNewVert), originals, overlaps); +#else + remaining.push_back( + make_tuple(Edge(iNewVert, iB), originals, overlaps)); + remaining.push_back( + make_tuple(Edge(iA, iNewVert), originals, overlaps)); +#endif + return; + } + + iT = iTopo; + t = triangles[iT]; + + const PtLineLocation::Enum loc = + locatePointLine(vOpo, a, b, distanceTolerance); + if(loc == PtLineLocation::Left) + { + iV = iVleft; + iVleft = iVopo; + } + else if(loc == PtLineLocation::Right) + { + iV = iVright; + iVright = iVopo; + } + else // encountered point on the edge + iB = iVopo; + } + + // encountered one or more points on the edge: add remaining edge part + if(iB != edge.v2()) + { +#ifdef CDT_CXX11_IS_SUPPORTED + remaining.emplace_back(Edge(iB, edge.v2()), originals, overlaps); +#else + remaining.push_back( + make_tuple(Edge(iB, edge.v2()), originals, overlaps)); +#endif + } + + // add mid-point to triangulation + const VertInd iMid = static_cast<VertInd>(vertices.size()); + const V2d<T>& start = vertices[iA]; + const V2d<T>& end = vertices[iB]; + addNewVertex( + V2d<T>::make((start.x + end.x) / T(2), (start.y + end.y) / T(2)), + noNeighbor); + const std::vector<Edge> flippedFixedEdges = + insertVertex_FlipFixedEdges(iMid); + +#ifdef CDT_CXX11_IS_SUPPORTED + remaining.emplace_back(Edge(iMid, iB), originals, overlaps); + remaining.emplace_back(Edge(iA, iMid), originals, overlaps); +#else + remaining.push_back(make_tuple(Edge(iMid, iB), originals, overlaps)); + remaining.push_back(make_tuple(Edge(iA, iMid), originals, overlaps)); +#endif + + // re-introduce fixed edges that were flipped + // and make sure overlap count is preserved + for(std::vector<Edge>::const_iterator it = flippedFixedEdges.begin(); + it != flippedFixedEdges.end(); + ++it) + { + const Edge& flippedFixedEdge = *it; + fixedEdges.erase(flippedFixedEdge); + + BoundaryOverlapCount prevOverlaps = 0; + const unordered_map<Edge, BoundaryOverlapCount>::const_iterator + overlapsIt = overlapCount.find(flippedFixedEdge); + if(overlapsIt != overlapCount.end()) + { + prevOverlaps = overlapsIt->second; + overlapCount.erase(overlapsIt); + } + // override overlapping boundaries count when re-inserting an edge + EdgeVec prevOriginals(1, flippedFixedEdge); + const unordered_map<Edge, EdgeVec>::const_iterator originalsIt = + pieceToOriginals.find(flippedFixedEdge); + if(originalsIt != pieceToOriginals.end()) + { + prevOriginals = originalsIt->second; + } +#ifdef CDT_CXX11_IS_SUPPORTED + remaining.emplace_back(flippedFixedEdge, prevOriginals, prevOverlaps); +#else + remaining.push_back( + make_tuple(flippedFixedEdge, prevOriginals, prevOverlaps)); +#endif + } +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::conformToEdge( + Edge edge, + EdgeVec originals, + BoundaryOverlapCount overlaps, + std::vector<ConformToEdgeTask>& remaining) +{ + // use iteration over recursion to avoid stack overflows + remaining.clear(); +#ifdef CDT_CXX11_IS_SUPPORTED + remaining.emplace_back(edge, originals, overlaps); +#else + remaining.push_back(make_tuple(edge, originals, overlaps)); +#endif + while(!remaining.empty()) + { + tie(edge, originals, overlaps) = remaining.back(); + remaining.pop_back(); + conformToEdgeIteration(edge, originals, overlaps, remaining); + } +} + +/*! + * Returns: + * - intersected triangle index + * - index of point on the left of the line + * - index of point on the right of the line + * If left point is right on the line: no triangle is intersected: + * - triangle index is no-neighbor (invalid) + * - index of point on the line + * - index of point on the right of the line + */ +template <typename T, typename TNearPointLocator> +tuple<TriInd, VertInd, VertInd> +Triangulation<T, TNearPointLocator>::intersectedTriangle( + const VertInd iA, + const V2d<T>& a, + const V2d<T>& b, + const T orientationTolerance) const +{ + const TriInd startTri = m_vertTris[iA]; + TriInd iT = startTri; + do + { + const Triangle t = triangles[iT]; + const Index i = vertexInd(t.vertices, iA); + const VertInd iP2 = t.vertices[ccw(i)]; + const T orientP2 = orient2D(vertices[iP2], a, b); + const PtLineLocation::Enum locP2 = classifyOrientation(orientP2); + if(locP2 == PtLineLocation::Right) + { + const VertInd iP1 = t.vertices[cw(i)]; + const T orientP1 = orient2D(vertices[iP1], a, b); + const PtLineLocation::Enum locP1 = classifyOrientation(orientP1); + if(locP1 == PtLineLocation::OnLine) + { + return make_tuple(noNeighbor, iP1, iP1); + } + if(locP1 == PtLineLocation::Left) + { + if(orientationTolerance) + { + T closestOrient; + VertInd iClosestP; + if(std::abs(orientP1) <= std::abs(orientP2)) + { + closestOrient = orientP1; + iClosestP = iP1; + } + else + { + closestOrient = orientP2; + iClosestP = iP2; + } + if(classifyOrientation( + closestOrient, orientationTolerance) == + PtLineLocation::OnLine) + { + return make_tuple(noNeighbor, iClosestP, iClosestP); + } + } + return make_tuple(iT, iP1, iP2); + } + } + iT = t.next(iA).first; + } while(iT != startTri); + throw std::runtime_error("Could not find vertex triangle intersected by " + "edge. Note: can be caused by duplicate points."); +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::addSuperTriangle(const Box2d<T>& box) +{ + m_nTargetVerts = 3; + m_superGeomType = SuperGeometryType::SuperTriangle; + + const V2d<T> center = { + (box.min.x + box.max.x) / T(2), (box.min.y + box.max.y) / T(2)}; + const T w = box.max.x - box.min.x; + const T h = box.max.y - box.min.y; + T r = std::sqrt(w * w + h * h) / T(2); // incircle radius + r *= T(1.1); + const T R = T(2) * r; // excircle radius + const T shiftX = R * std::sqrt(T(3)) / T(2); // R * cos(30 deg) + const V2d<T> posV1 = {center.x - shiftX, center.y - r}; + const V2d<T> posV2 = {center.x + shiftX, center.y - r}; + const V2d<T> posV3 = {center.x, center.y + R}; + addNewVertex(posV1, TriInd(0)); + addNewVertex(posV2, TriInd(0)); + addNewVertex(posV3, TriInd(0)); + const Triangle superTri = { + {VertInd(0), VertInd(1), VertInd(2)}, + {noNeighbor, noNeighbor, noNeighbor}}; + addTriangle(superTri); + if(m_vertexInsertionOrder != VertexInsertionOrder::Auto) + { + m_nearPtLocator.initialize(vertices); + } +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::addNewVertex( + const V2d<T>& pos, + const TriInd iT) +{ + vertices.push_back(pos); + m_vertTris.push_back(iT); +} + +template <typename T, typename TNearPointLocator> +std::vector<Edge> +Triangulation<T, TNearPointLocator>::insertVertex_FlipFixedEdges( + const VertInd iV1) +{ + std::vector<Edge> flippedFixedEdges; + + const V2d<T>& v1 = vertices[iV1]; + const VertInd startVertex = m_nearPtLocator.nearPoint(v1, vertices); + array<TriInd, 2> trisAt = walkingSearchTrianglesAt(v1, startVertex); + std::stack<TriInd> triStack = + trisAt[1] == noNeighbor ? insertVertexInsideTriangle(iV1, trisAt[0]) + : insertVertexOnEdge(iV1, trisAt[0], trisAt[1]); + + TriInd iTopo, n1, n2, n3, n4; + VertInd iV2, iV3, iV4; + while(!triStack.empty()) + { + const TriInd iT = triStack.top(); + triStack.pop(); + + edgeFlipInfo(iT, iV1, iTopo, iV2, iV3, iV4, n1, n2, n3, n4); + if(iTopo != noNeighbor && isFlipNeeded(v1, iV1, iV2, iV3, iV4)) + { + // if flipped edge is fixed, remember it + const Edge flippedEdge(iV2, iV4); + if(!fixedEdges.empty() && + fixedEdges.find(flippedEdge) != fixedEdges.end()) + { + flippedFixedEdges.push_back(flippedEdge); + } + + flipEdge(iT, iTopo, iV1, iV2, iV3, iV4, n1, n2, n3, n4); + triStack.push(iT); + triStack.push(iTopo); + } + } + + tryAddVertexToLocator(iV1); + return flippedFixedEdges; +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::insertVertex( + const VertInd iVert, + const VertInd walkStart) +{ + const V2d<T>& v = vertices[iVert]; + const array<TriInd, 2> trisAt = walkingSearchTrianglesAt(v, walkStart); + std::stack<TriInd> triStack = + trisAt[1] == noNeighbor + ? insertVertexInsideTriangle(iVert, trisAt[0]) + : insertVertexOnEdge(iVert, trisAt[0], trisAt[1]); + ensureDelaunayByEdgeFlips(v, iVert, triStack); +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::insertVertex(const VertInd iVert) +{ + const V2d<T>& v = vertices[iVert]; + const VertInd walkStart = m_nearPtLocator.nearPoint(v, vertices); + insertVertex(iVert, walkStart); + tryAddVertexToLocator(iVert); +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::ensureDelaunayByEdgeFlips( + const V2d<T>& v1, + const VertInd iV1, + std::stack<TriInd>& triStack) +{ + TriInd iTopo, n1, n2, n3, n4; + VertInd iV2, iV3, iV4; + while(!triStack.empty()) + { + const TriInd iT = triStack.top(); + triStack.pop(); + + edgeFlipInfo(iT, iV1, iTopo, iV2, iV3, iV4, n1, n2, n3, n4); + if(iTopo != noNeighbor && isFlipNeeded(v1, iV1, iV2, iV3, iV4)) + { + flipEdge(iT, iTopo, iV1, iV2, iV3, iV4, n1, n2, n3, n4); + triStack.push(iT); + triStack.push(iTopo); + } + } +} + +/* + * v4 original edge: (v1, v3) + * /|\ flip-candidate edge: (v, v2) + * / | \ + * n3 / | \ n4 + * / | \ + * new vertex--> v1 T | Topo v3 + * \ | / + * n1 \ | / n2 + * \ | / + * \|/ + * v2 + */ +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::edgeFlipInfo( + const TriInd iT, + const VertInd iV1, + TriInd& iTopo, + VertInd& iV2, + VertInd& iV3, + VertInd& iV4, + TriInd& n1, + TriInd& n2, + TriInd& n3, + TriInd& n4) +{ + /* v[2] + / \ + n[2]/ \n[1] + /_____\ + v[0] n[0] v[1] */ + const Triangle& t = triangles[iT]; + if(t.vertices[0] == iV1) + { + iV2 = t.vertices[1]; + iV4 = t.vertices[2]; + n1 = t.neighbors[0]; + n3 = t.neighbors[2]; + iTopo = t.neighbors[1]; + } + else if(t.vertices[1] == iV1) + { + iV2 = t.vertices[2]; + iV4 = t.vertices[0]; + n1 = t.neighbors[1]; + n3 = t.neighbors[0]; + iTopo = t.neighbors[2]; + } + else + { + iV2 = t.vertices[0]; + iV4 = t.vertices[1]; + n1 = t.neighbors[2]; + n3 = t.neighbors[1]; + iTopo = t.neighbors[0]; + } + if(iTopo == noNeighbor) + return; + const Triangle& tOpo = triangles[iTopo]; + if(tOpo.neighbors[0] == iT) + { + iV3 = tOpo.vertices[2]; + n2 = tOpo.neighbors[1]; + n4 = tOpo.neighbors[2]; + } + else if(tOpo.neighbors[1] == iT) + { + iV3 = tOpo.vertices[0]; + n2 = tOpo.neighbors[2]; + n4 = tOpo.neighbors[0]; + } + else + { + iV3 = tOpo.vertices[1]; + n2 = tOpo.neighbors[0]; + n4 = tOpo.neighbors[1]; + } +} + +/*! + * Handles super-triangle vertices. + * Super-tri points are not infinitely far and influence the input points + * Three cases are possible: + * 1. If one of the opposed vertices is super-tri: no flip needed + * 2. One of the shared vertices is super-tri: + * check if on point is same side of line formed by non-super-tri + * vertices as the non-super-tri shared vertex + * 3. None of the vertices are super-tri: normal circumcircle test + */ +/* + * v4 original edge: (v2, v4) + * /|\ flip-candidate edge: (v1, v3) + * / | \ + * / | \ + * / | \ + * new vertex--> v1 | v3 + * \ | / + * \ | / + * \ | / + * \|/ + * v2 + */ +template <typename T, typename TNearPointLocator> +bool Triangulation<T, TNearPointLocator>::isFlipNeeded( + const V2d<T>& v, + const VertInd iV1, + const VertInd iV2, + const VertInd iV3, + const VertInd iV4) const +{ + if(fixedEdges.count(Edge(iV2, iV4))) + return false; // flip not needed if the original edge is fixed + const V2d<T>& v2 = vertices[iV2]; + const V2d<T>& v3 = vertices[iV3]; + const V2d<T>& v4 = vertices[iV4]; + if(m_superGeomType == SuperGeometryType::SuperTriangle) + { + // If flip-candidate edge touches super-triangle in-circumference + // test has to be replaced with orient2d test against the line + // formed by two non-artificial vertices (that don't belong to + // super-triangle) + if(iV1 < 3) // flip-candidate edge touches super-triangle + { + // does original edge also touch super-triangle? + if(iV2 < 3) + return locatePointLine(v2, v3, v4) == + locatePointLine(v, v3, v4); + if(iV4 < 3) + return locatePointLine(v4, v2, v3) == + locatePointLine(v, v2, v3); + return false; // original edge does not touch super-triangle + } + if(iV3 < 3) // flip-candidate edge touches super-triangle + { + // does original edge also touch super-triangle? + if(iV2 < 3) + return locatePointLine(v2, v, v4) == locatePointLine(v3, v, v4); + if(iV4 < 3) + return locatePointLine(v4, v2, v) == locatePointLine(v3, v2, v); + return false; // original edge does not touch super-triangle + } + // flip-candidate edge does not touch super-triangle + if(iV2 < 3) + return locatePointLine(v2, v3, v4) == locatePointLine(v, v3, v4); + if(iV4 < 3) + return locatePointLine(v4, v2, v3) == locatePointLine(v, v2, v3); + } + return isInCircumcircle(v, v2, v3, v4); +} + +/* Flip edge between T and Topo: + * + * v4 | - old edge + * /|\ ~ - new edge + * / | \ + * n3 / T' \ n4 + * / | \ + * / | \ + * T -> v1 ~~~~~~~~ v3 <- Topo + * \ | / + * \ | / + * n1 \Topo'/ n2 + * \ | / + * \|/ + * v2 + */ +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::flipEdge( + const TriInd iT, + const TriInd iTopo, + const VertInd v1, + const VertInd v2, + const VertInd v3, + const VertInd v4, + const TriInd n1, + const TriInd n2, + const TriInd n3, + const TriInd n4) +{ + // change vertices and neighbors + using detail::arr3; + triangles[iT] = Triangle::make(arr3(v4, v1, v3), arr3(n3, iTopo, n4)); + triangles[iTopo] = Triangle::make(arr3(v2, v3, v1), arr3(n2, iT, n1)); + // adjust neighboring triangles and vertices + changeNeighbor(n1, iT, iTopo); + changeNeighbor(n4, iTopo, iT); + // only adjust adjacent triangles if triangulation is not finalized: + // can happen when called from outside on an already finalized + // triangulation + if(!isFinalized()) + { + setAdjacentTriangle(v4, iT); + setAdjacentTriangle(v2, iTopo); + } +} + +/* Insert point into triangle: split into 3 triangles: + * - create 2 new triangles + * - re-use old triangle for the 3rd + * v3 + * / | \ + * / | \ <-- original triangle (t) + * / | \ + * n3 / | \ n2 + * /newT2|newT1\ + * / v \ + * / __/ \__ \ + * / __/ \__ \ + * / _/ t' \_ \ + * v1 ___________________ v2 + * n1 + */ +template <typename T, typename TNearPointLocator> +std::stack<TriInd> +Triangulation<T, TNearPointLocator>::insertVertexInsideTriangle( + VertInd v, + TriInd iT) +{ + const TriInd iNewT1 = addTriangle(); + const TriInd iNewT2 = addTriangle(); + + Triangle& t = triangles[iT]; + const array<VertInd, 3> vv = t.vertices; + const array<TriInd, 3> nn = t.neighbors; + const VertInd v1 = vv[0], v2 = vv[1], v3 = vv[2]; + const TriInd n1 = nn[0], n2 = nn[1], n3 = nn[2]; + // make two new triangles and convert current triangle to 3rd new + // triangle + using detail::arr3; + triangles[iNewT1] = Triangle::make(arr3(v2, v3, v), arr3(n2, iNewT2, iT)); + triangles[iNewT2] = Triangle::make(arr3(v3, v1, v), arr3(n3, iT, iNewT1)); + t = Triangle::make(arr3(v1, v2, v), arr3(n1, iNewT1, iNewT2)); + // adjust adjacent triangles + setAdjacentTriangle(v, iT); + setAdjacentTriangle(v3, iNewT1); + // change triangle neighbor's neighbors to new triangles + changeNeighbor(n2, iT, iNewT1); + changeNeighbor(n3, iT, iNewT2); + // return newly added triangles + std::stack<TriInd> newTriangles; + newTriangles.push(iT); + newTriangles.push(iNewT1); + newTriangles.push(iNewT2); + return newTriangles; +} + +/* Inserting a point on the edge between two triangles + * T1 (top) v1 + * /|\ + * n1 / | \ n4 + * / | \ + * / T1' | Tnew1\ + * v2-------v-------v4 + * \ T2' | Tnew2/ + * \ | / + * n2 \ | / n3 + * \|/ + * T2 (bottom) v3 + */ +template <typename T, typename TNearPointLocator> +std::stack<TriInd> Triangulation<T, TNearPointLocator>::insertVertexOnEdge( + VertInd v, + TriInd iT1, + TriInd iT2) +{ + const TriInd iTnew1 = addTriangle(); + const TriInd iTnew2 = addTriangle(); + + Triangle& t1 = triangles[iT1]; + Triangle& t2 = triangles[iT2]; + Index i = opposedVertexInd(t1.neighbors, iT2); + const VertInd v1 = t1.vertices[i]; + const VertInd v2 = t1.vertices[ccw(i)]; + const TriInd n1 = t1.neighbors[i]; + const TriInd n4 = t1.neighbors[cw(i)]; + i = opposedVertexInd(t2.neighbors, iT1); + const VertInd v3 = t2.vertices[i]; + const VertInd v4 = t2.vertices[ccw(i)]; + const TriInd n3 = t2.neighbors[i]; + const TriInd n2 = t2.neighbors[cw(i)]; + // add new triangles and change existing ones + using detail::arr3; + t1 = Triangle::make(arr3(v, v1, v2), arr3(iTnew1, n1, iT2)); + t2 = Triangle::make(arr3(v, v2, v3), arr3(iT1, n2, iTnew2)); + triangles[iTnew1] = Triangle::make(arr3(v, v4, v1), arr3(iTnew2, n4, iT1)); + triangles[iTnew2] = Triangle::make(arr3(v, v3, v4), arr3(iT2, n3, iTnew1)); + // adjust adjacent triangles + setAdjacentTriangle(v, iT1); + setAdjacentTriangle(v4, iTnew1); + // adjust neighboring triangles and vertices + changeNeighbor(n4, iT1, iTnew1); + changeNeighbor(n3, iT2, iTnew2); + // return newly added triangles + std::stack<TriInd> newTriangles; + newTriangles.push(iT1); + newTriangles.push(iTnew2); + newTriangles.push(iT2); + newTriangles.push(iTnew1); + return newTriangles; +} + +template <typename T, typename TNearPointLocator> +array<TriInd, 2> +Triangulation<T, TNearPointLocator>::trianglesAt(const V2d<T>& pos) const +{ + array<TriInd, 2> out = {noNeighbor, noNeighbor}; + for(TriInd i = TriInd(0); i < TriInd(triangles.size()); ++i) + { + const Triangle& t = triangles[i]; + const V2d<T>& v1 = vertices[t.vertices[0]]; + const V2d<T>& v2 = vertices[t.vertices[1]]; + const V2d<T>& v3 = vertices[t.vertices[2]]; + const PtTriLocation::Enum loc = locatePointTriangle(pos, v1, v2, v3); + if(loc == PtTriLocation::Outside) + continue; + out[0] = i; + if(isOnEdge(loc)) + out[1] = t.neighbors[edgeNeighbor(loc)]; + return out; + } + throw std::runtime_error("No triangle was found at position"); +} + +template <typename T, typename TNearPointLocator> +TriInd Triangulation<T, TNearPointLocator>::walkTriangles( + const VertInd startVertex, + const V2d<T>& pos) const +{ + // begin walk in search of triangle at pos + TriInd currTri = m_vertTris[startVertex]; + bool found = false; + detail::SplitMix64RandGen prng; + while(!found) + { + const Triangle& t = triangles[currTri]; + found = true; + // stochastic offset to randomize which edge we check first + const Index offset(prng() % 3); + for(Index i_(0); i_ < Index(3); ++i_) + { + const Index i((i_ + offset) % 3); + const V2d<T>& vStart = vertices[t.vertices[i]]; + const V2d<T>& vEnd = vertices[t.vertices[ccw(i)]]; + const PtLineLocation::Enum edgeCheck = + locatePointLine(pos, vStart, vEnd); + const TriInd iN = t.neighbors[i]; + if(edgeCheck == PtLineLocation::Right && iN != noNeighbor) + { + found = false; + currTri = t.neighbors[i]; + break; + } + } + } + return currTri; +} + +template <typename T, typename TNearPointLocator> +array<TriInd, 2> Triangulation<T, TNearPointLocator>::walkingSearchTrianglesAt( + const V2d<T>& pos, + const VertInd startVertex) const +{ + array<TriInd, 2> out = {noNeighbor, noNeighbor}; + const TriInd iT = walkTriangles(startVertex, pos); + // Finished walk, locate point in current triangle + const Triangle& t = triangles[iT]; + const V2d<T>& v1 = vertices[t.vertices[0]]; + const V2d<T>& v2 = vertices[t.vertices[1]]; + const V2d<T>& v3 = vertices[t.vertices[2]]; + const PtTriLocation::Enum loc = locatePointTriangle(pos, v1, v2, v3); + if(loc == PtTriLocation::Outside) + throw std::runtime_error("No triangle was found at position"); + out[0] = iT; + if(isOnEdge(loc)) + out[1] = t.neighbors[edgeNeighbor(loc)]; + return out; +} + +/* Flip edge between T and Topo: + * + * v4 | - old edge + * /|\ ~ - new edge + * / | \ + * n3 / T' \ n4 + * / | \ + * / | \ + * T -> v1~~~~~~~~~v3 <- Topo + * \ | / + * \ | / + * n1 \Topo'/ n2 + * \ | / + * \|/ + * v2 + */ +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::flipEdge( + const TriInd iT, + const TriInd iTopo) +{ + Triangle& t = triangles[iT]; + Triangle& tOpo = triangles[iTopo]; + const array<TriInd, 3>& triNs = t.neighbors; + const array<TriInd, 3>& triOpoNs = tOpo.neighbors; + const array<VertInd, 3>& triVs = t.vertices; + const array<VertInd, 3>& triOpoVs = tOpo.vertices; + // find vertices and neighbors + Index i = opposedVertexInd(t.neighbors, iTopo); + const VertInd v1 = triVs[i]; + const VertInd v2 = triVs[ccw(i)]; + const TriInd n1 = triNs[i]; + const TriInd n3 = triNs[cw(i)]; + i = opposedVertexInd(tOpo.neighbors, iT); + const VertInd v3 = triOpoVs[i]; + const VertInd v4 = triOpoVs[ccw(i)]; + const TriInd n4 = triOpoNs[i]; + const TriInd n2 = triOpoNs[cw(i)]; + // change vertices and neighbors + using detail::arr3; + t = Triangle::make(arr3(v4, v1, v3), arr3(n3, iTopo, n4)); + tOpo = Triangle::make(arr3(v2, v3, v1), arr3(n2, iT, n1)); + // adjust neighboring triangles and vertices + changeNeighbor(n1, iT, iTopo); + changeNeighbor(n4, iTopo, iT); + // only adjust adjacent triangles if triangulation is not finalized: + // can happen when called from outside on an already finalized + // triangulation + if(!isFinalized()) + { + setAdjacentTriangle(v4, iT); + setAdjacentTriangle(v2, iTopo); + } +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::changeNeighbor( + const TriInd iT, + const TriInd oldNeighbor, + const TriInd newNeighbor) +{ + if(iT == noNeighbor) + return; + NeighborsArr3& nn = triangles[iT].neighbors; + assert( + nn[0] == oldNeighbor || nn[1] == oldNeighbor || nn[2] == oldNeighbor); + if(nn[0] == oldNeighbor) + nn[0] = newNeighbor; + else if(nn[1] == oldNeighbor) + nn[1] = newNeighbor; + else + nn[2] = newNeighbor; +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::changeNeighbor( + const TriInd iT, + const VertInd iVedge1, + const VertInd iVedge2, + const TriInd newNeighbor) +{ + assert(iT != noNeighbor); + Triangle& t = triangles[iT]; + t.neighbors[edgeNeighborInd(t.vertices, iVedge1, iVedge2)] = newNeighbor; +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::triangulatePseudopolygon( + const std::vector<VertInd>& poly, + const std::vector<TriInd>& outerTris, + const TriInd iT, + const TriInd iN, + std::vector<TriangulatePseudopolygonTask>& iterations) +{ + assert(poly.size() > 2); + + // note: needed for proper linking with outer triangles + // during pseudo-polygon triangulation, vertex triangle + // will be set back, see asserts at the end + for(std::size_t i = 1; i < outerTris.size(); ++i) + if(outerTris[i] == noNeighbor) + m_vertTris[poly[i]] = noNeighbor; + + // note: uses interation instead of recursion to avoid stack overflows + iterations.clear(); + iterations.push_back(make_tuple( + IndexSizeType(0), + static_cast<IndexSizeType>(poly.size() - 1), + iT, + iN, + Index(0))); + while(!iterations.empty()) + { + triangulatePseudopolygonIteration(poly, outerTris, iterations); + } + + // make sure adjacent triangles were restored + for(std::size_t i = 0; i < poly.size(); ++i) + assert(m_vertTris[poly[i]] != noNeighbor); +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::triangulatePseudopolygonIteration( + const std::vector<VertInd>& poly, + const std::vector<TriInd>& outerTris, + std::vector<TriangulatePseudopolygonTask>& iterations) +{ + IndexSizeType iA, iB; + TriInd iT, iParent; + Index iInParent; + assert(!iterations.empty()); + tie(iA, iB, iT, iParent, iInParent) = iterations.back(); + iterations.pop_back(); + assert(iB - iA > 1 && iT != noNeighbor && iParent != noNeighbor); + Triangle& t = triangles[iT]; + // find Delaunay point + const IndexSizeType iC = findDelaunayPoint(poly, iA, iB); + + const VertInd a = poly[iA]; + const VertInd b = poly[iB]; + const VertInd c = poly[iC]; + // split pseudo-polygon in two parts and triangulate them + // + // note: first part needs to be pushed on stack last + // in order to be processed first + // + // second part: points after the Delaunay point + if(iB - iC > 1) + { + const TriInd iNext = addTriangle(); + iterations.push_back(make_tuple(iC, iB, iNext, iT, Index(1))); + } + else // pseudo-poly is reduced to a single outer edge + { + const TriInd outerTri = outerTris[iC]; + if(outerTri != noNeighbor) + { + assert(outerTri != iT); + t.neighbors[1] = outerTri; + changeNeighbor(outerTri, c, b, iT); + } + } + // first part: points before the Delaunay point + if(iC - iA > 1) + { // add next triangle and add another iteration + const TriInd iNext = addTriangle(); + iterations.push_back(make_tuple(iA, iC, iNext, iT, Index(2))); + } + else + { // pseudo-poly is reduced to a single outer edge + const TriInd outerTri = + outerTris[iA] != noNeighbor ? outerTris[iA] : m_vertTris[c]; + if(outerTri != noNeighbor) + { + assert(outerTri != iT); + t.neighbors[2] = outerTri; + changeNeighbor(outerTri, c, a, iT); + } + } + // Finalize triangle + // note: only when triangle is finalized to we add it as a neighbor to + // parent to maintain triangulation topology consistency + triangles[iParent].neighbors[iInParent] = iT; + t.neighbors[0] = iParent; + t.vertices = detail::arr3(a, b, c); + setAdjacentTriangle(a, iT); + setAdjacentTriangle(c, iT); +} + +template <typename T, typename TNearPointLocator> +IndexSizeType Triangulation<T, TNearPointLocator>::findDelaunayPoint( + const std::vector<VertInd>& poly, + const IndexSizeType iA, + const IndexSizeType iB) const +{ + assert(iB - iA > 1); + const V2d<T>& a = vertices[poly[iA]]; + const V2d<T>& b = vertices[poly[iB]]; + IndexSizeType out = iA + 1; + const V2d<T>* c = &vertices[poly[out]]; // caching for better performance + for(IndexSizeType i = iA + 1; i < iB; ++i) + { + const V2d<T>& v = vertices[poly[i]]; + if(isInCircumcircle(v, a, b, *c)) + { + out = i; + c = &v; + } + } + assert(out > iA && out < iB); // point is between ends + return out; +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::insertVertices( + const std::vector<V2d<T> >& newVertices) +{ + return insertVertices( + newVertices.begin(), newVertices.end(), getX_V2d<T>, getY_V2d<T>); +} + +template <typename T, typename TNearPointLocator> +bool Triangulation<T, TNearPointLocator>::isFinalized() const +{ + return m_vertTris.empty() && !vertices.empty(); +} + +template <typename T, typename TNearPointLocator> +unordered_map<TriInd, LayerDepth> +Triangulation<T, TNearPointLocator>::peelLayer( + std::stack<TriInd> seeds, + const LayerDepth layerDepth, + std::vector<LayerDepth>& triDepths) const +{ + unordered_map<TriInd, LayerDepth> behindBoundary; + while(!seeds.empty()) + { + const TriInd iT = seeds.top(); + seeds.pop(); + triDepths[iT] = std::min(triDepths[iT], layerDepth); + behindBoundary.erase(iT); + const Triangle& t = triangles[iT]; + for(Index i(0); i < Index(3); ++i) + { + const Edge opEdge(t.vertices[ccw(i)], t.vertices[cw(i)]); + const TriInd iN = t.neighbors[opoNbr(i)]; + if(iN == noNeighbor || triDepths[iN] <= layerDepth) + continue; + if(fixedEdges.count(opEdge)) + { + const unordered_map<Edge, LayerDepth>::const_iterator cit = + overlapCount.find(opEdge); + const LayerDepth triDepth = cit == overlapCount.end() + ? layerDepth + 1 + : layerDepth + cit->second + 1; + behindBoundary[iN] = triDepth; + continue; + } + seeds.push(iN); + } + } + return behindBoundary; +} + +template <typename T, typename TNearPointLocator> +std::vector<LayerDepth> +Triangulation<T, TNearPointLocator>::calculateTriangleDepths() const +{ + std::vector<LayerDepth> triDepths( + triangles.size(), std::numeric_limits<LayerDepth>::max()); + std::stack<TriInd> seeds(TriDeque(1, m_vertTris[0])); + LayerDepth layerDepth = 0; + LayerDepth deepestSeedDepth = 0; + + unordered_map<LayerDepth, TriIndUSet> seedsByDepth; + do + { + const unordered_map<TriInd, LayerDepth>& newSeeds = + peelLayer(seeds, layerDepth, triDepths); + + seedsByDepth.erase(layerDepth); + typedef unordered_map<TriInd, LayerDepth>::const_iterator Iter; + for(Iter it = newSeeds.begin(); it != newSeeds.end(); ++it) + { + deepestSeedDepth = std::max(deepestSeedDepth, it->second); + seedsByDepth[it->second].insert(it->first); + } + const TriIndUSet& nextLayerSeeds = seedsByDepth[layerDepth + 1]; + seeds = std::stack<TriInd>( + TriDeque(nextLayerSeeds.begin(), nextLayerSeeds.end())); + ++layerDepth; + } while(!seeds.empty() || deepestSeedDepth > layerDepth); + + return triDepths; +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::insertVertices_AsProvided( + VertInd superGeomVertCount) +{ + for(VertInd iV = superGeomVertCount; iV < vertices.size(); ++iV) + { + insertVertex(iV); + } +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::insertVertices_Randomized( + VertInd superGeomVertCount) +{ + std::size_t vertexCount = vertices.size() - superGeomVertCount; + std::vector<VertInd> ii(vertexCount); + detail::iota(ii.begin(), ii.end(), superGeomVertCount); + detail::random_shuffle(ii.begin(), ii.end()); + for(std::vector<VertInd>::iterator it = ii.begin(); it != ii.end(); ++it) + { + insertVertex(*it); + } +} + +namespace detail +{ + +// log2 implementation backwards compatible with pre c++11 +template <typename T> +inline double log2_bc(T x) +{ +#ifdef CDT_CXX11_IS_SUPPORTED + return std::log2(x); +#else + static double log2_constant = std::log(2.0); + return std::log(static_cast<double>(x)) / log2_constant; +#endif +} + +/// Since KD-tree bulk load builds a balanced tree the maximum length of a +/// queue can be pre-calculated: it is calculated as size of a completely +/// filled tree layer plus the number of the nodes on a completely filled +/// layer that have two children. +inline std::size_t maxQueueLengthBFSKDTree(const std::size_t vertexCount) +{ + const int filledLayerPow2 = + static_cast<int>(std::floor(log2_bc(vertexCount)) - 1); + const std::size_t nodesInFilledTree = + static_cast<std::size_t>(std::pow(2., filledLayerPow2 + 1) - 1); + const std::size_t nodesInLastFilledLayer = + static_cast<std::size_t>(std::pow(2., filledLayerPow2)); + const std::size_t nodesInLastLayer = vertexCount - nodesInFilledTree; + return nodesInLastLayer >= nodesInLastFilledLayer + ? nodesInLastFilledLayer + nodesInLastLayer - + nodesInLastFilledLayer + : nodesInLastFilledLayer; +} + +template <typename T> +class FixedCapacityQueue +{ +public: + FixedCapacityQueue(const std::size_t capacity) + : m_vec(capacity) + , m_front(m_vec.begin()) + , m_back(m_vec.begin()) + , m_size(0) + {} + bool empty() const + { + return m_size == 0; + } + const T& front() const + { + return *m_front; + } + void pop() + { + assert(m_size > 0); + ++m_front; + if(m_front == m_vec.end()) + m_front = m_vec.begin(); + --m_size; + } + void push(const T& t) + { + assert(m_size < m_vec.size()); + *m_back = t; + ++m_back; + if(m_back == m_vec.end()) + m_back = m_vec.begin(); + ++m_size; + } +#ifdef CDT_CXX11_IS_SUPPORTED + void push(const T&& t) + { + assert(m_size < m_vec.size()); + *m_back = t; + ++m_back; + if(m_back == m_vec.end()) + m_back = m_vec.begin(); + ++m_size; + } +#endif +private: + std::vector<T> m_vec; + typename std::vector<T>::iterator m_front; + typename std::vector<T>::iterator m_back; + std::size_t m_size; +}; + +template <typename T> +struct less_than_x +{ + less_than_x(const std::vector<V2d<T> >& vertices) + : vertices(vertices) + {} + bool operator()(const VertInd a, const VertInd b) const + { + return vertices[a].x < vertices[b].x; + } + const std::vector<V2d<T> >& vertices; +}; + +template <typename T> +struct less_than_y +{ + less_than_y(const std::vector<V2d<T> >& vertices) + : vertices(vertices) + {} + bool operator()(const VertInd a, const VertInd b) const + { + return vertices[a].y < vertices[b].y; + } + const std::vector<V2d<T> >& vertices; +}; + +} // namespace detail + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::insertVertices_KDTreeBFS( + VertInd superGeomVertCount, + V2d<T> boxMin, + V2d<T> boxMax) +{ + // calculate original indices + const VertInd vertexCount = vertices.size() - superGeomVertCount; + if(vertexCount <= 0) + return; + std::vector<VertInd> ii(vertexCount); + detail::iota(ii.begin(), ii.end(), superGeomVertCount); + + typedef std::vector<VertInd>::iterator It; + detail::FixedCapacityQueue<tuple<It, It, V2d<T>, V2d<T>, VertInd> > queue( + detail::maxQueueLengthBFSKDTree(vertexCount)); + queue.push(make_tuple(ii.begin(), ii.end(), boxMin, boxMax, VertInd(0))); + + It first, last; + V2d<T> newBoxMin, newBoxMax; + VertInd parent, mid; + + const detail::less_than_x<T> cmpX(vertices); + const detail::less_than_y<T> cmpY(vertices); + + while(!queue.empty()) + { + tie(first, last, boxMin, boxMax, parent) = queue.front(); + queue.pop(); + assert(first != last); + + const std::ptrdiff_t len = std::distance(first, last); + if(len == 1) + { + insertVertex(*first, parent); + continue; + } + const It midIt = first + len / 2; + if(boxMax.x - boxMin.x >= boxMax.y - boxMin.y) + { + detail::portable_nth_element(first, midIt, last, cmpX); + mid = *midIt; + const T split = vertices[mid].x; + newBoxMin.x = split; + newBoxMin.y = boxMin.y; + newBoxMax.x = split; + newBoxMax.y = boxMax.y; + } + else + { + detail::portable_nth_element(first, midIt, last, cmpY); + mid = *midIt; + const T split = vertices[mid].y; + newBoxMin.x = boxMin.x; + newBoxMin.y = split; + newBoxMax.x = boxMax.x; + newBoxMax.y = split; + } + insertVertex(mid, parent); + if(first != midIt) + { + queue.push(make_tuple(first, midIt, boxMin, newBoxMax, mid)); + } + if(midIt + 1 != last) + { + queue.push(make_tuple(midIt + 1, last, newBoxMin, boxMax, mid)); + } + } +} + +template <typename T, typename TNearPointLocator> +std::pair<TriInd, TriInd> Triangulation<T, TNearPointLocator>::edgeTriangles( + const VertInd a, + const VertInd b) const +{ + const TriInd triStart = m_vertTris[a]; + assert(triStart != noNeighbor); + TriInd iT = triStart, iTNext = triStart; + VertInd iV = noVertex; + do + { + const Triangle& t = triangles[iT]; + tie(iTNext, iV) = t.next(a); + assert(iTNext != noNeighbor); + if(iV == b) + { + return std::make_pair(iT, iTNext); + } + iT = iTNext; + } while(iT != triStart); + return std::make_pair(invalidIndex, invalidIndex); +} + +template <typename T, typename TNearPointLocator> +bool Triangulation<T, TNearPointLocator>::hasEdge( + const VertInd a, + const VertInd b) const +{ + return edgeTriangles(a, b).first != invalidIndex; +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::setAdjacentTriangle( + const VertInd v, + const TriInd t) +{ + assert(t != noNeighbor); + m_vertTris[v] = t; + assert( + triangles[t].vertices[0] == v || triangles[t].vertices[1] == v || + triangles[t].vertices[2] == v); +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::pivotVertexTriangleCW(const VertInd v) +{ + assert(m_vertTris[v] != noNeighbor); + m_vertTris[v] = triangles[m_vertTris[v]].next(v).first; + assert(m_vertTris[v] != noNeighbor); + assert( + triangles[m_vertTris[v]].vertices[0] == v || + triangles[m_vertTris[v]].vertices[1] == v || + triangles[m_vertTris[v]].vertices[2] == v); +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::tryAddVertexToLocator(const VertInd v) +{ + if(!m_nearPtLocator.empty()) // only if locator is initialized already + m_nearPtLocator.addPoint(v, vertices); +} + +template <typename T, typename TNearPointLocator> +void Triangulation<T, TNearPointLocator>::tryInitNearestPointLocator() +{ + if(!vertices.empty() && m_nearPtLocator.empty()) + { + m_nearPtLocator.initialize(vertices); + } +} + +} // namespace CDT
cpp/hcdt.cpp view
@@ -1,133 +1,133 @@-#include <cstdlib> // to use malloc-#include <stdio.h> // to use printf-#include "hcdt.hpp"-#include "CDT.h"--extern "C"-{--TriangulationT* delaunay(VertexT* points, size_t npoints){- CDT::Triangulation<double> cdt(CDT::VertexInsertionOrder::AsProvided);- // insert vertices- std::vector<CDT::V2d<double>> vertices(npoints);- for(size_t k = 0; k < npoints; ++k) {- const VertexT v = points[k];- vertices[k] = CDT::V2d<double>::make(v.x, v.y);- }- cdt.insertVertices(vertices);- cdt.eraseSuperTriangle();- //// output- // triangles- const CDT::TriangleVec triangles = cdt.triangles;- const size_t ntriangles = triangles.size();- TriangleT* out_triangles = (TriangleT*)malloc(ntriangles * sizeof(TriangleT));- for(size_t k = 0; k < ntriangles; ++k){- const CDT::VerticesArr3 trgl = triangles[k].vertices;- out_triangles[k].i1 = trgl[0];- out_triangles[k].i2 = trgl[1];- out_triangles[k].i3 = trgl[2];- }- // all edges- CDT::EdgeUSet allEdges = CDT::extractEdgesFromTriangles(triangles);- const size_t nedges = allEdges.size();- EdgeT* out_alledges = (EdgeT*)malloc(nedges * sizeof(EdgeT));- std::unordered_set<CDT::Edge> :: iterator it;- size_t k = 0;- for(it = allEdges.begin(); it != allEdges.end(); it++){- const CDT::Edge edge = *it;- out_alledges[k].i = CDT::edge_get_v1(edge);- out_alledges[k].j = CDT::edge_get_v2(edge);- k++;- }- /* output mesh */- TriangulationT* out = (TriangulationT*)malloc(sizeof(TriangulationT));- out->vertices = points;- out->nvertices = npoints;- out->triangles = out_triangles;- out->ntriangles = ntriangles;- out->edges = out_alledges;- out->nedges = nedges;- return out;-}--CTriangulationT* cdelaunay(VertexT* points, size_t npoints, EdgeT* fedges, size_t nfedges){- CDT::Triangulation<double> cdt(- CDT::VertexInsertionOrder::AsProvided,- CDT::IntersectingConstraintEdges::Resolve,- 0.0- );- // insert vertices- std::vector<CDT::V2d<double>> vertices(npoints);- for(size_t k = 0; k < npoints; ++k) {- const VertexT v = points[k];- vertices[k] = CDT::V2d<double>::make(v.x, v.y);- }- // insert edges- std::vector<CDT::Edge> Edges;- Edges.reserve(nfedges);- for (size_t k = 0; k < nfedges; ++k) {- const EdgeT e = fedges[k];- Edges.push_back(CDT::Edge(e.i, e.j));- }- cdt.insertVertices(vertices);- cdt.insertEdges(Edges);- cdt.eraseOuterTrianglesAndHoles();- //// output- // vertices- const std::vector<CDT::V2d<double>> cdt_vertices = cdt.vertices;- const size_t nvertices = cdt_vertices.size();- VertexT* out_vertices = (VertexT*)malloc(nvertices * sizeof(VertexT));- for(size_t k = 0; k < nvertices; ++k){- const CDT::V2d<double> v = cdt_vertices[k];- out_vertices[k].x = v.x;- out_vertices[k].y = v.y;- }- // triangles- const CDT::TriangleVec triangles = cdt.triangles;- const size_t ntriangles = triangles.size();- //printf("ntriangles: %u\n", ntriangles);- TriangleT* out_triangles = (TriangleT*)malloc(ntriangles * sizeof(TriangleT));- for(size_t k = 0; k < ntriangles; ++k){- const CDT::VerticesArr3 trgl = triangles[k].vertices;- out_triangles[k].i1 = trgl[0];- out_triangles[k].i2 = trgl[1];- out_triangles[k].i3 = trgl[2];- }- // all edges- CDT::EdgeUSet allEdges = CDT::extractEdgesFromTriangles(triangles);- const size_t nedges = allEdges.size();- EdgeT* out_alledges = (EdgeT*)malloc(nedges * sizeof(EdgeT));- std::unordered_set<CDT::Edge> :: iterator it;- size_t k = 0;- for(it = allEdges.begin(); it != allEdges.end(); it++){- const CDT::Edge edge = *it;- out_alledges[k].i = CDT::edge_get_v1(edge);- out_alledges[k].j = CDT::edge_get_v2(edge);- k++;- }- // fixed edges- CDT::EdgeUSet fixedEdges = cdt.fixedEdges;- const size_t nfixededges = fixedEdges.size();- EdgeT* out_fixededges = (EdgeT*)malloc(nfixededges * sizeof(EdgeT));- std::unordered_set<CDT::Edge> :: iterator itedge;- size_t l = 0;- for(itedge = fixedEdges.begin(); itedge != fixedEdges.end(); itedge++){- const CDT::Edge edge = *itedge;- out_fixededges[l].i = CDT::edge_get_v1(edge);- out_fixededges[l].j = CDT::edge_get_v2(edge);- l++;- }- /* output mesh */- CTriangulationT* out = (CTriangulationT*)malloc(sizeof(CTriangulationT));- out->vertices = out_vertices;- out->nvertices = nvertices;- out->triangles = out_triangles;- out->ntriangles = ntriangles;- out->edges = out_alledges;- out->nedges = nedges;- out->fixededges = out_fixededges;- out->nfixededges = nfixededges;- return out;-}-+#include <cstdlib> // to use malloc +#include <stdio.h> // to use printf +#include "hcdt.hpp" +#include "CDT.h" + +extern "C" +{ + +TriangulationT* delaunay(VertexT* points, size_t npoints){ + CDT::Triangulation<double> cdt(CDT::VertexInsertionOrder::AsProvided); + // insert vertices + std::vector<CDT::V2d<double>> vertices(npoints); + for(size_t k = 0; k < npoints; ++k) { + const VertexT v = points[k]; + vertices[k] = CDT::V2d<double>::make(v.x, v.y); + } + cdt.insertVertices(vertices); + cdt.eraseSuperTriangle(); + //// output + // triangles + const CDT::TriangleVec triangles = cdt.triangles; + const size_t ntriangles = triangles.size(); + TriangleT* out_triangles = (TriangleT*)malloc(ntriangles * sizeof(TriangleT)); + for(size_t k = 0; k < ntriangles; ++k){ + const CDT::VerticesArr3 trgl = triangles[k].vertices; + out_triangles[k].i1 = trgl[0]; + out_triangles[k].i2 = trgl[1]; + out_triangles[k].i3 = trgl[2]; + } + // all edges + CDT::EdgeUSet allEdges = CDT::extractEdgesFromTriangles(triangles); + const size_t nedges = allEdges.size(); + EdgeT* out_alledges = (EdgeT*)malloc(nedges * sizeof(EdgeT)); + std::unordered_set<CDT::Edge> :: iterator it; + size_t k = 0; + for(it = allEdges.begin(); it != allEdges.end(); it++){ + const CDT::Edge edge = *it; + out_alledges[k].i = CDT::edge_get_v1(edge); + out_alledges[k].j = CDT::edge_get_v2(edge); + k++; + } + /* output mesh */ + TriangulationT* out = (TriangulationT*)malloc(sizeof(TriangulationT)); + out->vertices = points; + out->nvertices = npoints; + out->triangles = out_triangles; + out->ntriangles = ntriangles; + out->edges = out_alledges; + out->nedges = nedges; + return out; +} + +CTriangulationT* cdelaunay(VertexT* points, size_t npoints, EdgeT* fedges, size_t nfedges){ + CDT::Triangulation<double> cdt( + CDT::VertexInsertionOrder::AsProvided, + CDT::IntersectingConstraintEdges::Resolve, + 0.0 + ); + // insert vertices + std::vector<CDT::V2d<double>> vertices(npoints); + for(size_t k = 0; k < npoints; ++k) { + const VertexT v = points[k]; + vertices[k] = CDT::V2d<double>::make(v.x, v.y); + } + // insert edges + std::vector<CDT::Edge> Edges; + Edges.reserve(nfedges); + for (size_t k = 0; k < nfedges; ++k) { + const EdgeT e = fedges[k]; + Edges.push_back(CDT::Edge(e.i, e.j)); + } + cdt.insertVertices(vertices); + cdt.insertEdges(Edges); + cdt.eraseOuterTrianglesAndHoles(); + //// output + // vertices + const std::vector<CDT::V2d<double>> cdt_vertices = cdt.vertices; + const size_t nvertices = cdt_vertices.size(); + VertexT* out_vertices = (VertexT*)malloc(nvertices * sizeof(VertexT)); + for(size_t k = 0; k < nvertices; ++k){ + const CDT::V2d<double> v = cdt_vertices[k]; + out_vertices[k].x = v.x; + out_vertices[k].y = v.y; + } + // triangles + const CDT::TriangleVec triangles = cdt.triangles; + const size_t ntriangles = triangles.size(); + //printf("ntriangles: %u\n", ntriangles); + TriangleT* out_triangles = (TriangleT*)malloc(ntriangles * sizeof(TriangleT)); + for(size_t k = 0; k < ntriangles; ++k){ + const CDT::VerticesArr3 trgl = triangles[k].vertices; + out_triangles[k].i1 = trgl[0]; + out_triangles[k].i2 = trgl[1]; + out_triangles[k].i3 = trgl[2]; + } + // all edges + CDT::EdgeUSet allEdges = CDT::extractEdgesFromTriangles(triangles); + const size_t nedges = allEdges.size(); + EdgeT* out_alledges = (EdgeT*)malloc(nedges * sizeof(EdgeT)); + std::unordered_set<CDT::Edge> :: iterator it; + size_t k = 0; + for(it = allEdges.begin(); it != allEdges.end(); it++){ + const CDT::Edge edge = *it; + out_alledges[k].i = CDT::edge_get_v1(edge); + out_alledges[k].j = CDT::edge_get_v2(edge); + k++; + } + // fixed edges + CDT::EdgeUSet fixedEdges = cdt.fixedEdges; + const size_t nfixededges = fixedEdges.size(); + EdgeT* out_fixededges = (EdgeT*)malloc(nfixededges * sizeof(EdgeT)); + std::unordered_set<CDT::Edge> :: iterator itedge; + size_t l = 0; + for(itedge = fixedEdges.begin(); itedge != fixedEdges.end(); itedge++){ + const CDT::Edge edge = *itedge; + out_fixededges[l].i = CDT::edge_get_v1(edge); + out_fixededges[l].j = CDT::edge_get_v2(edge); + l++; + } + /* output mesh */ + CTriangulationT* out = (CTriangulationT*)malloc(sizeof(CTriangulationT)); + out->vertices = out_vertices; + out->nvertices = nvertices; + out->triangles = out_triangles; + out->ntriangles = ntriangles; + out->edges = out_alledges; + out->nedges = nedges; + out->fixededges = out_fixededges; + out->nfixededges = nfixededges; + return out; +} + }
cpp/hcdt.hpp view
@@ -1,35 +1,35 @@-typedef struct Vertex {- double x;- double y;-} VertexT;--typedef struct Edge {- unsigned i;- unsigned j;-} EdgeT;--typedef struct Triangle {- unsigned i1;- unsigned i2;- unsigned i3;-} TriangleT;--typedef struct Triangulation {- VertexT* vertices;- size_t nvertices;- TriangleT* triangles;- size_t ntriangles;- EdgeT* edges;- size_t nedges;-} TriangulationT;--typedef struct CTriangulation {- VertexT* vertices;- size_t nvertices;- TriangleT* triangles;- size_t ntriangles;- EdgeT* edges;- size_t nedges;- EdgeT* fixededges;- size_t nfixededges;+typedef struct Vertex { + double x; + double y; +} VertexT; + +typedef struct Edge { + unsigned i; + unsigned j; +} EdgeT; + +typedef struct Triangle { + unsigned i1; + unsigned i2; + unsigned i3; +} TriangleT; + +typedef struct Triangulation { + VertexT* vertices; + size_t nvertices; + TriangleT* triangles; + size_t ntriangles; + EdgeT* edges; + size_t nedges; +} TriangulationT; + +typedef struct CTriangulation { + VertexT* vertices; + size_t nvertices; + TriangleT* triangles; + size_t ntriangles; + EdgeT* edges; + size_t nedges; + EdgeT* fixededges; + size_t nfixededges; } CTriangulationT;
cpp/predicates.h view
@@ -1,939 +1,915 @@-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *- * *- * Copyright (c) 2019, William C. Lenthe *- * All rights reserved. *- * *- * Redistribution and use in source and binary forms, with or without *- * modification, are permitted provided that the following conditions are met: *- * *- * 1. Redistributions of source code must retain the above copyright notice, this *- * list of conditions and the following disclaimer. *- * *- * 2. Redistributions in binary form must reproduce the above copyright notice, *- * this list of conditions and the following disclaimer in the documentation *- * and/or other materials provided with the distribution. *- * *- * 3. Neither the name of the copyright holder nor the names of its *- * contributors may be used to endorse or promote products derived from *- * this software without specific prior written permission. *- * *- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" *- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE *- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE *- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL *- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER *- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE *- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *- * *- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */--#ifndef PREDICATES_H_INCLUDED-#define PREDICATES_H_INCLUDED--//@reference: https://www.cs.cmu.edu/~quake/robust.html--namespace predicates {- //@brief: geometric predicates using arbitrary precision arithmetic - //@note : these are provided primarily for illustrative purposes and adaptive routines should be preferred- namespace exact {- //@brief : determine if the 2d point c is above, on, or below the line defined by a and b- //@param ax: X-coordinate of a- //@param ay: Y-coordinate of a- //@param bx: X-coordinate of b- //@param by: Y-coordinate of b- //@param cx: X-coordinate of c- //@param cy: Y-coordinate of c- //@return : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}}- //@note : positive, 0, negative result for c above, on, or below the line defined by a -> b- template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy);-- //@brief : determine if the 2d point c is above, on, or below the line defined by a and b- //@param pa: pointer to a as {x, y}- //@param pb: pointer to b as {x, y}- //@param pc: pointer to c as {x, y}- //@return : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}}- //@note : positive, 0, negative result for c above, on, or below the line defined by a -> b- template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc);-- //@brief : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c- //@param ax: X-coordinate of a- //@param ay: Y-coordinate of a- //@param bx: X-coordinate of b- //@param by: Y-coordinate of b- //@param cx: X-coordinate of c- //@param cy: Y-coordinate of c- //@param dx: X-coordinate of d- //@param dy: Y-coordinate of d- //@return : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}}- //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c- template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy);-- //@brief : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c- //@param pa: pointer to a as {x, y}- //@param pb: pointer to b as {x, y}- //@param pc: pointer to c as {x, y}- //@param pc: pointer to d as {x, y}- //@return : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}}- //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c- template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd);-- //@brief : determine if the 3d point d is above, on, or below the plane defined by a, b, and c- //@param pa: pointer to a as {x, y, z}- //@param pb: pointer to b as {x, y, z}- //@param pc: pointer to c as {x, y, z}- //@param pd: pointer to d as {x, y, z}- //@return : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}}- //@note : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c- template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd);- - //@brief : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d- //@param pa: pointer to a as {x, y, z}- //@param pb: pointer to b as {x, y, z}- //@param pc: pointer to c as {x, y, z}- //@param pd: pointer to d as {x, y, z}- //@param pe: pointer to e as {x, y, z}- //@return : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}}- //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c- template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe);- }-- //@brief: geometric predicates using normal floating point arithmetic but falling back to arbitrary precision when needed- //@note : these should have the same accuracy but are significantly faster when determinants are large- namespace adaptive {- //@brief : determine if the 2d point c is above, on, or below the line defined by a and b- //@param ax: X-coordinate of a- //@param ay: Y-coordinate of a- //@param bx: X-coordinate of b- //@param by: Y-coordinate of b- //@param cx: X-coordinate of c- //@param cy: Y-coordinate of c- //@return : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}}- //@note : positive, 0, negative result for c above, on, or below the line defined by a -> b- template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy);-- //@brief : determine if the 2d point c is above, on, or below the line defined by a and b- //@param pa: pointer to a as {x, y}- //@param pb: pointer to b as {x, y}- //@param pc: pointer to c as {x, y}- //@return : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}}- //@note : positive, 0, negative result for c above, on, or below the line defined by a -> b- template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc);-- //@brief : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c- //@param ax: X-coordinate of a- //@param ay: Y-coordinate of a- //@param bx: X-coordinate of b- //@param by: Y-coordinate of b- //@param cx: X-coordinate of c- //@param cy: Y-coordinate of c- //@param dx: X-coordinate of d- //@param dy: Y-coordinate of d- //@return : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}}- //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c- template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy);-- //@brief : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c- //@param pa: pointer to a as {x, y}- //@param pb: pointer to b as {x, y}- //@param pc: pointer to c as {x, y}- //@param pc: pointer to d as {x, y}- //@return : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}}- //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c- template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd);-- //@brief : determine if the 3d point d is above, on, or below the plane defined by a, b, and c- //@param pa: pointer to a as {x, y, z}- //@param pb: pointer to b as {x, y, z}- //@param pc: pointer to c as {x, y, z}- //@param pd: pointer to d as {x, y, z}- //@return : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}}- //@note : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c- template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd);-- //@brief : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d- //@param pa: pointer to a as {x, y, z}- //@param pb: pointer to b as {x, y, z}- //@param pc: pointer to c as {x, y, z}- //@param pd: pointer to d as {x, y, z}- //@param pe: pointer to e as {x, y, z}- //@return : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}}- //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c- template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe);- }-}--#include <cmath>//abs, fma-#include <limits>-#include <utility>//pair-#include <numeric>//accumulate-#include <algorithm>//transform, copy_n, merge-#include <functional>//negate--// a macro based static assert for pre c++11-#define PREDICATES_PORTABLE_STATIC_ASSERT(condition, message) typedef char message[(condition) ? 1 : -1]--// check if c++11 is supported-#if !defined(__cplusplus) && !defined(_MSC_VER)- PREDICATES_PORTABLE_STATIC_ASSERT(false, couldnt_parse_cxx_standard)-#endif-#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)- #define PREDICATES_CXX11_IS_SUPPORTED-#endif--// choose to use c++11 features or their backports-#ifdef PREDICATES_CXX11_IS_SUPPORTED-#include <array>-#include <type_traits>// is_same, enable_if-#undef PREDICATES_PORTABLE_STATIC_ASSERT-#define PREDICATES_TOKEN_TO_STRING1(x) #x-#define PREDICATES_TOKEN_TO_STRING(x) PREDICATES_TOKEN_TO_STRING1(x)-#define PREDICATES_PORTABLE_STATIC_ASSERT(condition, message) static_assert(condition, PREDICATES_TOKEN_TO_STRING(message))-namespace predicates {-namespace stdx {- using std::array;- using std::copy_n;-}-#else-namespace predicates {-namespace stdx {- // array- template<typename T, size_t N>- class array {- T buff[N];- public:- T& operator[](const size_t& i) { return buff[i]; }- const T& operator[](const size_t& i) const { return buff[i]; }-- T * data() { return buff; }- T const * data() const { return buff; }-- T * begin() { return buff; }- T const * cbegin() const { return buff; }- };- // copy_n- template< class InputIt, class Size, class OutputIt>- OutputIt copy_n(InputIt first, Size count, OutputIt result)- {- if (count > 0) {- *result++ = *first;- for (Size i = 1; i < count; ++i) {- *result++ = *++first;- }- }- return result;- }-}-#endif // PREDICATES_CXX11_IS_SUPPORTED--namespace detail {- template<typename T> class ExpansionBase;-- //@brief: class to exactly represent the result of a sequence of arithmetic operations as an sequence of values that sum to the result- template<typename T, size_t N>- class Expansion : private ExpansionBase<T>, public stdx::array<T, N> {- private:- public:- size_t m_size;- template <typename S> friend class ExpansionBase;//access for base class- template <typename S, size_t M> friend class Expansion;//access for expansions of different size-- Expansion() : m_size(0) {}- template <size_t M> Expansion& operator=(const Expansion<T, M>& e) {- PREDICATES_PORTABLE_STATIC_ASSERT(M <= N, cannot_assign_a_larger_expansion_to_a_smaller_expansion);- stdx::copy_n(e.cbegin(), e.size(), stdx::array<T, N>::begin());- m_size = e.size();- return *this;- }-- //vector like convenience functions- size_t size() const {return m_size;}- bool empty() const {return 0 == m_size;}- void push_back(const T v) {stdx::array<T, N>::operator[](m_size++) = v;}-- public:- //estimates of expansion value- T mostSignificant() const {return empty() ? T(0) : stdx::array<T, N>::operator[](m_size - 1);}- T estimate() const {return std::accumulate(stdx::array<T, N>::cbegin(), stdx::array<T, N>::cbegin() + size(), T(0));}-- template <size_t M> Expansion<T, N+M> operator+(const Expansion<T, M>& f) const {- Expansion<T, N+M> h;- h.m_size = ExpansionBase<T>::ExpansionSum(this->data(), this->size(), f.data(), f.size(), h.data());- return h;- }-- void negate() {std::transform(stdx::array<T, N>::cbegin(), stdx::array<T, N>::cbegin() + size(), stdx::array<T, N>::begin(), std::negate<T>());}- Expansion operator-() const {Expansion e = *this; e.negate(); return e;}- template <size_t M> Expansion<T, N+M> operator-(const Expansion<T, M>& f) const {return operator+(-f);}-- Expansion<T, 2*N> operator*(const T b) const {- Expansion<T, 2*N> h;- h.m_size = ExpansionBase<T>::ScaleExpansion(this->data(), this->size(), b, h.data());- return h;- }- };-- //std::fma is faster than dekker's product when the processor instruction is available- #ifdef FP_FAST_FMAF- static const bool fp_fast_fmaf = true;- #else- static const bool fp_fast_fmaf = false;- #endif-- #ifdef FP_FAST_FMA- static const bool fp_fast_fma = true;- #else- static const bool fp_fast_fma = false;- #endif-- #ifdef FP_FAST_FMAL- static const bool fp_fast_fmal = true;- #else- static const bool fp_fast_fmal = false;- #endif-- #ifdef PREDICATES_CXX11_IS_SUPPORTED- template <typename T> struct use_fma {static const bool value = (std::is_same<T, float>::value && fp_fast_fmaf) ||- (std::is_same<T, double>::value && fp_fast_fma) ||- (std::is_same<T, long double>::value && fp_fast_fmal);};- #endif-- //@brief : helper function to sort by absolute value- //@param a: lhs item to compare- //@param b: rhs item to compare- //@return : true if |a| < |b|- //@note : defined since lambda functions aren't allow in c++03- template <typename T> bool absLess(const T& a, const T& b) {return std::abs(a) < std::abs(b);}-- template<typename T>- class ExpansionBase {- private:- static const T Splitter;-- PREDICATES_PORTABLE_STATIC_ASSERT(std::numeric_limits<T>::is_iec559, Requires_IEC_559_IEEE_754_floating_point_type);- PREDICATES_PORTABLE_STATIC_ASSERT(2 == std::numeric_limits<T>::radix, Requires_base_2_floating_point_type);-- //combine result + roundoff error into expansion- static inline Expansion<T, 2> MakeExpansion(const T value, const T tail) {- Expansion<T, 2> e;- if(T(0) != tail) e.push_back(tail);- if(T(0) != value) e.push_back(value);- return e;- }-- protected:- //add 2 expansions- static size_t ExpansionSum(T const * const e, const size_t n, T const * const f, const size_t m, T * const h) {- std::merge(e, e + n, f, f + m, h, absLess<T>);- if(m == 0) return n;- if(n == 0) return m;- size_t hIndex = 0;- T Q = h[0];- T Qnew = h[1] + Q;- T hh = FastPlusTail(h[1], Q, Qnew);- Q = Qnew;- if(T(0) != hh) h[hIndex++] = hh;- for(size_t g = 2; g != n + m; ++g) {- Qnew = Q + h[g];- hh = PlusTail(Q, h[g], Qnew);- Q = Qnew;- if(T(0) != hh) h[hIndex++] = hh;- }- if(T(0) != Q) h[hIndex++] = Q;- return hIndex;- }-- //scale an expansion by a constant- static size_t ScaleExpansion(T const * const e, const size_t n, const T b, T * const h) {- if(n == 0 || T(0) == b) return 0;- size_t hIndex = 0;- T Q = e[0] * b;- const std::pair<T, T> bSplit = Split(b);- T hh = MultTailPreSplit(e[0], b, bSplit, Q);- if(T(0) != hh) h[hIndex++] = hh;- for(size_t eIndex = 1; eIndex < n; ++eIndex) {- T Ti = e[eIndex] * b;- T ti = MultTailPreSplit(e[eIndex], b, bSplit, Ti);- T Qi = Q + ti;- hh = PlusTail(Q, ti, Qi);- if(T(0) != hh) h[hIndex++] = hh;- Q = Ti + Qi;- hh = FastPlusTail(Ti, Qi, Q);- if(T(0) != hh) h[hIndex++] = hh;- }- if(T(0) != Q) h[hIndex++] = Q;- return hIndex;- }- - public:- //roundoff error of x = a + b- static inline T PlusTail(const T a, const T b, const T x) {- const T bVirtual = x - a;- const T aVirtual = x - bVirtual;- const T bRoundoff = b - bVirtual;- const T aRoundoff = a - aVirtual;- return aRoundoff + bRoundoff;- }-- //roundoff error of x = a + b if |a| > |b|- static inline T FastPlusTail(const T a, const T b, const T x) {- const T bVirtual = x - a;- return b - bVirtual;- }-- //roundoff error of x = a - b- static inline T MinusTail(const T a, const T b, const T x) {- const T bVirtual = a - x;- const T aVirtual = x + bVirtual;- const T bRoundoff = bVirtual - b;- const T aRoundoff = a - aVirtual;- return aRoundoff + bRoundoff;- }-- //split a into 2 nonoverlapping values- static inline std::pair<T, T> Split(const T a) {- const T c = a * Splitter;- const T aBig = c - a;- const T aHi = c - aBig;- return std::pair<T, T>(aHi, a - aHi);- }-- //roundoff error of x = a * b via dekkers product- static inline T DekkersProduct(const T /*a*/, const std::pair<T, T> aSplit, const T /*b*/, const std::pair<T, T> bSplit, const T p) {- T y = p - T(aSplit.first * bSplit.first);- y -= T(aSplit.second * bSplit.first);- y -= T(aSplit.first * bSplit.second);- return T(aSplit.second * bSplit.second) - y;- }-- //roundoff error of x = a * b-#ifdef PREDICATES_CXX11_IS_SUPPORTED- template <typename S = T> static typename std::enable_if< use_fma<S>::value, S>::type MultTail(const T a, const T b, const T p) {return std::fma(a, b, -p);}- template <typename S = T> static typename std::enable_if<!use_fma<S>::value, S>::type MultTail(const T a, const T b, const T p) {return DekkersProduct(a, Split(a), b, Split(b), p);}-- template <typename S = T> static typename std::enable_if< use_fma<S>::value, S>::type MultTailPreSplit(const T a, const T b, const std::pair<T, T> /*bSplit*/, const T p) {return std::fma(a, b, -p);}- template <typename S = T> static typename std::enable_if<!use_fma<S>::value, S>::type MultTailPreSplit(const T a, const T b, const std::pair<T, T> bSplit, const T p) {return DekkersProduct(a, Split(a), b, bSplit, p);}-#else- static T MultTail(const T a, const T b, const T p) {return DekkersProduct(a, Split(a), b, Split(b), p);}- static T MultTailPreSplit(const T a, const T b, const std::pair<T, T> bSplit, const T p) {return DekkersProduct(a, Split(a), b, bSplit, p);}-#endif- //expand a + b- static inline Expansion<T, 2> Plus(const T a, const T b) {- const T x = a + b;- return MakeExpansion(x, PlusTail(a, b, x));- }-- //expand a - b- static inline Expansion<T, 2> Minus(const T a, const T b) {return Plus(a, -b);}-- //expand a * b- static inline Expansion<T, 2> Mult(const T a, const T b) {- const T x = a * b;- return MakeExpansion(x, MultTail(a, b, x));- }-- //expand the determinant of {{ax, ay}, {bx, by}} (unrolled Mult(ax, by) - Mult(ay, bx))- static inline Expansion<T, 4> TwoTwoDiff(const T ax, const T by, const T ay, const T bx) {- const T axby1 = ax * by;- const T axby0 = MultTail(ax, by, axby1);- const T bxay1 = bx * ay;- const T bxay0 = MultTail(bx, ay, bxay1);- const T _i0 = axby0 - bxay0;- const T x0 = MinusTail(axby0, bxay0, _i0);- const T _j = axby1 + _i0;- const T _0 = PlusTail(axby1, _i0, _j);- const T _i1 = _0 - bxay1;- const T x1 = MinusTail(_0, bxay1, _i1);- const T x3 = _j + _i1;- const T x2 = PlusTail(_j, _i1, x3);- Expansion<T, 4> e;- if(T(0) != x0) e.push_back(x0);- if(T(0) != x1) e.push_back(x1);- if(T(0) != x2) e.push_back(x2);- if(T(0) != x3) e.push_back(x3);- return e;- }-- //TwoTwoDiff checking for zeros to avoid extra splitting- static inline Expansion<T, 4> TwoTwoDiffZeroCheck(const T ax, const T by, const T ay, const T bx) {- Expansion<T, 4> e;- if(T(0) == ax && T(0) == ay) return e;- else if(T(0) == ax) e = Mult(ay, bx);- else if(T(0) == ay) e = Mult(ax, by);- else e = TwoTwoDiff(ax, by, ay, bx);- return e;- }-- //(a * b) * c checking for zeros- static inline Expansion<T, 4> ThreeProd(const T a, const T b, const T c) {return (T(0) == a || T(0) == b || T(0) == c) ? Expansion<T, 4>() : Mult(a, b) * c;}- };-- template <typename T> const T ExpansionBase<T>::Splitter = static_cast<T>(-#ifdef PREDICATES_CXX11_IS_SUPPORTED- std::exp2((std::numeric_limits<T>::digits + std::numeric_limits<T>::digits%2)/2 + 1)-#else- std::ldexp(T(1), (std::numeric_limits<T>::digits + std::numeric_limits<T>::digits%2)/2 + 1)-#endif- );-}-- namespace exact {- template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy)- {- const detail::Expansion<T, 4> aterms = detail::ExpansionBase<T>::TwoTwoDiff(ax, by, ax, cy);- const detail::Expansion<T, 4> bterms = detail::ExpansionBase<T>::TwoTwoDiff(bx, cy, bx, ay);- const detail::Expansion<T, 4> cterms = detail::ExpansionBase<T>::TwoTwoDiff(cx, ay, cx, by);- const detail::Expansion<T, 12> w = aterms + bterms + cterms;- return w.mostSignificant();- }-- template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc) {- return orient2d(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1]);- }-- template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy) {- const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(ax, by, bx, ay);- const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bx, cy, cx, by);- const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(cx, dy, dx, cy);- const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(dx, ay, ax, dy);- const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(ax, cy, cx, ay);- const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(bx, dy, dx, by);-- const detail::Expansion<T, 12> abc = ab + bc - ac;- const detail::Expansion<T, 12> bcd = bc + cd - bd;- const detail::Expansion<T, 12> cda = cd + da + ac;- const detail::Expansion<T, 12> dab = da + ab + bd;-- const detail::Expansion<T, 96> adet = bcd * ax * ax + bcd * ay * ay;- const detail::Expansion<T, 96> bdet = cda * bx * -bx + cda * by * -by;- const detail::Expansion<T, 96> cdet = dab * cx * cx + dab * cy * cy;- const detail::Expansion<T, 96> ddet = abc * dx * -dx + abc * dy * -dy;-- const detail::Expansion<T, 384> deter = (adet + bdet) + (cdet + ddet);- return deter.mostSignificant();- }-- template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd) {- return incircle(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1], pd[0], pd[1]);- }-- //@brief : determine if the 3d point d is above, on, or below the plane defined by a, b, and c- //@param pa: pointer to a as {x, y, z}- //@param pb: pointer to b as {x, y, z}- //@param pc: pointer to c as {x, y, z}- //@param pd: pointer to d as {x, y, z}- //@return : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}}- //@note : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c- template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd) {- const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pb[1], pb[0], pa[1]);- const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pc[1], pc[0], pb[1]);- const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(pc[0], pd[1], pd[0], pc[1]);- const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(pd[0], pa[1], pa[0], pd[1]);- const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pc[1], pc[0], pa[1]);- const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pd[1], pd[0], pb[1]);-- const detail::Expansion<T, 12> abc = ab + bc - ac;- const detail::Expansion<T, 12> bcd = bc + cd - bd;- const detail::Expansion<T, 12> cda = cd + da + ac;- const detail::Expansion<T, 12> dab = da + ab + bd;-- const detail::Expansion<T, 24> adet = bcd * pa[2];- const detail::Expansion<T, 24> bdet = cda * -pb[2];- const detail::Expansion<T, 24> cdet = dab * pc[2];- const detail::Expansion<T, 24> ddet = abc * -pd[2];-- const detail::Expansion<T, 96> deter = (adet + bdet) + (cdet + ddet);- return deter.mostSignificant();- }-- //@brief : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d- //@param pa: pointer to a as {x, y, z}- //@param pb: pointer to b as {x, y, z}- //@param pc: pointer to c as {x, y, z}- //@param pd: pointer to d as {x, y, z}- //@param pe: pointer to e as {x, y, z}- //@return : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}}- //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c- template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe) {- const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pb[1], pb[0], pa[1]);- const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pc[1], pc[0], pb[1]);- const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(pc[0], pd[1], pd[0], pc[1]);- const detail::Expansion<T, 4> de = detail::ExpansionBase<T>::TwoTwoDiff(pd[0], pe[1], pe[0], pd[1]);- const detail::Expansion<T, 4> ea = detail::ExpansionBase<T>::TwoTwoDiff(pe[0], pa[1], pa[0], pe[1]);- const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pc[1], pc[0], pa[1]);- const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pd[1], pd[0], pb[1]);- const detail::Expansion<T, 4> ce = detail::ExpansionBase<T>::TwoTwoDiff(pc[0], pe[1], pe[0], pc[1]);- const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(pd[0], pa[1], pa[0], pd[1]);- const detail::Expansion<T, 4> eb = detail::ExpansionBase<T>::TwoTwoDiff(pe[0], pb[1], pb[0], pe[1]);-- const detail::Expansion<T, 24> abc = bc * pa[2] + ac * -pb[2] + ab * pc[2];- const detail::Expansion<T, 24> bcd = cd * pb[2] + bd * -pc[2] + bc * pd[2];- const detail::Expansion<T, 24> cde = de * pc[2] + ce * -pd[2] + cd * pe[2];- const detail::Expansion<T, 24> dea = ea * pd[2] + da * -pe[2] + de * pa[2];- const detail::Expansion<T, 24> eab = ab * pe[2] + eb * -pa[2] + ea * pb[2];- const detail::Expansion<T, 24> abd = bd * pa[2] + da * pb[2] + ab * pd[2];- const detail::Expansion<T, 24> bce = ce * pb[2] + eb * pc[2] + bc * pe[2];- const detail::Expansion<T, 24> cda = da * pc[2] + ac * pd[2] + cd * pa[2];- const detail::Expansion<T, 24> deb = eb * pd[2] + bd * pe[2] + de * pb[2];- const detail::Expansion<T, 24> eac = ac * pe[2] + ce * pa[2] + ea * pc[2];-- const detail::Expansion<T, 96> bcde = (cde + bce) - (deb + bcd);- const detail::Expansion<T, 96> cdea = (dea + cda) - (eac + cde);- const detail::Expansion<T, 96> deab = (eab + deb) - (abd + dea);- const detail::Expansion<T, 96> eabc = (abc + eac) - (bce + eab);- const detail::Expansion<T, 96> abcd = (bcd + abd) - (cda + abc);-- const detail::Expansion<T, 1152> adet = bcde * pa[0] * pa[0] + bcde * pa[1] * pa[1] + bcde * pa[2] * pa[2];- const detail::Expansion<T, 1152> bdet = cdea * pb[0] * pb[0] + cdea * pb[1] * pb[1] + cdea * pb[2] * pb[2];- const detail::Expansion<T, 1152> cdet = deab * pc[0] * pc[0] + deab * pc[1] * pc[1] + deab * pc[2] * pc[2];- const detail::Expansion<T, 1152> ddet = eabc * pd[0] * pd[0] + eabc * pd[1] * pd[1] + eabc * pd[2] * pd[2];- const detail::Expansion<T, 1152> edet = abcd * pe[0] * pe[0] + abcd * pe[1] * pe[1] + abcd * pe[2] * pe[2];-- const detail::Expansion<T, 5760> deter = (adet + bdet) + ((cdet + ddet) + edet);- return deter.mostSignificant();- }- }-- template <typename T>- const T& Epsilon()- {- static const T epsilon = static_cast<T>(-#ifdef PREDICATES_CXX11_IS_SUPPORTED- std::exp2(-std::numeric_limits<T>::digits)-#else- std::ldexp(T(1), -std::numeric_limits<T>::digits)-#endif- );- return epsilon;- }-- template <typename T>- class Constants {- public:- static const T epsilon, resulterrbound;- static const T ccwerrboundA, ccwerrboundB, ccwerrboundC;- static const T o3derrboundA, o3derrboundB, o3derrboundC;- static const T iccerrboundA, iccerrboundB, iccerrboundC;- static const T isperrboundA, isperrboundB, isperrboundC;- };-- template <typename T> const T Constants<T>::epsilon = Epsilon<T>();- template <typename T> const T Constants<T>::resulterrbound = (T( 3) + T( 8) * Epsilon<T>()) * Epsilon<T>();- template <typename T> const T Constants<T>::ccwerrboundA = (T( 3) + T( 16) * Epsilon<T>()) * Epsilon<T>();- template <typename T> const T Constants<T>::ccwerrboundB = (T( 2) + T( 12) * Epsilon<T>()) * Epsilon<T>();- template <typename T> const T Constants<T>::ccwerrboundC = (T( 9) + T( 64) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>();- template <typename T> const T Constants<T>::o3derrboundA = (T( 7) + T( 56) * Epsilon<T>()) * Epsilon<T>();- template <typename T> const T Constants<T>::o3derrboundB = (T( 3) + T( 28) * Epsilon<T>()) * Epsilon<T>();- template <typename T> const T Constants<T>::o3derrboundC = (T(26) + T( 288) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>();- template <typename T> const T Constants<T>::iccerrboundA = (T(10) + T( 96) * Epsilon<T>()) * Epsilon<T>();- template <typename T> const T Constants<T>::iccerrboundB = (T( 4) + T( 48) * Epsilon<T>()) * Epsilon<T>();- template <typename T> const T Constants<T>::iccerrboundC = (T(44) + T( 576) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>();- template <typename T> const T Constants<T>::isperrboundA = (T(16) + T( 224) * Epsilon<T>()) * Epsilon<T>();- template <typename T> const T Constants<T>::isperrboundB = (T( 5) + T( 72) * Epsilon<T>()) * Epsilon<T>();- template <typename T> const T Constants<T>::isperrboundC = (T(71) + T(1408) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>();-- namespace adaptive {- template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy) {- const T acx = ax - cx;- const T bcx = bx - cx;- const T acy = ay - cy;- const T bcy = by - cy;- const T detleft = acx * bcy;- const T detright = acy * bcx;- T det = detleft - detright;- if((detleft < 0) != (detright < 0)) return det;- if(T(0) == detleft || T(0) == detright) return det;-- const T detsum = std::abs(detleft + detright);- T errbound = Constants<T>::ccwerrboundA * detsum;- if(std::abs(det) >= std::abs(errbound)) return det;-- const detail::Expansion<T, 4> B = detail::ExpansionBase<T>::TwoTwoDiff(acx, bcy, acy, bcx);- det = B.estimate();- errbound = Constants<T>::ccwerrboundB * detsum;- if(std::abs(det) >= std::abs(errbound)) return det;-- const T acxtail = detail::ExpansionBase<T>::MinusTail(ax, cx, acx);- const T bcxtail = detail::ExpansionBase<T>::MinusTail(bx, cx, bcx);- const T acytail = detail::ExpansionBase<T>::MinusTail(ay, cy, acy);- const T bcytail = detail::ExpansionBase<T>::MinusTail(by, cy, bcy);- if(T(0) == acxtail && T(0) == bcxtail && T(0) == acytail && T(0) == bcytail) return det;-- errbound = Constants<T>::ccwerrboundC * detsum + Constants<T>::resulterrbound * std::abs(det);- det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail);- if(std::abs(det) >= std::abs(errbound)) return det;-- const detail::Expansion<T, 16> D = ((B + detail::ExpansionBase<T>::TwoTwoDiff(acxtail, bcy, acytail, bcx)) + detail::ExpansionBase<T>::TwoTwoDiff(acx, bcytail, acy, bcxtail)) + detail::ExpansionBase<T>::TwoTwoDiff(acxtail, bcytail, acytail, bcxtail);- return D.mostSignificant();- }-- template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc) {- return orient2d(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1]);- }-- template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy) {- const T adx = ax - dx;- const T bdx = bx - dx;- const T cdx = cx - dx;- const T ady = ay - dy;- const T bdy = by - dy;- const T cdy = cy - dy;- const T bdxcdy = bdx * cdy;- const T cdxbdy = cdx * bdy;- const T cdxady = cdx * ady;- const T adxcdy = adx * cdy;- const T adxbdy = adx * bdy;- const T bdxady = bdx * ady;- const T alift = adx * adx + ady * ady;- const T blift = bdx * bdx + bdy * bdy;- const T clift = cdx * cdx + cdy * cdy;- T det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + clift * (adxbdy - bdxady);- const T permanent = (std::abs(bdxcdy) + std::abs(cdxbdy)) * alift- + (std::abs(cdxady) + std::abs(adxcdy)) * blift- + (std::abs(adxbdy) + std::abs(bdxady)) * clift;- T errbound = Constants<T>::iccerrboundA * permanent;- if(std::abs(det) >= std::abs(errbound)) return det;-- const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bdx, cdy, cdx, bdy);- const detail::Expansion<T, 4> ca = detail::ExpansionBase<T>::TwoTwoDiff(cdx, ady, adx, cdy);- const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(adx, bdy, bdx, ady);- const detail::Expansion<T, 32> adet = bc * adx * adx + bc * ady * ady;- const detail::Expansion<T, 32> bdet = ca * bdx * bdx + ca * bdy * bdy;- const detail::Expansion<T, 32> cdet = ab * cdx * cdx + ab * cdy * cdy;- const detail::Expansion<T, 96> fin1 = adet + bdet + cdet;- det = fin1.estimate();- errbound = Constants<T>::iccerrboundB * permanent;- if(std::abs(det) >= std::abs(errbound)) return det;-- const T adxtail = detail::ExpansionBase<T>::MinusTail(ax, dx, adx);- const T adytail = detail::ExpansionBase<T>::MinusTail(ay, dy, ady);- const T bdxtail = detail::ExpansionBase<T>::MinusTail(bx, dx, bdx);- const T bdytail = detail::ExpansionBase<T>::MinusTail(by, dy, bdy);- const T cdxtail = detail::ExpansionBase<T>::MinusTail(cx, dx, cdx);- const T cdytail = detail::ExpansionBase<T>::MinusTail(cy, dy, cdy);- if(T(0) == adxtail && T(0) == bdxtail && T(0) == cdxtail && T(0) == adytail && T(0) == bdytail && T(0) == cdytail) return det;-- errbound = Constants<T>::iccerrboundC * permanent + Constants<T>::resulterrbound * std::abs(det);- det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail))- + (bdx * cdy - bdy * cdx) * (adx * adxtail + ady * adytail) * T(2))- + ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail))- + (cdx * ady - cdy * adx) * (bdx * bdxtail + bdy * bdytail) * T(2))- + ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail))- + (adx * bdy - ady * bdx) * (cdx * cdxtail + cdy * cdytail) * T(2));- if(std::abs(det) >= std::abs(errbound)) return det;- return exact::incircle(ax, ay, bx, by, cx, cy, dx, dy);- }-- template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd) {- return incircle(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1], pd[0], pd[1]);- }-- //@brief : determine if the 3d point d is above, on, or below the plane defined by a, b, and c- //@param pa: pointer to a as {x, y, z}- //@param pb: pointer to b as {x, y, z}- //@param pc: pointer to c as {x, y, z}- //@param pd: pointer to d as {x, y, z}- //@return : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}}- //@note : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c- template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd) {- const T adx = pa[0] - pd[0];- const T bdx = pb[0] - pd[0];- const T cdx = pc[0] - pd[0];- const T ady = pa[1] - pd[1];- const T bdy = pb[1] - pd[1];- const T cdy = pc[1] - pd[1];- const T adz = pa[2] - pd[2];- const T bdz = pb[2] - pd[2];- const T cdz = pc[2] - pd[2];- const T bdxcdy = bdx * cdy;- const T cdxbdy = cdx * bdy;- const T cdxady = cdx * ady;- const T adxcdy = adx * cdy;- const T adxbdy = adx * bdy;- const T bdxady = bdx * ady;- T det = adz * (bdxcdy - cdxbdy) + bdz * (cdxady - adxcdy) + cdz * (adxbdy - bdxady);- const T permanent = (std::abs(bdxcdy) + std::abs(cdxbdy)) * std::abs(adz) + (std::abs(cdxady) + std::abs(adxcdy)) * std::abs(bdz) + (std::abs(adxbdy) + std::abs(bdxady)) * std::abs(cdz);- T errbound = Constants<T>::o3derrboundA * permanent;- if(std::abs(det) >= std::abs(errbound)) return det;-- const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bdx, cdy, cdx, bdy);- const detail::Expansion<T, 4> ca = detail::ExpansionBase<T>::TwoTwoDiff(cdx, ady, adx, cdy);- const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(adx, bdy, bdx, ady);- const detail::Expansion<T, 24> fin1 = (bc * adz + ca * bdz) + ab * cdz;- det = fin1.estimate();- errbound = Constants<T>::o3derrboundB * permanent;- if(std::abs(det) >= std::abs(errbound)) return det;-- const T adxtail = detail::ExpansionBase<T>::MinusTail(pa[0], pd[0], adx);- const T bdxtail = detail::ExpansionBase<T>::MinusTail(pb[0], pd[0], bdx);- const T cdxtail = detail::ExpansionBase<T>::MinusTail(pc[0], pd[0], cdx);- const T adytail = detail::ExpansionBase<T>::MinusTail(pa[1], pd[1], ady);- const T bdytail = detail::ExpansionBase<T>::MinusTail(pb[1], pd[1], bdy);- const T cdytail = detail::ExpansionBase<T>::MinusTail(pc[1], pd[1], cdy);- const T adztail = detail::ExpansionBase<T>::MinusTail(pa[2], pd[2], adz);- const T bdztail = detail::ExpansionBase<T>::MinusTail(pb[2], pd[2], bdz);- const T cdztail = detail::ExpansionBase<T>::MinusTail(pc[2], pd[2], cdz);- if(T(0) == adxtail && T(0) == adytail && T(0) == adztail &&- T(0) == bdxtail && T(0) == bdytail && T(0) == bdztail &&- T(0) == cdxtail && T(0) == cdytail && T(0) == cdztail) return det;-- errbound = Constants<T>::o3derrboundC * permanent + Constants<T>::resulterrbound * std::abs(det);- det += (adz * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) + adztail * (bdx * cdy - bdy * cdx))- + (bdz * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) + bdztail * (cdx * ady - cdy * adx))- + (cdz * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) + cdztail * (adx * bdy - ady * bdx));- if(std::abs(det) >= std::abs(errbound)) return det;-- const detail::Expansion<T, 8> bct = detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(bdxtail, cdy, bdytail, cdx) + detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(cdytail, bdx, cdxtail, bdy);- const detail::Expansion<T, 8> cat = detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(cdxtail, ady, cdytail, adx) + detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(adytail, cdx, adxtail, cdy);- const detail::Expansion<T, 8> abt = detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(adxtail, bdy, adytail, bdx) + detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(bdytail, adx, bdxtail, ady);- const detail::Expansion<T, 192> fin2 = fin1 + bct * adz + cat * bdz + abt * cdz + bc * adztail + ca * bdztail + ab * cdztail- + detail::ExpansionBase<T>::ThreeProd( adxtail, bdytail, cdz) + detail::ExpansionBase<T>::ThreeProd( adxtail, bdytail, cdztail)- + detail::ExpansionBase<T>::ThreeProd(-adxtail, cdytail, bdz) + detail::ExpansionBase<T>::ThreeProd(-adxtail, cdytail, bdztail)- + detail::ExpansionBase<T>::ThreeProd( bdxtail, cdytail, adz) + detail::ExpansionBase<T>::ThreeProd( bdxtail, cdytail, adztail)- + detail::ExpansionBase<T>::ThreeProd(-bdxtail, adytail, cdz) + detail::ExpansionBase<T>::ThreeProd(-bdxtail, adytail, cdztail)- + detail::ExpansionBase<T>::ThreeProd( cdxtail, adytail, bdz) + detail::ExpansionBase<T>::ThreeProd( cdxtail, adytail, bdztail)- + detail::ExpansionBase<T>::ThreeProd(-cdxtail, bdytail, adz) + detail::ExpansionBase<T>::ThreeProd(-cdxtail, bdytail, adztail)- + bct * adztail + cat * bdztail + abt * cdztail;- return fin2.mostSignificant();- }-- //@brief : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d- //@param pa: pointer to a as {x, y, z}- //@param pb: pointer to b as {x, y, z}- //@param pc: pointer to c as {x, y, z}- //@param pd: pointer to d as {x, y, z}- //@param pe: pointer to e as {x, y, z}- //@return : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}}- //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c- template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe) {- T permanent;- const T aex = pa[0] - pe[0];- const T bex = pb[0] - pe[0];- const T cex = pc[0] - pe[0];- const T dex = pd[0] - pe[0];- const T aey = pa[1] - pe[1];- const T bey = pb[1] - pe[1];- const T cey = pc[1] - pe[1];- const T dey = pd[1] - pe[1];- const T aez = pa[2] - pe[2];- const T bez = pb[2] - pe[2];- const T cez = pc[2] - pe[2];- const T dez = pd[2] - pe[2];- {- const T aexbey = aex * bey;- const T bexaey = bex * aey;- const T bexcey = bex * cey;- const T cexbey = cex * bey;- const T cexdey = cex * dey;- const T dexcey = dex * cey;- const T dexaey = dex * aey;- const T aexdey = aex * dey;- const T aexcey = aex * cey;- const T cexaey = cex * aey;- const T bexdey = bex * dey;- const T dexbey = dex * bey;- const T ab = aexbey - bexaey;- const T bc = bexcey - cexbey;- const T cd = cexdey - dexcey;- const T da = dexaey - aexdey;- const T ac = aexcey - cexaey;- const T bd = bexdey - dexbey;- const T abc = aez * bc - bez * ac + cez * ab;- const T bcd = bez * cd - cez * bd + dez * bc;- const T cda = cez * da + dez * ac + aez * cd;- const T dab = dez * ab + aez * bd + bez * da;- const T alift = aex * aex + aey * aey + aez * aez;- const T blift = bex * bex + bey * bey + bez * bez;- const T clift = cex * cex + cey * cey + cez * cez;- const T dlift = dex * dex + dey * dey + dez * dez;- const T det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd);- const T aezplus = std::abs(aez);- const T bezplus = std::abs(bez);- const T cezplus = std::abs(cez);- const T dezplus = std::abs(dez);- const T aexbeyplus = std::abs(aexbey);- const T bexaeyplus = std::abs(bexaey);- const T bexceyplus = std::abs(bexcey);- const T cexbeyplus = std::abs(cexbey);- const T cexdeyplus = std::abs(cexdey);- const T dexceyplus = std::abs(dexcey);- const T dexaeyplus = std::abs(dexaey);- const T aexdeyplus = std::abs(aexdey);- const T aexceyplus = std::abs(aexcey);- const T cexaeyplus = std::abs(cexaey);- const T bexdeyplus = std::abs(bexdey);- const T dexbeyplus = std::abs(dexbey);- permanent = ((cexdeyplus + dexceyplus) * bezplus + (dexbeyplus + bexdeyplus) * cezplus + (bexceyplus + cexbeyplus) * dezplus) * alift- + ((dexaeyplus + aexdeyplus) * cezplus + (aexceyplus + cexaeyplus) * dezplus + (cexdeyplus + dexceyplus) * aezplus) * blift- + ((aexbeyplus + bexaeyplus) * dezplus + (bexdeyplus + dexbeyplus) * aezplus + (dexaeyplus + aexdeyplus) * bezplus) * clift- + ((bexceyplus + cexbeyplus) * aezplus + (cexaeyplus + aexceyplus) * bezplus + (aexbeyplus + bexaeyplus) * cezplus) * dlift;- const T errbound = Constants<T>::isperrboundA * permanent;- if(std::abs(det) >= std::abs(errbound)) return det;- }-- const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(aex, bey, bex, aey);- const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bex, cey, cex, bey);- const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(cex, dey, dex, cey);- const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(dex, aey, aex, dey);- const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(aex, cey, cex, aey);- const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(bex, dey, dex, bey);- const detail::Expansion<T, 24> temp24a = bc * dez + (cd * bez + bd * -cez);- const detail::Expansion<T, 24> temp24b = cd * aez + (da * cez + ac * dez);- const detail::Expansion<T, 24> temp24c = da * bez + (ab * dez + bd * aez);- const detail::Expansion<T, 24> temp24d = ab * cez + (bc * aez + ac * -bez);- const detail::Expansion<T, 288> adet = temp24a * aex * -aex + temp24a * aey * -aey + temp24a * aez * -aez;- const detail::Expansion<T, 288> bdet = temp24b * bex * bex + temp24b * bey * bey + temp24b * bez * bez;- const detail::Expansion<T, 288> cdet = temp24c * cex * -cex + temp24c * cey * -cey + temp24c * cez * -cez;- const detail::Expansion<T, 288> ddet = temp24d * dex * dex + temp24d * dey * dey + temp24d * dez * dez;- const detail::Expansion<T, 1152> fin1 = (adet + bdet) + (cdet + ddet);- T det = fin1.estimate();- T errbound = Constants<T>::isperrboundB * permanent;- if(std::abs(det) >= std::abs(errbound)) return det;-- const T aextail = detail::ExpansionBase<T>::MinusTail(pa[0], pe[0], aex);- const T aeytail = detail::ExpansionBase<T>::MinusTail(pa[1], pe[1], aey);- const T aeztail = detail::ExpansionBase<T>::MinusTail(pa[2], pe[2], aez);- const T bextail = detail::ExpansionBase<T>::MinusTail(pb[0], pe[0], bex);- const T beytail = detail::ExpansionBase<T>::MinusTail(pb[1], pe[1], bey);- const T beztail = detail::ExpansionBase<T>::MinusTail(pb[2], pe[2], bez);- const T cextail = detail::ExpansionBase<T>::MinusTail(pc[0], pe[0], cex);- const T ceytail = detail::ExpansionBase<T>::MinusTail(pc[1], pe[1], cey);- const T ceztail = detail::ExpansionBase<T>::MinusTail(pc[2], pe[2], cez);- const T dextail = detail::ExpansionBase<T>::MinusTail(pd[0], pe[0], dex);- const T deytail = detail::ExpansionBase<T>::MinusTail(pd[1], pe[1], dey);- const T deztail = detail::ExpansionBase<T>::MinusTail(pd[2], pe[2], dez);- if (T(0) == aextail && T(0) == aeytail && T(0) == aeztail &&- T(0) == bextail && T(0) == beytail && T(0) == beztail &&- T(0) == cextail && T(0) == ceytail && T(0) == ceztail &&- T(0) == dextail && T(0) == deytail && T(0) == deztail) return det;-- errbound = Constants<T>::isperrboundC * permanent + Constants<T>::resulterrbound * std::abs(det);- const T abeps = (aex * beytail + bey * aextail) - (aey * bextail + bex * aeytail);- const T bceps = (bex * ceytail + cey * bextail) - (bey * cextail + cex * beytail);- const T cdeps = (cex * deytail + dey * cextail) - (cey * dextail + dex * ceytail);- const T daeps = (dex * aeytail + aey * dextail) - (dey * aextail + aex * deytail);- const T aceps = (aex * ceytail + cey * aextail) - (aey * cextail + cex * aeytail);- const T bdeps = (bex * deytail + dey * bextail) - (bey * dextail + dex * beytail);- const T ab3 = ab.mostSignificant();- const T bc3 = bc.mostSignificant();- const T cd3 = cd.mostSignificant();- const T da3 = da.mostSignificant();- const T ac3 = ac.mostSignificant();- const T bd3 = bd.mostSignificant();- det += ( ( (bex * bex + bey * bey + bez * bez) * ((cez * daeps + dez * aceps + aez * cdeps) + (ceztail * da3 + deztail * ac3 + aeztail * cd3))- + (dex * dex + dey * dey + dez * dez) * ((aez * bceps - bez * aceps + cez * abeps) + (aeztail * bc3 - beztail * ac3 + ceztail * ab3)) )- - ( (aex * aex + aey * aey + aez * aez) * ((bez * cdeps - cez * bdeps + dez * bceps) + (beztail * cd3 - ceztail * bd3 + deztail * bc3))- + (cex * cex + cey * cey + cez * cez) * ((dez * abeps + aez * bdeps + bez * daeps) + (deztail * ab3 + aeztail * bd3 + beztail * da3)) ) )- + T(2) * ( ( (bex * bextail + bey * beytail + bez * beztail) * (cez * da3 + dez * ac3 + aez * cd3)- + (dex * dextail + dey * deytail + dez * deztail) * (aez * bc3 - bez * ac3 + cez * ab3))- - ( (aex * aextail + aey * aeytail + aez * aeztail) * (bez * cd3 - cez * bd3 + dez * bc3)- + (cex * cextail + cey * ceytail + cez * ceztail) * (dez * ab3 + aez * bd3 + bez * da3)));- if(std::abs(det) >= std::abs(errbound)) return det;- return exact::insphere(pa, pb, pc, pd, pe);- }- }-}--#endif+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (c) 2019, William C. Lenthe * + * All rights reserved. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions are met: * + * * + * 1. Redistributions of source code must retain the above copyright notice, this * + * list of conditions and the following disclaimer. * + * * + * 2. Redistributions in binary form must reproduce the above copyright notice, * + * this list of conditions and the following disclaimer in the documentation * + * and/or other materials provided with the distribution. * + * * + * 3. Neither the name of the copyright holder nor the names of its * + * contributors may be used to endorse or promote products derived from * + * this software without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef PREDICATES_H_INCLUDED +#define PREDICATES_H_INCLUDED + +//@reference: https://www.cs.cmu.edu/~quake/robust.html + +namespace predicates { + //@brief: geometric predicates using arbitrary precision arithmetic + //@note : these are provided primarily for illustrative purposes and adaptive routines should be preferred + namespace exact { + //@brief : determine if the 2d point c is above, on, or below the line defined by a and b + //@param ax: X-coordinate of a + //@param ay: Y-coordinate of a + //@param bx: X-coordinate of b + //@param by: Y-coordinate of b + //@param cx: X-coordinate of c + //@param cy: Y-coordinate of c + //@return : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}} + //@note : positive, 0, negative result for c above, on, or below the line defined by a -> b + template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy); + + //@brief : determine if the 2d point c is above, on, or below the line defined by a and b + //@param pa: pointer to a as {x, y} + //@param pb: pointer to b as {x, y} + //@param pc: pointer to c as {x, y} + //@return : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}} + //@note : positive, 0, negative result for c above, on, or below the line defined by a -> b + template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc); + + //@brief : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c + //@param ax: X-coordinate of a + //@param ay: Y-coordinate of a + //@param bx: X-coordinate of b + //@param by: Y-coordinate of b + //@param cx: X-coordinate of c + //@param cy: Y-coordinate of c + //@param dx: X-coordinate of d + //@param dy: Y-coordinate of d + //@return : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}} + //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c + template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy); + + //@brief : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c + //@param pa: pointer to a as {x, y} + //@param pb: pointer to b as {x, y} + //@param pc: pointer to c as {x, y} + //@param pc: pointer to d as {x, y} + //@return : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}} + //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c + template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd); + + //@brief : determine if the 3d point d is above, on, or below the plane defined by a, b, and c + //@param pa: pointer to a as {x, y, z} + //@param pb: pointer to b as {x, y, z} + //@param pc: pointer to c as {x, y, z} + //@param pd: pointer to d as {x, y, z} + //@return : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}} + //@note : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c + template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd); + + //@brief : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d + //@param pa: pointer to a as {x, y, z} + //@param pb: pointer to b as {x, y, z} + //@param pc: pointer to c as {x, y, z} + //@param pd: pointer to d as {x, y, z} + //@param pe: pointer to e as {x, y, z} + //@return : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}} + //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c + template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe); + } + + //@brief: geometric predicates using normal floating point arithmetic but falling back to arbitrary precision when needed + //@note : these should have the same accuracy but are significantly faster when determinants are large + namespace adaptive { + //@brief : determine if the 2d point c is above, on, or below the line defined by a and b + //@param ax: X-coordinate of a + //@param ay: Y-coordinate of a + //@param bx: X-coordinate of b + //@param by: Y-coordinate of b + //@param cx: X-coordinate of c + //@param cy: Y-coordinate of c + //@return : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}} + //@note : positive, 0, negative result for c above, on, or below the line defined by a -> b + template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy); + + //@brief : determine if the 2d point c is above, on, or below the line defined by a and b + //@param pa: pointer to a as {x, y} + //@param pb: pointer to b as {x, y} + //@param pc: pointer to c as {x, y} + //@return : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}} + //@note : positive, 0, negative result for c above, on, or below the line defined by a -> b + template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc); + + //@brief : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c + //@param ax: X-coordinate of a + //@param ay: Y-coordinate of a + //@param bx: X-coordinate of b + //@param by: Y-coordinate of b + //@param cx: X-coordinate of c + //@param cy: Y-coordinate of c + //@param dx: X-coordinate of d + //@param dy: Y-coordinate of d + //@return : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}} + //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c + template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy); + + //@brief : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c + //@param pa: pointer to a as {x, y} + //@param pb: pointer to b as {x, y} + //@param pc: pointer to c as {x, y} + //@param pc: pointer to d as {x, y} + //@return : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}} + //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c + template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd); + + //@brief : determine if the 3d point d is above, on, or below the plane defined by a, b, and c + //@param pa: pointer to a as {x, y, z} + //@param pb: pointer to b as {x, y, z} + //@param pc: pointer to c as {x, y, z} + //@param pd: pointer to d as {x, y, z} + //@return : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}} + //@note : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c + template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd); + + //@brief : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d + //@param pa: pointer to a as {x, y, z} + //@param pb: pointer to b as {x, y, z} + //@param pc: pointer to c as {x, y, z} + //@param pd: pointer to d as {x, y, z} + //@param pe: pointer to e as {x, y, z} + //@return : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}} + //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c + template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe); + } +} + +#include <cmath>//abs, fma +#include <limits> +#include <utility>//pair +#include <numeric>//accumulate +#include <algorithm>//transform, copy_n, merge +#include <functional>//negate + +// a macro based static assert for pre c++11 +#define PREDICATES_PORTABLE_STATIC_ASSERT(condition, message) typedef char message[(condition) ? 1 : -1] + +// check if c++11 is supported +#if !defined(__cplusplus) && !defined(_MSC_VER) + PREDICATES_PORTABLE_STATIC_ASSERT(false, couldnt_parse_cxx_standard) +#endif +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) + #define PREDICATES_CXX11_IS_SUPPORTED +#endif + +// choose to use c++11 features or their backports +#ifdef PREDICATES_CXX11_IS_SUPPORTED +#include <array> +#include <type_traits>// is_same, enable_if +#undef PREDICATES_PORTABLE_STATIC_ASSERT +#define PREDICATES_TOKEN_TO_STRING1(x) #x +#define PREDICATES_TOKEN_TO_STRING(x) PREDICATES_TOKEN_TO_STRING1(x) +#define PREDICATES_PORTABLE_STATIC_ASSERT(condition, message) static_assert(condition, PREDICATES_TOKEN_TO_STRING(message)) +namespace predicates { +namespace stdx { + using std::array; + using std::copy_n; +} +#else +namespace predicates { +namespace stdx { + // array + template<typename T, size_t N> + class array { + T buff[N]; + public: + T& operator[](const size_t& i) { return buff[i]; } + const T& operator[](const size_t& i) const { return buff[i]; } + + T * data() { return buff; } + T const * data() const { return buff; } + + T * begin() { return buff; } + T const * cbegin() const { return buff; } + }; + // copy_n + template< class InputIt, class Size, class OutputIt> + OutputIt copy_n(InputIt first, Size count, OutputIt result) + { + if (count > 0) { + *result++ = *first; + for (Size i = 1; i < count; ++i) { + *result++ = *++first; + } + } + return result; + } +} +#endif // PREDICATES_CXX11_IS_SUPPORTED + +namespace detail { + template<typename T> class ExpansionBase; + + //@brief: class to exactly represent the result of a sequence of arithmetic operations as an sequence of values that sum to the result + template<typename T, size_t N> + class Expansion : private ExpansionBase<T>, public stdx::array<T, N> { + private: + public: + size_t m_size; + template <typename S> friend class ExpansionBase;//access for base class + template <typename S, size_t M> friend class Expansion;//access for expansions of different size + + Expansion() : m_size(0) {} + template <size_t M> Expansion& operator=(const Expansion<T, M>& e) { + PREDICATES_PORTABLE_STATIC_ASSERT(M <= N, cannot_assign_a_larger_expansion_to_a_smaller_expansion); + stdx::copy_n(e.cbegin(), e.size(), stdx::array<T, N>::begin()); + m_size = e.size(); + return *this; + } + + //vector like convenience functions + size_t size() const {return m_size;} + bool empty() const {return 0 == m_size;} + void push_back(const T v) {stdx::array<T, N>::operator[](m_size++) = v;} + + public: + //estimates of expansion value + T mostSignificant() const {return empty() ? T(0) : stdx::array<T, N>::operator[](m_size - 1);} + T estimate() const {return std::accumulate(stdx::array<T, N>::cbegin(), stdx::array<T, N>::cbegin() + size(), T(0));} + + template <size_t M> Expansion<T, N+M> operator+(const Expansion<T, M>& f) const { + Expansion<T, N+M> h; + h.m_size = ExpansionBase<T>::ExpansionSum(this->data(), this->size(), f.data(), f.size(), h.data()); + return h; + } + + void negate() {std::transform(stdx::array<T, N>::cbegin(), stdx::array<T, N>::cbegin() + size(), stdx::array<T, N>::begin(), std::negate<T>());} + Expansion operator-() const {Expansion e = *this; e.negate(); return e;} + template <size_t M> Expansion<T, N+M> operator-(const Expansion<T, M>& f) const {return operator+(-f);} + + Expansion<T, 2*N> operator*(const T b) const { + Expansion<T, 2*N> h; + h.m_size = ExpansionBase<T>::ScaleExpansion(this->data(), this->size(), b, h.data()); + return h; + } + }; + +// See: https://stackoverflow.com/a/40765925/1597714 +// Standard defines: https://en.cppreference.com/w/cpp/numeric/math/fma +#if defined(__FMA__) || defined(__FMA4__) || defined(__AVX2__) || (defined(FP_FAST_FMA) && defined(FP_FAST_FMAF)) +#define PREDICATES_FAST_FMA 1 +#endif + + //@brief : helper function to sort by absolute value + //@param a: lhs item to compare + //@param b: rhs item to compare + //@return : true if |a| < |b| + //@note : defined since lambda functions aren't allow in c++03 + template <typename T> bool absLess(const T& a, const T& b) {return std::abs(a) < std::abs(b);} + + template<typename T> + class ExpansionBase { + private: + static const T Splitter; + + PREDICATES_PORTABLE_STATIC_ASSERT(std::numeric_limits<T>::is_iec559, Requires_IEC_559_IEEE_754_floating_point_type); + PREDICATES_PORTABLE_STATIC_ASSERT(2 == std::numeric_limits<T>::radix, Requires_base_2_floating_point_type); + + //combine result + roundoff error into expansion + static inline Expansion<T, 2> MakeExpansion(const T value, const T tail) { + Expansion<T, 2> e; + if(T(0) != tail) e.push_back(tail); + if(T(0) != value) e.push_back(value); + return e; + } + + protected: + //add 2 expansions + static size_t ExpansionSum(T const * const e, const size_t n, T const * const f, const size_t m, T * const h) { + std::merge(e, e + n, f, f + m, h, absLess<T>); + if(m == 0) return n; + if(n == 0) return m; + size_t hIndex = 0; + T Q = h[0]; + T Qnew = h[1] + Q; + T hh = FastPlusTail(h[1], Q, Qnew); + Q = Qnew; + if(T(0) != hh) h[hIndex++] = hh; + for(size_t g = 2; g != n + m; ++g) { + Qnew = Q + h[g]; + hh = PlusTail(Q, h[g], Qnew); + Q = Qnew; + if(T(0) != hh) h[hIndex++] = hh; + } + if(T(0) != Q) h[hIndex++] = Q; + return hIndex; + } + + //scale an expansion by a constant + static size_t ScaleExpansion(T const * const e, const size_t n, const T b, T * const h) { + if(n == 0 || T(0) == b) return 0; + size_t hIndex = 0; + T Q = e[0] * b; + const std::pair<T, T> bSplit = Split(b); + T hh = MultTailPreSplit(e[0], b, bSplit, Q); + if(T(0) != hh) h[hIndex++] = hh; + for(size_t eIndex = 1; eIndex < n; ++eIndex) { + T Ti = e[eIndex] * b; + T ti = MultTailPreSplit(e[eIndex], b, bSplit, Ti); + T Qi = Q + ti; + hh = PlusTail(Q, ti, Qi); + if(T(0) != hh) h[hIndex++] = hh; + Q = Ti + Qi; + hh = FastPlusTail(Ti, Qi, Q); + if(T(0) != hh) h[hIndex++] = hh; + } + if(T(0) != Q) h[hIndex++] = Q; + return hIndex; + } + + public: + //roundoff error of x = a + b + static inline T PlusTail(const T a, const T b, const T x) { + const T bVirtual = x - a; + const T aVirtual = x - bVirtual; + const T bRoundoff = b - bVirtual; + const T aRoundoff = a - aVirtual; + return aRoundoff + bRoundoff; + } + + //roundoff error of x = a + b if |a| > |b| + static inline T FastPlusTail(const T a, const T b, const T x) { + const T bVirtual = x - a; + return b - bVirtual; + } + + //roundoff error of x = a - b + static inline T MinusTail(const T a, const T b, const T x) { + const T bVirtual = a - x; + const T aVirtual = x + bVirtual; + const T bRoundoff = bVirtual - b; + const T aRoundoff = a - aVirtual; + return aRoundoff + bRoundoff; + } + + //split a into 2 nonoverlapping values + static inline std::pair<T, T> Split(const T a) { + const T c = a * Splitter; + const T aBig = c - a; + const T aHi = c - aBig; + return std::pair<T, T>(aHi, a - aHi); + } + + //roundoff error of x = a * b via dekkers product + static inline T DekkersProduct(const T /*a*/, const std::pair<T, T> aSplit, const T /*b*/, const std::pair<T, T> bSplit, const T p) { + T y = p - T(aSplit.first * bSplit.first); + y -= T(aSplit.second * bSplit.first); + y -= T(aSplit.first * bSplit.second); + return T(aSplit.second * bSplit.second) - y; + } + + //roundoff error of x = a * b +#if defined(PREDICATES_CXX11_IS_SUPPORTED) && defined(PREDICATES_FAST_FMA) + static T MultTail(const T a, const T b, const T p) {return std::fma(a, b, -p);} + static T MultTailPreSplit(const T a, const T b, const std::pair<T, T> /*bSplit*/, const T p) {return std::fma(a, b, -p);} +#else + static T MultTail(const T a, const T b, const T p) {return DekkersProduct(a, Split(a), b, Split(b), p);} + static T MultTailPreSplit(const T a, const T b, const std::pair<T, T> bSplit, const T p) {return DekkersProduct(a, Split(a), b, bSplit, p);} +#endif + //expand a + b + static inline Expansion<T, 2> Plus(const T a, const T b) { + const T x = a + b; + return MakeExpansion(x, PlusTail(a, b, x)); + } + + //expand a - b + static inline Expansion<T, 2> Minus(const T a, const T b) {return Plus(a, -b);} + + //expand a * b + static inline Expansion<T, 2> Mult(const T a, const T b) { + const T x = a * b; + return MakeExpansion(x, MultTail(a, b, x)); + } + + //expand the determinant of {{ax, ay}, {bx, by}} (unrolled Mult(ax, by) - Mult(ay, bx)) + static inline Expansion<T, 4> TwoTwoDiff(const T ax, const T by, const T ay, const T bx) { + const T axby1 = ax * by; + const T axby0 = MultTail(ax, by, axby1); + const T bxay1 = bx * ay; + const T bxay0 = MultTail(bx, ay, bxay1); + const T _i0 = axby0 - bxay0; + const T x0 = MinusTail(axby0, bxay0, _i0); + const T _j = axby1 + _i0; + const T _0 = PlusTail(axby1, _i0, _j); + const T _i1 = _0 - bxay1; + const T x1 = MinusTail(_0, bxay1, _i1); + const T x3 = _j + _i1; + const T x2 = PlusTail(_j, _i1, x3); + Expansion<T, 4> e; + if(T(0) != x0) e.push_back(x0); + if(T(0) != x1) e.push_back(x1); + if(T(0) != x2) e.push_back(x2); + if(T(0) != x3) e.push_back(x3); + return e; + } + + //TwoTwoDiff checking for zeros to avoid extra splitting + static inline Expansion<T, 4> TwoTwoDiffZeroCheck(const T ax, const T by, const T ay, const T bx) { + Expansion<T, 4> e; + if(T(0) == ax && T(0) == ay) return e; + else if(T(0) == ax) e = Mult(ay, bx); + else if(T(0) == ay) e = Mult(ax, by); + else e = TwoTwoDiff(ax, by, ay, bx); + return e; + } + + //(a * b) * c checking for zeros + static inline Expansion<T, 4> ThreeProd(const T a, const T b, const T c) {return (T(0) == a || T(0) == b || T(0) == c) ? Expansion<T, 4>() : Mult(a, b) * c;} + }; + + template <typename T> const T ExpansionBase<T>::Splitter = +#ifdef PREDICATES_CXX11_IS_SUPPORTED + static_cast<T>(std::exp2(std::numeric_limits<T>::digits/2 + 1)); +#else + static_cast<T>(std::ldexp(T(1), std::numeric_limits<T>::digits/2 + 1)); +#endif +} + + namespace exact { + template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy) + { + const detail::Expansion<T, 4> aterms = detail::ExpansionBase<T>::TwoTwoDiff(ax, by, ax, cy); + const detail::Expansion<T, 4> bterms = detail::ExpansionBase<T>::TwoTwoDiff(bx, cy, bx, ay); + const detail::Expansion<T, 4> cterms = detail::ExpansionBase<T>::TwoTwoDiff(cx, ay, cx, by); + const detail::Expansion<T, 12> w = aterms + bterms + cterms; + return w.mostSignificant(); + } + + template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc) { + return orient2d(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1]); + } + + template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy) { + const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(ax, by, bx, ay); + const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bx, cy, cx, by); + const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(cx, dy, dx, cy); + const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(dx, ay, ax, dy); + const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(ax, cy, cx, ay); + const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(bx, dy, dx, by); + + const detail::Expansion<T, 12> abc = ab + bc - ac; + const detail::Expansion<T, 12> bcd = bc + cd - bd; + const detail::Expansion<T, 12> cda = cd + da + ac; + const detail::Expansion<T, 12> dab = da + ab + bd; + + const detail::Expansion<T, 96> adet = bcd * ax * ax + bcd * ay * ay; + const detail::Expansion<T, 96> bdet = cda * bx * -bx + cda * by * -by; + const detail::Expansion<T, 96> cdet = dab * cx * cx + dab * cy * cy; + const detail::Expansion<T, 96> ddet = abc * dx * -dx + abc * dy * -dy; + + const detail::Expansion<T, 384> deter = (adet + bdet) + (cdet + ddet); + return deter.mostSignificant(); + } + + template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd) { + return incircle(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1], pd[0], pd[1]); + } + + //@brief : determine if the 3d point d is above, on, or below the plane defined by a, b, and c + //@param pa: pointer to a as {x, y, z} + //@param pb: pointer to b as {x, y, z} + //@param pc: pointer to c as {x, y, z} + //@param pd: pointer to d as {x, y, z} + //@return : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}} + //@note : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c + template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd) { + const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pb[1], pb[0], pa[1]); + const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pc[1], pc[0], pb[1]); + const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(pc[0], pd[1], pd[0], pc[1]); + const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(pd[0], pa[1], pa[0], pd[1]); + const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pc[1], pc[0], pa[1]); + const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pd[1], pd[0], pb[1]); + + const detail::Expansion<T, 12> abc = ab + bc - ac; + const detail::Expansion<T, 12> bcd = bc + cd - bd; + const detail::Expansion<T, 12> cda = cd + da + ac; + const detail::Expansion<T, 12> dab = da + ab + bd; + + const detail::Expansion<T, 24> adet = bcd * pa[2]; + const detail::Expansion<T, 24> bdet = cda * -pb[2]; + const detail::Expansion<T, 24> cdet = dab * pc[2]; + const detail::Expansion<T, 24> ddet = abc * -pd[2]; + + const detail::Expansion<T, 96> deter = (adet + bdet) + (cdet + ddet); + return deter.mostSignificant(); + } + + //@brief : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d + //@param pa: pointer to a as {x, y, z} + //@param pb: pointer to b as {x, y, z} + //@param pc: pointer to c as {x, y, z} + //@param pd: pointer to d as {x, y, z} + //@param pe: pointer to e as {x, y, z} + //@return : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}} + //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c + template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe) { + const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pb[1], pb[0], pa[1]); + const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pc[1], pc[0], pb[1]); + const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(pc[0], pd[1], pd[0], pc[1]); + const detail::Expansion<T, 4> de = detail::ExpansionBase<T>::TwoTwoDiff(pd[0], pe[1], pe[0], pd[1]); + const detail::Expansion<T, 4> ea = detail::ExpansionBase<T>::TwoTwoDiff(pe[0], pa[1], pa[0], pe[1]); + const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pc[1], pc[0], pa[1]); + const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pd[1], pd[0], pb[1]); + const detail::Expansion<T, 4> ce = detail::ExpansionBase<T>::TwoTwoDiff(pc[0], pe[1], pe[0], pc[1]); + const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(pd[0], pa[1], pa[0], pd[1]); + const detail::Expansion<T, 4> eb = detail::ExpansionBase<T>::TwoTwoDiff(pe[0], pb[1], pb[0], pe[1]); + + const detail::Expansion<T, 24> abc = bc * pa[2] + ac * -pb[2] + ab * pc[2]; + const detail::Expansion<T, 24> bcd = cd * pb[2] + bd * -pc[2] + bc * pd[2]; + const detail::Expansion<T, 24> cde = de * pc[2] + ce * -pd[2] + cd * pe[2]; + const detail::Expansion<T, 24> dea = ea * pd[2] + da * -pe[2] + de * pa[2]; + const detail::Expansion<T, 24> eab = ab * pe[2] + eb * -pa[2] + ea * pb[2]; + const detail::Expansion<T, 24> abd = bd * pa[2] + da * pb[2] + ab * pd[2]; + const detail::Expansion<T, 24> bce = ce * pb[2] + eb * pc[2] + bc * pe[2]; + const detail::Expansion<T, 24> cda = da * pc[2] + ac * pd[2] + cd * pa[2]; + const detail::Expansion<T, 24> deb = eb * pd[2] + bd * pe[2] + de * pb[2]; + const detail::Expansion<T, 24> eac = ac * pe[2] + ce * pa[2] + ea * pc[2]; + + const detail::Expansion<T, 96> bcde = (cde + bce) - (deb + bcd); + const detail::Expansion<T, 96> cdea = (dea + cda) - (eac + cde); + const detail::Expansion<T, 96> deab = (eab + deb) - (abd + dea); + const detail::Expansion<T, 96> eabc = (abc + eac) - (bce + eab); + const detail::Expansion<T, 96> abcd = (bcd + abd) - (cda + abc); + + const detail::Expansion<T, 1152> adet = bcde * pa[0] * pa[0] + bcde * pa[1] * pa[1] + bcde * pa[2] * pa[2]; + const detail::Expansion<T, 1152> bdet = cdea * pb[0] * pb[0] + cdea * pb[1] * pb[1] + cdea * pb[2] * pb[2]; + const detail::Expansion<T, 1152> cdet = deab * pc[0] * pc[0] + deab * pc[1] * pc[1] + deab * pc[2] * pc[2]; + const detail::Expansion<T, 1152> ddet = eabc * pd[0] * pd[0] + eabc * pd[1] * pd[1] + eabc * pd[2] * pd[2]; + const detail::Expansion<T, 1152> edet = abcd * pe[0] * pe[0] + abcd * pe[1] * pe[1] + abcd * pe[2] * pe[2]; + + const detail::Expansion<T, 5760> deter = (adet + bdet) + ((cdet + ddet) + edet); + return deter.mostSignificant(); + } + } + + template <typename T> + const T& Epsilon() + { + static const T epsilon = +#ifdef PREDICATES_CXX11_IS_SUPPORTED + static_cast<T>(std::exp2(-std::numeric_limits<T>::digits)); +#else + static_cast<T>(std::ldexp(T(1), -std::numeric_limits<T>::digits)); +#endif + return epsilon; + } + + template <typename T> + class Constants { + public: + static const T epsilon, resulterrbound; + static const T ccwerrboundA, ccwerrboundB, ccwerrboundC; + static const T o3derrboundA, o3derrboundB, o3derrboundC; + static const T iccerrboundA, iccerrboundB, iccerrboundC; + static const T isperrboundA, isperrboundB, isperrboundC; + }; + + template <typename T> const T Constants<T>::epsilon = Epsilon<T>(); + template <typename T> const T Constants<T>::resulterrbound = (T( 3) + T( 8) * Epsilon<T>()) * Epsilon<T>(); + template <typename T> const T Constants<T>::ccwerrboundA = (T( 3) + T( 16) * Epsilon<T>()) * Epsilon<T>(); + template <typename T> const T Constants<T>::ccwerrboundB = (T( 2) + T( 12) * Epsilon<T>()) * Epsilon<T>(); + template <typename T> const T Constants<T>::ccwerrboundC = (T( 9) + T( 64) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>(); + template <typename T> const T Constants<T>::o3derrboundA = (T( 7) + T( 56) * Epsilon<T>()) * Epsilon<T>(); + template <typename T> const T Constants<T>::o3derrboundB = (T( 3) + T( 28) * Epsilon<T>()) * Epsilon<T>(); + template <typename T> const T Constants<T>::o3derrboundC = (T(26) + T( 288) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>(); + template <typename T> const T Constants<T>::iccerrboundA = (T(10) + T( 96) * Epsilon<T>()) * Epsilon<T>(); + template <typename T> const T Constants<T>::iccerrboundB = (T( 4) + T( 48) * Epsilon<T>()) * Epsilon<T>(); + template <typename T> const T Constants<T>::iccerrboundC = (T(44) + T( 576) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>(); + template <typename T> const T Constants<T>::isperrboundA = (T(16) + T( 224) * Epsilon<T>()) * Epsilon<T>(); + template <typename T> const T Constants<T>::isperrboundB = (T( 5) + T( 72) * Epsilon<T>()) * Epsilon<T>(); + template <typename T> const T Constants<T>::isperrboundC = (T(71) + T(1408) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>(); + + namespace adaptive { + template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy) { + const T acx = ax - cx; + const T bcx = bx - cx; + const T acy = ay - cy; + const T bcy = by - cy; + const T detleft = acx * bcy; + const T detright = acy * bcx; + T det = detleft - detright; + if((detleft < 0) != (detright < 0)) return det; + if(T(0) == detleft || T(0) == detright) return det; + + const T detsum = std::abs(detleft + detright); + T errbound = Constants<T>::ccwerrboundA * detsum; + if(std::abs(det) >= std::abs(errbound)) return det; + + const detail::Expansion<T, 4> B = detail::ExpansionBase<T>::TwoTwoDiff(acx, bcy, acy, bcx); + det = B.estimate(); + errbound = Constants<T>::ccwerrboundB * detsum; + if(std::abs(det) >= std::abs(errbound)) return det; + + const T acxtail = detail::ExpansionBase<T>::MinusTail(ax, cx, acx); + const T bcxtail = detail::ExpansionBase<T>::MinusTail(bx, cx, bcx); + const T acytail = detail::ExpansionBase<T>::MinusTail(ay, cy, acy); + const T bcytail = detail::ExpansionBase<T>::MinusTail(by, cy, bcy); + if(T(0) == acxtail && T(0) == bcxtail && T(0) == acytail && T(0) == bcytail) return det; + + errbound = Constants<T>::ccwerrboundC * detsum + Constants<T>::resulterrbound * std::abs(det); + det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail); + if(std::abs(det) >= std::abs(errbound)) return det; + + const detail::Expansion<T, 16> D = ((B + detail::ExpansionBase<T>::TwoTwoDiff(acxtail, bcy, acytail, bcx)) + detail::ExpansionBase<T>::TwoTwoDiff(acx, bcytail, acy, bcxtail)) + detail::ExpansionBase<T>::TwoTwoDiff(acxtail, bcytail, acytail, bcxtail); + return D.mostSignificant(); + } + + template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc) { + return orient2d(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1]); + } + + template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy) { + const T adx = ax - dx; + const T bdx = bx - dx; + const T cdx = cx - dx; + const T ady = ay - dy; + const T bdy = by - dy; + const T cdy = cy - dy; + const T bdxcdy = bdx * cdy; + const T cdxbdy = cdx * bdy; + const T cdxady = cdx * ady; + const T adxcdy = adx * cdy; + const T adxbdy = adx * bdy; + const T bdxady = bdx * ady; + const T alift = adx * adx + ady * ady; + const T blift = bdx * bdx + bdy * bdy; + const T clift = cdx * cdx + cdy * cdy; + T det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + clift * (adxbdy - bdxady); + const T permanent = (std::abs(bdxcdy) + std::abs(cdxbdy)) * alift + + (std::abs(cdxady) + std::abs(adxcdy)) * blift + + (std::abs(adxbdy) + std::abs(bdxady)) * clift; + T errbound = Constants<T>::iccerrboundA * permanent; + if(std::abs(det) >= std::abs(errbound)) return det; + + const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bdx, cdy, cdx, bdy); + const detail::Expansion<T, 4> ca = detail::ExpansionBase<T>::TwoTwoDiff(cdx, ady, adx, cdy); + const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(adx, bdy, bdx, ady); + const detail::Expansion<T, 32> adet = bc * adx * adx + bc * ady * ady; + const detail::Expansion<T, 32> bdet = ca * bdx * bdx + ca * bdy * bdy; + const detail::Expansion<T, 32> cdet = ab * cdx * cdx + ab * cdy * cdy; + const detail::Expansion<T, 96> fin1 = adet + bdet + cdet; + det = fin1.estimate(); + errbound = Constants<T>::iccerrboundB * permanent; + if(std::abs(det) >= std::abs(errbound)) return det; + + const T adxtail = detail::ExpansionBase<T>::MinusTail(ax, dx, adx); + const T adytail = detail::ExpansionBase<T>::MinusTail(ay, dy, ady); + const T bdxtail = detail::ExpansionBase<T>::MinusTail(bx, dx, bdx); + const T bdytail = detail::ExpansionBase<T>::MinusTail(by, dy, bdy); + const T cdxtail = detail::ExpansionBase<T>::MinusTail(cx, dx, cdx); + const T cdytail = detail::ExpansionBase<T>::MinusTail(cy, dy, cdy); + if(T(0) == adxtail && T(0) == bdxtail && T(0) == cdxtail && T(0) == adytail && T(0) == bdytail && T(0) == cdytail) return det; + + errbound = Constants<T>::iccerrboundC * permanent + Constants<T>::resulterrbound * std::abs(det); + det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) + + (bdx * cdy - bdy * cdx) * (adx * adxtail + ady * adytail) * T(2)) + + ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) + + (cdx * ady - cdy * adx) * (bdx * bdxtail + bdy * bdytail) * T(2)) + + ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) + + (adx * bdy - ady * bdx) * (cdx * cdxtail + cdy * cdytail) * T(2)); + if(std::abs(det) >= std::abs(errbound)) return det; + return exact::incircle(ax, ay, bx, by, cx, cy, dx, dy); + } + + template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd) { + return incircle(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1], pd[0], pd[1]); + } + + //@brief : determine if the 3d point d is above, on, or below the plane defined by a, b, and c + //@param pa: pointer to a as {x, y, z} + //@param pb: pointer to b as {x, y, z} + //@param pc: pointer to c as {x, y, z} + //@param pd: pointer to d as {x, y, z} + //@return : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}} + //@note : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c + template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd) { + const T adx = pa[0] - pd[0]; + const T bdx = pb[0] - pd[0]; + const T cdx = pc[0] - pd[0]; + const T ady = pa[1] - pd[1]; + const T bdy = pb[1] - pd[1]; + const T cdy = pc[1] - pd[1]; + const T adz = pa[2] - pd[2]; + const T bdz = pb[2] - pd[2]; + const T cdz = pc[2] - pd[2]; + const T bdxcdy = bdx * cdy; + const T cdxbdy = cdx * bdy; + const T cdxady = cdx * ady; + const T adxcdy = adx * cdy; + const T adxbdy = adx * bdy; + const T bdxady = bdx * ady; + T det = adz * (bdxcdy - cdxbdy) + bdz * (cdxady - adxcdy) + cdz * (adxbdy - bdxady); + const T permanent = (std::abs(bdxcdy) + std::abs(cdxbdy)) * std::abs(adz) + (std::abs(cdxady) + std::abs(adxcdy)) * std::abs(bdz) + (std::abs(adxbdy) + std::abs(bdxady)) * std::abs(cdz); + T errbound = Constants<T>::o3derrboundA * permanent; + if(std::abs(det) >= std::abs(errbound)) return det; + + const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bdx, cdy, cdx, bdy); + const detail::Expansion<T, 4> ca = detail::ExpansionBase<T>::TwoTwoDiff(cdx, ady, adx, cdy); + const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(adx, bdy, bdx, ady); + const detail::Expansion<T, 24> fin1 = (bc * adz + ca * bdz) + ab * cdz; + det = fin1.estimate(); + errbound = Constants<T>::o3derrboundB * permanent; + if(std::abs(det) >= std::abs(errbound)) return det; + + const T adxtail = detail::ExpansionBase<T>::MinusTail(pa[0], pd[0], adx); + const T bdxtail = detail::ExpansionBase<T>::MinusTail(pb[0], pd[0], bdx); + const T cdxtail = detail::ExpansionBase<T>::MinusTail(pc[0], pd[0], cdx); + const T adytail = detail::ExpansionBase<T>::MinusTail(pa[1], pd[1], ady); + const T bdytail = detail::ExpansionBase<T>::MinusTail(pb[1], pd[1], bdy); + const T cdytail = detail::ExpansionBase<T>::MinusTail(pc[1], pd[1], cdy); + const T adztail = detail::ExpansionBase<T>::MinusTail(pa[2], pd[2], adz); + const T bdztail = detail::ExpansionBase<T>::MinusTail(pb[2], pd[2], bdz); + const T cdztail = detail::ExpansionBase<T>::MinusTail(pc[2], pd[2], cdz); + if(T(0) == adxtail && T(0) == adytail && T(0) == adztail && + T(0) == bdxtail && T(0) == bdytail && T(0) == bdztail && + T(0) == cdxtail && T(0) == cdytail && T(0) == cdztail) return det; + + errbound = Constants<T>::o3derrboundC * permanent + Constants<T>::resulterrbound * std::abs(det); + det += (adz * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) + adztail * (bdx * cdy - bdy * cdx)) + + (bdz * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) + bdztail * (cdx * ady - cdy * adx)) + + (cdz * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) + cdztail * (adx * bdy - ady * bdx)); + if(std::abs(det) >= std::abs(errbound)) return det; + + const detail::Expansion<T, 8> bct = detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(bdxtail, cdy, bdytail, cdx) + detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(cdytail, bdx, cdxtail, bdy); + const detail::Expansion<T, 8> cat = detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(cdxtail, ady, cdytail, adx) + detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(adytail, cdx, adxtail, cdy); + const detail::Expansion<T, 8> abt = detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(adxtail, bdy, adytail, bdx) + detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(bdytail, adx, bdxtail, ady); + const detail::Expansion<T, 192> fin2 = fin1 + bct * adz + cat * bdz + abt * cdz + bc * adztail + ca * bdztail + ab * cdztail + + detail::ExpansionBase<T>::ThreeProd( adxtail, bdytail, cdz) + detail::ExpansionBase<T>::ThreeProd( adxtail, bdytail, cdztail) + + detail::ExpansionBase<T>::ThreeProd(-adxtail, cdytail, bdz) + detail::ExpansionBase<T>::ThreeProd(-adxtail, cdytail, bdztail) + + detail::ExpansionBase<T>::ThreeProd( bdxtail, cdytail, adz) + detail::ExpansionBase<T>::ThreeProd( bdxtail, cdytail, adztail) + + detail::ExpansionBase<T>::ThreeProd(-bdxtail, adytail, cdz) + detail::ExpansionBase<T>::ThreeProd(-bdxtail, adytail, cdztail) + + detail::ExpansionBase<T>::ThreeProd( cdxtail, adytail, bdz) + detail::ExpansionBase<T>::ThreeProd( cdxtail, adytail, bdztail) + + detail::ExpansionBase<T>::ThreeProd(-cdxtail, bdytail, adz) + detail::ExpansionBase<T>::ThreeProd(-cdxtail, bdytail, adztail) + + bct * adztail + cat * bdztail + abt * cdztail; + return fin2.mostSignificant(); + } + + //@brief : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d + //@param pa: pointer to a as {x, y, z} + //@param pb: pointer to b as {x, y, z} + //@param pc: pointer to c as {x, y, z} + //@param pd: pointer to d as {x, y, z} + //@param pe: pointer to e as {x, y, z} + //@return : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}} + //@note : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c + template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe) { + T permanent; + const T aex = pa[0] - pe[0]; + const T bex = pb[0] - pe[0]; + const T cex = pc[0] - pe[0]; + const T dex = pd[0] - pe[0]; + const T aey = pa[1] - pe[1]; + const T bey = pb[1] - pe[1]; + const T cey = pc[1] - pe[1]; + const T dey = pd[1] - pe[1]; + const T aez = pa[2] - pe[2]; + const T bez = pb[2] - pe[2]; + const T cez = pc[2] - pe[2]; + const T dez = pd[2] - pe[2]; + { + const T aexbey = aex * bey; + const T bexaey = bex * aey; + const T bexcey = bex * cey; + const T cexbey = cex * bey; + const T cexdey = cex * dey; + const T dexcey = dex * cey; + const T dexaey = dex * aey; + const T aexdey = aex * dey; + const T aexcey = aex * cey; + const T cexaey = cex * aey; + const T bexdey = bex * dey; + const T dexbey = dex * bey; + const T ab = aexbey - bexaey; + const T bc = bexcey - cexbey; + const T cd = cexdey - dexcey; + const T da = dexaey - aexdey; + const T ac = aexcey - cexaey; + const T bd = bexdey - dexbey; + const T abc = aez * bc - bez * ac + cez * ab; + const T bcd = bez * cd - cez * bd + dez * bc; + const T cda = cez * da + dez * ac + aez * cd; + const T dab = dez * ab + aez * bd + bez * da; + const T alift = aex * aex + aey * aey + aez * aez; + const T blift = bex * bex + bey * bey + bez * bez; + const T clift = cex * cex + cey * cey + cez * cez; + const T dlift = dex * dex + dey * dey + dez * dez; + const T det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd); + const T aezplus = std::abs(aez); + const T bezplus = std::abs(bez); + const T cezplus = std::abs(cez); + const T dezplus = std::abs(dez); + const T aexbeyplus = std::abs(aexbey); + const T bexaeyplus = std::abs(bexaey); + const T bexceyplus = std::abs(bexcey); + const T cexbeyplus = std::abs(cexbey); + const T cexdeyplus = std::abs(cexdey); + const T dexceyplus = std::abs(dexcey); + const T dexaeyplus = std::abs(dexaey); + const T aexdeyplus = std::abs(aexdey); + const T aexceyplus = std::abs(aexcey); + const T cexaeyplus = std::abs(cexaey); + const T bexdeyplus = std::abs(bexdey); + const T dexbeyplus = std::abs(dexbey); + permanent = ((cexdeyplus + dexceyplus) * bezplus + (dexbeyplus + bexdeyplus) * cezplus + (bexceyplus + cexbeyplus) * dezplus) * alift + + ((dexaeyplus + aexdeyplus) * cezplus + (aexceyplus + cexaeyplus) * dezplus + (cexdeyplus + dexceyplus) * aezplus) * blift + + ((aexbeyplus + bexaeyplus) * dezplus + (bexdeyplus + dexbeyplus) * aezplus + (dexaeyplus + aexdeyplus) * bezplus) * clift + + ((bexceyplus + cexbeyplus) * aezplus + (cexaeyplus + aexceyplus) * bezplus + (aexbeyplus + bexaeyplus) * cezplus) * dlift; + const T errbound = Constants<T>::isperrboundA * permanent; + if(std::abs(det) >= std::abs(errbound)) return det; + } + + const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(aex, bey, bex, aey); + const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bex, cey, cex, bey); + const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(cex, dey, dex, cey); + const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(dex, aey, aex, dey); + const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(aex, cey, cex, aey); + const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(bex, dey, dex, bey); + const detail::Expansion<T, 24> temp24a = bc * dez + (cd * bez + bd * -cez); + const detail::Expansion<T, 24> temp24b = cd * aez + (da * cez + ac * dez); + const detail::Expansion<T, 24> temp24c = da * bez + (ab * dez + bd * aez); + const detail::Expansion<T, 24> temp24d = ab * cez + (bc * aez + ac * -bez); + const detail::Expansion<T, 288> adet = temp24a * aex * -aex + temp24a * aey * -aey + temp24a * aez * -aez; + const detail::Expansion<T, 288> bdet = temp24b * bex * bex + temp24b * bey * bey + temp24b * bez * bez; + const detail::Expansion<T, 288> cdet = temp24c * cex * -cex + temp24c * cey * -cey + temp24c * cez * -cez; + const detail::Expansion<T, 288> ddet = temp24d * dex * dex + temp24d * dey * dey + temp24d * dez * dez; + const detail::Expansion<T, 1152> fin1 = (adet + bdet) + (cdet + ddet); + T det = fin1.estimate(); + T errbound = Constants<T>::isperrboundB * permanent; + if(std::abs(det) >= std::abs(errbound)) return det; + + const T aextail = detail::ExpansionBase<T>::MinusTail(pa[0], pe[0], aex); + const T aeytail = detail::ExpansionBase<T>::MinusTail(pa[1], pe[1], aey); + const T aeztail = detail::ExpansionBase<T>::MinusTail(pa[2], pe[2], aez); + const T bextail = detail::ExpansionBase<T>::MinusTail(pb[0], pe[0], bex); + const T beytail = detail::ExpansionBase<T>::MinusTail(pb[1], pe[1], bey); + const T beztail = detail::ExpansionBase<T>::MinusTail(pb[2], pe[2], bez); + const T cextail = detail::ExpansionBase<T>::MinusTail(pc[0], pe[0], cex); + const T ceytail = detail::ExpansionBase<T>::MinusTail(pc[1], pe[1], cey); + const T ceztail = detail::ExpansionBase<T>::MinusTail(pc[2], pe[2], cez); + const T dextail = detail::ExpansionBase<T>::MinusTail(pd[0], pe[0], dex); + const T deytail = detail::ExpansionBase<T>::MinusTail(pd[1], pe[1], dey); + const T deztail = detail::ExpansionBase<T>::MinusTail(pd[2], pe[2], dez); + if (T(0) == aextail && T(0) == aeytail && T(0) == aeztail && + T(0) == bextail && T(0) == beytail && T(0) == beztail && + T(0) == cextail && T(0) == ceytail && T(0) == ceztail && + T(0) == dextail && T(0) == deytail && T(0) == deztail) return det; + + errbound = Constants<T>::isperrboundC * permanent + Constants<T>::resulterrbound * std::abs(det); + const T abeps = (aex * beytail + bey * aextail) - (aey * bextail + bex * aeytail); + const T bceps = (bex * ceytail + cey * bextail) - (bey * cextail + cex * beytail); + const T cdeps = (cex * deytail + dey * cextail) - (cey * dextail + dex * ceytail); + const T daeps = (dex * aeytail + aey * dextail) - (dey * aextail + aex * deytail); + const T aceps = (aex * ceytail + cey * aextail) - (aey * cextail + cex * aeytail); + const T bdeps = (bex * deytail + dey * bextail) - (bey * dextail + dex * beytail); + const T ab3 = ab.mostSignificant(); + const T bc3 = bc.mostSignificant(); + const T cd3 = cd.mostSignificant(); + const T da3 = da.mostSignificant(); + const T ac3 = ac.mostSignificant(); + const T bd3 = bd.mostSignificant(); + det += ( ( (bex * bex + bey * bey + bez * bez) * ((cez * daeps + dez * aceps + aez * cdeps) + (ceztail * da3 + deztail * ac3 + aeztail * cd3)) + + (dex * dex + dey * dey + dez * dez) * ((aez * bceps - bez * aceps + cez * abeps) + (aeztail * bc3 - beztail * ac3 + ceztail * ab3)) ) + - ( (aex * aex + aey * aey + aez * aez) * ((bez * cdeps - cez * bdeps + dez * bceps) + (beztail * cd3 - ceztail * bd3 + deztail * bc3)) + + (cex * cex + cey * cey + cez * cez) * ((dez * abeps + aez * bdeps + bez * daeps) + (deztail * ab3 + aeztail * bd3 + beztail * da3)) ) ) + + T(2) * ( ( (bex * bextail + bey * beytail + bez * beztail) * (cez * da3 + dez * ac3 + aez * cd3) + + (dex * dextail + dey * deytail + dez * deztail) * (aez * bc3 - bez * ac3 + cez * ab3)) + - ( (aex * aextail + aey * aeytail + aez * aeztail) * (bez * cd3 - cez * bd3 + dez * bc3) + + (cex * cextail + cey * ceytail + cez * ceztail) * (dez * ab3 + aez * bd3 + bez * da3))); + if(std::abs(det) >= std::abs(errbound)) return det; + return exact::insphere(pa, pb, pc, pd, pe); + } + } +} + +#endif
hcdt.cabal view
@@ -1,53 +1,65 @@-cabal-version: 2.2-name: hcdt-version: 0.1.0.5-license: BSD-3-Clause-license-file: LICENSE-copyright: 2022 Stéphane Laurent-maintainer: laurent_step@outlook.fr-author: Stéphane Laurent-homepage: https://github.com/githubuser/hcdt#readme-synopsis: 2d Delaunay triangulation-description:- This library performs the constrained or unconstrained 2d Delaunay triangulation.- .- It uses the C++ library 'CDT'.- .- For examples, look the README file.--category: Geometry-build-type: Simple-extra-source-files:- README.md- CHANGELOG.md- LICENSE_CDT.txt--source-repository head- type: git- location: https://github.com/githubuser/hcdt--library- exposed-modules: Geometry.HCDT- cxx-options: -fPIC -std=c++11- c-sources: cpp/hcdt.cpp- hs-source-dirs: src- other-modules:- Geometry.HCDT.Types- Geometry.HCDT.Triangulation- Geometry.HCDT.Delaunay- Geometry.HCDT.Utils-- default-language: Haskell2010- other-extensions: CPP ForeignFunctionInterface- extra-libraries: stdc++- include-dirs: cpp- install-includes:- cpp/CDT.h cpp/CDT.hpp cpp/CDTUtils.h cpp/CDTUtils.hpp cpp/hcdt.hpp- cpp/KDTree.h cpp/LocatorKDTree.h cpp/predicates.h cpp/remove_at.hpp- cpp/Triangulation.h cpp/Triangulation.hpp-- ghc-options: -Wall -optcxx-std=c++11- build-depends:- base >=4.7 && <5,- containers >=0.6.4.1,- indexed-traversable >=0.1.2+cabal-version: 2.2 +name: hcdt +version: 0.1.1.0 +synopsis: 2d Delaunay triangulation +description: + This library performs the constrained or unconstrained 2d Delaunay triangulation. + . + It uses the C++ library 'CDT'. + . + For examples, look the README file. +homepage: https://github.com/stla/hcdt#readme +license: BSD-3-Clause +license-file: LICENSE +author: Stéphane Laurent +maintainer: laurent_step@outlook.fr +copyright: 2022 Stéphane Laurent +category: Geometry +build-type: Simple +extra-source-files: README.md + CHANGELOG.md + LICENSE_CDT.txt + +library + hs-source-dirs: src + exposed-modules: Geometry.HCDT + other-modules: Geometry.HCDT.Types + , Geometry.HCDT.Triangulation + , Geometry.HCDT.Delaunay + , Geometry.HCDT.Utils + build-depends: base >= 4.7 && < 5 + , containers >= 0.6.4.1 + , indexed-traversable >= 0.1.2 + , hashable >= 1.3.5.0 + , witherable >= 0.4 + if impl(ghc >= 9.4) + build-depends: system-cxx-std-lib == 1.0 + elif os(darwin) || os(freebsd) + extra-libraries: c++11 + else + extra-libraries: stdc++ + include-dirs: cpp + C-sources: cpp/hcdt.cpp + install-includes: cpp/CDT.h + , cpp/CDT.hpp + , cpp/CDTUtils.h + , cpp/CDTUtils.hpp + , cpp/hcdt.hpp + , cpp/KDTree.h + , cpp/LocatorKDTree.h + , cpp/predicates.h + , cpp/remove_at.hpp + , cpp/Triangulation.h + , cpp/Triangulation.hpp + ghc-options: -Wall + -optcxx-std=c++11 + if os(darwin) || os(freebsd) + ghc-options: -optcxx-stdlib=libc++ + other-extensions: CPP + , ForeignFunctionInterface + , InstanceSigs + default-language: Haskell2010 + +source-repository head + type: git + location: https://github.com/stla/hcdt
src/Geometry/HCDT.hs view
@@ -1,6 +1,6 @@-module Geometry.HCDT- (module X)- where-import Geometry.HCDT.Delaunay as X-import Geometry.HCDT.Types as X+module Geometry.HCDT + (module X) + where +import Geometry.HCDT.Delaunay as X +import Geometry.HCDT.Types as X import Geometry.HCDT.Utils as X
src/Geometry/HCDT/Delaunay.hs view
@@ -1,42 +1,42 @@-module Geometry.HCDT.Delaunay- ( delaunay,- cdelaunay- )-where--import Foreign.Marshal.Alloc (free, mallocBytes)-import Foreign.Marshal.Array (pokeArray)-import Foreign.Storable (peek, sizeOf)-import Geometry.HCDT.Triangulation-import Geometry.HCDT.Types (Triangulation, Vertex, ConstrainedTriangulation, Edge)---- | Unconstrained 2d Delaunay triangulation.-delaunay :: [Vertex] -> IO Triangulation-delaunay vertices = do- let nvertices = length vertices- verticesPtr <- mallocBytes (nvertices * sizeOf (undefined :: CVertex))- cvertices <- mapM vertexToCVertex vertices- pokeArray verticesPtr cvertices- ctriangulationPtr <- c_delaunay verticesPtr (fromIntegral nvertices)- ctriangulation <- peek ctriangulationPtr - free verticesPtr- free ctriangulationPtr- cTriangulationToTriangulation ctriangulation---- | Constrained 2d Delaunay triangulation.-cdelaunay :: [Vertex] -> [Edge] -> IO ConstrainedTriangulation-cdelaunay vertices edges = do- let nvertices = length vertices- verticesPtr <- mallocBytes (nvertices * sizeOf (undefined :: CVertex))- cvertices <- mapM vertexToCVertex vertices- pokeArray verticesPtr cvertices- let nedges = length edges- edgesPtr <- mallocBytes (nedges * sizeOf (undefined :: CEdge))- cedges <- mapM edgeToCEdge edges- pokeArray edgesPtr cedges- cctriangulationPtr <- c_cdelaunay verticesPtr (fromIntegral nvertices) edgesPtr (fromIntegral nedges)- cctriangulation <- peek cctriangulationPtr - free verticesPtr- free edgesPtr- free cctriangulationPtr- cCTriangulationToConstrainedTriangulation cctriangulation+module Geometry.HCDT.Delaunay + ( delaunay, + cdelaunay + ) +where + +import Foreign.Marshal.Alloc (free, mallocBytes) +import Foreign.Marshal.Array (pokeArray) +import Foreign.Storable (peek, sizeOf) +import Geometry.HCDT.Triangulation +import Geometry.HCDT.Types (Triangulation, Vertex, ConstrainedTriangulation, Edge) + +-- | Unconstrained 2d Delaunay triangulation. +delaunay :: [Vertex] -> IO Triangulation +delaunay vertices = do + let nvertices = length vertices + verticesPtr <- mallocBytes (nvertices * sizeOf (undefined :: CVertex)) + cvertices <- mapM vertexToCVertex vertices + pokeArray verticesPtr cvertices + ctriangulationPtr <- c_delaunay verticesPtr (fromIntegral nvertices) + ctriangulation <- peek ctriangulationPtr + free verticesPtr + free ctriangulationPtr + cTriangulationToTriangulation ctriangulation + +-- | Constrained 2d Delaunay triangulation. +cdelaunay :: [Vertex] -> [Edge] -> IO ConstrainedTriangulation +cdelaunay vertices edges = do + let nvertices = length vertices + verticesPtr <- mallocBytes (nvertices * sizeOf (undefined :: CVertex)) + cvertices <- mapM vertexToCVertex vertices + pokeArray verticesPtr cvertices + let nedges = length edges + edgesPtr <- mallocBytes (nedges * sizeOf (undefined :: CEdge)) + cedges <- mapM edgeToCEdge edges + pokeArray edgesPtr cedges + cctriangulationPtr <- c_cdelaunay verticesPtr (fromIntegral nvertices) edgesPtr (fromIntegral nedges) + cctriangulation <- peek cctriangulationPtr + free verticesPtr + free edgesPtr + free cctriangulationPtr + cCTriangulationToConstrainedTriangulation cctriangulation
src/Geometry/HCDT/Triangulation.hs view
@@ -1,277 +1,277 @@-{-# LINE 1 "Triangulation.hsc" #-}-{-# LANGUAGE ForeignFunctionInterface #-}-{-# LANGUAGE CPP #-}-module Geometry.HCDT.Triangulation-( cTriangulationToTriangulation-, cCTriangulationToConstrainedTriangulation-, vertexToCVertex-, edgeToCEdge-, c_delaunay -, c_cdelaunay-, CTriangulation (..)-, CVertex (..)-, CCTriangulation (..)-, CEdge (..) )- where-import Geometry.HCDT.Types-import Foreign-import Foreign.C.Types-import qualified Data.IntMap.Strict as IM----data CVertex = CVertex {- __x :: CDouble- , __y :: CDouble-}--instance Storable CVertex where- sizeOf __ = (16)-{-# LINE 29 "Triangulation.hsc" #-}- alignment __ = 8-{-# LINE 30 "Triangulation.hsc" #-}- peek ptr = do- x' <- (`peekByteOff` 0) ptr-{-# LINE 32 "Triangulation.hsc" #-}- y' <- (`peekByteOff` 8) ptr-{-# LINE 33 "Triangulation.hsc" #-}- return CVertex { __x = x', __y = y' }- poke ptr (CVertex r1 r2)- = do- (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1-{-# LINE 37 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2-{-# LINE 38 "Triangulation.hsc" #-}--cVertexToVertex :: CVertex -> IO Vertex-cVertexToVertex cvertex = do- let x = realToFrac $ __x cvertex- let y = realToFrac $ __y cvertex- return $ Vertex x y--vertexToCVertex :: Vertex -> IO CVertex-vertexToCVertex (Vertex x y) = do- return $ CVertex { __x = realToFrac x, __y = realToFrac y }--data CEdge = CEdge {- __i :: CUInt- , __j :: CUInt-}--instance Storable CEdge where- sizeOf __ = (8)-{-# LINE 56 "Triangulation.hsc" #-}- alignment __ = 4-{-# LINE 57 "Triangulation.hsc" #-}- peek ptr = do- i' <- (`peekByteOff` 0) ptr-{-# LINE 59 "Triangulation.hsc" #-}- j' <- (`peekByteOff` 4) ptr-{-# LINE 60 "Triangulation.hsc" #-}- return CEdge { __i = i'- , __j = j' }- poke ptr (CEdge r1 r2)- = do- (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1-{-# LINE 65 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 4) ptr r2-{-# LINE 66 "Triangulation.hsc" #-}--cEdgeToEdge :: CEdge -> IO Edge-cEdgeToEdge cedge = do- let i = fromIntegral $ __i cedge- let j = fromIntegral $ __j cedge- return $ Edge i j--edgeToCEdge :: Edge -> IO CEdge-edgeToCEdge (Edge i j) = do- return $ CEdge { __i = fromIntegral i, __j = fromIntegral j }--data CTriangle = CTriangle {- __i1 :: CUInt- , __i2 :: CUInt- , __i3 :: CUInt-}--instance Storable CTriangle where- sizeOf __ = (12)-{-# LINE 85 "Triangulation.hsc" #-}- alignment __ = 4-{-# LINE 86 "Triangulation.hsc" #-}- peek ptr = do- i1' <- (`peekByteOff` 0) ptr-{-# LINE 88 "Triangulation.hsc" #-}- i2' <- (`peekByteOff` 4) ptr-{-# LINE 89 "Triangulation.hsc" #-}- i3' <- (`peekByteOff` 8) ptr-{-# LINE 90 "Triangulation.hsc" #-}- return CTriangle { __i1 = i1'- , __i2 = i2'- , __i3 = i3' }- poke ptr (CTriangle r1 r2 r3)- = do- (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1-{-# LINE 96 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 4) ptr r2-{-# LINE 97 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r3-{-# LINE 98 "Triangulation.hsc" #-}--cTriangleToTriangle :: CTriangle -> IO Triangle-cTriangleToTriangle ctriangle = do- let i1 = fromIntegral $ __i1 ctriangle- let i2 = fromIntegral $ __i2 ctriangle- let i3 = fromIntegral $ __i3 ctriangle- return $ Triangle i1 i2 i3--data CTriangulation = CTriangulation {- __vertices :: Ptr CVertex- , __nvertices :: CSize- , __triangles :: Ptr CTriangle- , __ntriangles :: CSize- , __edges :: Ptr CEdge- , __nedges :: CSize-}--instance Storable CTriangulation where- sizeOf __ = (48)-{-# LINE 117 "Triangulation.hsc" #-}- alignment __ = 8-{-# LINE 118 "Triangulation.hsc" #-}- peek ptr = do- vs <- (`peekByteOff` 0) ptr-{-# LINE 120 "Triangulation.hsc" #-}- nvs <- (`peekByteOff` 8) ptr-{-# LINE 121 "Triangulation.hsc" #-}- ts <- (`peekByteOff` 16) ptr-{-# LINE 122 "Triangulation.hsc" #-}- nts <- (`peekByteOff` 24) ptr-{-# LINE 123 "Triangulation.hsc" #-}- es <- (`peekByteOff` 32) ptr-{-# LINE 124 "Triangulation.hsc" #-}- nes <- (`peekByteOff` 40) ptr-{-# LINE 125 "Triangulation.hsc" #-}- return CTriangulation { __vertices = vs- , __nvertices = nvs- , __triangles = ts- , __ntriangles = nts- , __edges = es- , __nedges = nes }- poke ptr (CTriangulation r1 r2 r3 r4 r5 r6)- = do- (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1-{-# LINE 134 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2-{-# LINE 135 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r3-{-# LINE 136 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4-{-# LINE 137 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r5-{-# LINE 138 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r6-{-# LINE 139 "Triangulation.hsc" #-}--data CCTriangulation = CCTriangulation {- __vertices' :: Ptr CVertex- , __nvertices' :: CSize- , __triangles' :: Ptr CTriangle- , __ntriangles' :: CSize- , __edges' :: Ptr CEdge- , __nedges' :: CSize- , __fixededges' :: Ptr CEdge- , __nfixededges' :: CSize-}--instance Storable CCTriangulation where- sizeOf __ = (64)-{-# LINE 153 "Triangulation.hsc" #-}- alignment __ = 8-{-# LINE 154 "Triangulation.hsc" #-}- peek ptr = do- vs <- (`peekByteOff` 0) ptr-{-# LINE 156 "Triangulation.hsc" #-}- nvs <- (`peekByteOff` 8) ptr-{-# LINE 157 "Triangulation.hsc" #-}- ts <- (`peekByteOff` 16) ptr-{-# LINE 158 "Triangulation.hsc" #-}- nts <- (`peekByteOff` 24) ptr-{-# LINE 159 "Triangulation.hsc" #-}- es <- (`peekByteOff` 32) ptr-{-# LINE 160 "Triangulation.hsc" #-}- nes <- (`peekByteOff` 40) ptr-{-# LINE 161 "Triangulation.hsc" #-}- fes <- (`peekByteOff` 48) ptr-{-# LINE 162 "Triangulation.hsc" #-}- nfes <- (`peekByteOff` 56) ptr-{-# LINE 163 "Triangulation.hsc" #-}- return CCTriangulation { __vertices' = vs- , __nvertices' = nvs- , __triangles' = ts- , __ntriangles' = nts- , __edges' = es- , __nedges' = nes - , __fixededges' = fes- , __nfixededges' = nfes }- poke ptr (CCTriangulation r1 r2 r3 r4 r5 r6 r7 r8)- = do- (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1-{-# LINE 174 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2-{-# LINE 175 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r3-{-# LINE 176 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4-{-# LINE 177 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r5-{-# LINE 178 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r6-{-# LINE 179 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr r7-{-# LINE 180 "Triangulation.hsc" #-}- (\hsc_ptr -> pokeByteOff hsc_ptr 56) ptr r8-{-# LINE 181 "Triangulation.hsc" #-}--cTriangulationToTriangulation :: CTriangulation -> IO Triangulation-cTriangulationToTriangulation ctriangulation = do- let nvertices = fromIntegral $ __nvertices ctriangulation- ntriangles = fromIntegral $ __ntriangles ctriangulation- nedges = fromIntegral $ __nedges ctriangulation- vertices <- peekArray nvertices (__vertices ctriangulation)- triangles <- peekArray ntriangles (__triangles ctriangulation)- edges <- peekArray nedges (__edges ctriangulation)- vertices' <- mapM cVertexToVertex vertices- triangles' <- mapM cTriangleToTriangle triangles- edges' <- mapM cEdgeToEdge edges- return $ Triangulation { _vertices = IM.fromAscList (zip [0 .. nvertices-1] vertices')- , _triangles = triangles'- , _edges = edges' }--cCTriangulationToConstrainedTriangulation :: CCTriangulation -> IO ConstrainedTriangulation-cCTriangulationToConstrainedTriangulation cctriangulation = do- let nvertices = fromIntegral $ __nvertices' cctriangulation- ntriangles = fromIntegral $ __ntriangles' cctriangulation- nedges = fromIntegral $ __nedges' cctriangulation- nfedges = fromIntegral $ __nfixededges' cctriangulation- vertices <- peekArray nvertices (__vertices' cctriangulation)- triangles <- peekArray ntriangles (__triangles' cctriangulation)- edges <- peekArray nedges (__edges' cctriangulation)- fedges <- peekArray nfedges (__fixededges' cctriangulation)- vertices' <- mapM cVertexToVertex vertices- triangles' <- mapM cTriangleToTriangle triangles- edges' <- mapM cEdgeToEdge edges- fedges' <- mapM cEdgeToEdge fedges- let triangulation = Triangulation { - _vertices = IM.fromAscList (zip [0 .. nvertices-1] vertices')- , _triangles = triangles'- , _edges = edges' - }- return ConstrainedTriangulation { - _triangulation = triangulation- , _fixedEdges = fedges' }--foreign import ccall unsafe "delaunay" c_delaunay- :: Ptr CVertex -> CSize -> IO (Ptr CTriangulation)--foreign import ccall unsafe "cdelaunay" c_cdelaunay- :: Ptr CVertex -> CSize -> Ptr CEdge -> CSize -> IO (Ptr CCTriangulation)+{-# LINE 1 "Triangulation.hsc" #-} +{-# LANGUAGE ForeignFunctionInterface #-} +{-# LANGUAGE CPP #-} +module Geometry.HCDT.Triangulation +( cTriangulationToTriangulation +, cCTriangulationToConstrainedTriangulation +, vertexToCVertex +, edgeToCEdge +, c_delaunay +, c_cdelaunay +, CTriangulation (..) +, CVertex (..) +, CCTriangulation (..) +, CEdge (..) ) + where +import Geometry.HCDT.Types +import Foreign +import Foreign.C.Types +import qualified Data.IntMap.Strict as IM + + + +data CVertex = CVertex { + __x :: CDouble + , __y :: CDouble +} + +instance Storable CVertex where + sizeOf __ = (16) +{-# LINE 29 "Triangulation.hsc" #-} + alignment __ = 8 +{-# LINE 30 "Triangulation.hsc" #-} + peek ptr = do + x' <- (`peekByteOff` 0) ptr +{-# LINE 32 "Triangulation.hsc" #-} + y' <- (`peekByteOff` 8) ptr +{-# LINE 33 "Triangulation.hsc" #-} + return CVertex { __x = x', __y = y' } + poke ptr (CVertex r1 r2) + = do + (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1 +{-# LINE 37 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2 +{-# LINE 38 "Triangulation.hsc" #-} + +cVertexToVertex :: CVertex -> IO Vertex +cVertexToVertex cvertex = do + let x = realToFrac $ __x cvertex + let y = realToFrac $ __y cvertex + return $ Vertex x y + +vertexToCVertex :: Vertex -> IO CVertex +vertexToCVertex (Vertex x y) = do + return $ CVertex { __x = realToFrac x, __y = realToFrac y } + +data CEdge = CEdge { + __i :: CUInt + , __j :: CUInt +} + +instance Storable CEdge where + sizeOf __ = (8) +{-# LINE 56 "Triangulation.hsc" #-} + alignment __ = 4 +{-# LINE 57 "Triangulation.hsc" #-} + peek ptr = do + i' <- (`peekByteOff` 0) ptr +{-# LINE 59 "Triangulation.hsc" #-} + j' <- (`peekByteOff` 4) ptr +{-# LINE 60 "Triangulation.hsc" #-} + return CEdge { __i = i' + , __j = j' } + poke ptr (CEdge r1 r2) + = do + (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1 +{-# LINE 65 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 4) ptr r2 +{-# LINE 66 "Triangulation.hsc" #-} + +cEdgeToEdge :: CEdge -> IO Edge +cEdgeToEdge cedge = do + let i = fromIntegral $ __i cedge + let j = fromIntegral $ __j cedge + return $ Edge i j + +edgeToCEdge :: Edge -> IO CEdge +edgeToCEdge (Edge i j) = do + return $ CEdge { __i = fromIntegral i, __j = fromIntegral j } + +data CTriangle = CTriangle { + __i1 :: CUInt + , __i2 :: CUInt + , __i3 :: CUInt +} + +instance Storable CTriangle where + sizeOf __ = (12) +{-# LINE 85 "Triangulation.hsc" #-} + alignment __ = 4 +{-# LINE 86 "Triangulation.hsc" #-} + peek ptr = do + i1' <- (`peekByteOff` 0) ptr +{-# LINE 88 "Triangulation.hsc" #-} + i2' <- (`peekByteOff` 4) ptr +{-# LINE 89 "Triangulation.hsc" #-} + i3' <- (`peekByteOff` 8) ptr +{-# LINE 90 "Triangulation.hsc" #-} + return CTriangle { __i1 = i1' + , __i2 = i2' + , __i3 = i3' } + poke ptr (CTriangle r1 r2 r3) + = do + (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1 +{-# LINE 96 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 4) ptr r2 +{-# LINE 97 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r3 +{-# LINE 98 "Triangulation.hsc" #-} + +cTriangleToTriangle :: CTriangle -> IO Triangle +cTriangleToTriangle ctriangle = do + let i1 = fromIntegral $ __i1 ctriangle + let i2 = fromIntegral $ __i2 ctriangle + let i3 = fromIntegral $ __i3 ctriangle + return $ Triangle i1 i2 i3 + +data CTriangulation = CTriangulation { + __vertices :: Ptr CVertex + , __nvertices :: CSize + , __triangles :: Ptr CTriangle + , __ntriangles :: CSize + , __edges :: Ptr CEdge + , __nedges :: CSize +} + +instance Storable CTriangulation where + sizeOf __ = (48) +{-# LINE 117 "Triangulation.hsc" #-} + alignment __ = 8 +{-# LINE 118 "Triangulation.hsc" #-} + peek ptr = do + vs <- (`peekByteOff` 0) ptr +{-# LINE 120 "Triangulation.hsc" #-} + nvs <- (`peekByteOff` 8) ptr +{-# LINE 121 "Triangulation.hsc" #-} + ts <- (`peekByteOff` 16) ptr +{-# LINE 122 "Triangulation.hsc" #-} + nts <- (`peekByteOff` 24) ptr +{-# LINE 123 "Triangulation.hsc" #-} + es <- (`peekByteOff` 32) ptr +{-# LINE 124 "Triangulation.hsc" #-} + nes <- (`peekByteOff` 40) ptr +{-# LINE 125 "Triangulation.hsc" #-} + return CTriangulation { __vertices = vs + , __nvertices = nvs + , __triangles = ts + , __ntriangles = nts + , __edges = es + , __nedges = nes } + poke ptr (CTriangulation r1 r2 r3 r4 r5 r6) + = do + (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1 +{-# LINE 134 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2 +{-# LINE 135 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r3 +{-# LINE 136 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4 +{-# LINE 137 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r5 +{-# LINE 138 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r6 +{-# LINE 139 "Triangulation.hsc" #-} + +data CCTriangulation = CCTriangulation { + __vertices' :: Ptr CVertex + , __nvertices' :: CSize + , __triangles' :: Ptr CTriangle + , __ntriangles' :: CSize + , __edges' :: Ptr CEdge + , __nedges' :: CSize + , __fixededges' :: Ptr CEdge + , __nfixededges' :: CSize +} + +instance Storable CCTriangulation where + sizeOf __ = (64) +{-# LINE 153 "Triangulation.hsc" #-} + alignment __ = 8 +{-# LINE 154 "Triangulation.hsc" #-} + peek ptr = do + vs <- (`peekByteOff` 0) ptr +{-# LINE 156 "Triangulation.hsc" #-} + nvs <- (`peekByteOff` 8) ptr +{-# LINE 157 "Triangulation.hsc" #-} + ts <- (`peekByteOff` 16) ptr +{-# LINE 158 "Triangulation.hsc" #-} + nts <- (`peekByteOff` 24) ptr +{-# LINE 159 "Triangulation.hsc" #-} + es <- (`peekByteOff` 32) ptr +{-# LINE 160 "Triangulation.hsc" #-} + nes <- (`peekByteOff` 40) ptr +{-# LINE 161 "Triangulation.hsc" #-} + fes <- (`peekByteOff` 48) ptr +{-# LINE 162 "Triangulation.hsc" #-} + nfes <- (`peekByteOff` 56) ptr +{-# LINE 163 "Triangulation.hsc" #-} + return CCTriangulation { __vertices' = vs + , __nvertices' = nvs + , __triangles' = ts + , __ntriangles' = nts + , __edges' = es + , __nedges' = nes + , __fixededges' = fes + , __nfixededges' = nfes } + poke ptr (CCTriangulation r1 r2 r3 r4 r5 r6 r7 r8) + = do + (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1 +{-# LINE 174 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2 +{-# LINE 175 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r3 +{-# LINE 176 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4 +{-# LINE 177 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r5 +{-# LINE 178 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r6 +{-# LINE 179 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr r7 +{-# LINE 180 "Triangulation.hsc" #-} + (\hsc_ptr -> pokeByteOff hsc_ptr 56) ptr r8 +{-# LINE 181 "Triangulation.hsc" #-} + +cTriangulationToTriangulation :: CTriangulation -> IO Triangulation +cTriangulationToTriangulation ctriangulation = do + let nvertices = fromIntegral $ __nvertices ctriangulation + ntriangles = fromIntegral $ __ntriangles ctriangulation + nedges = fromIntegral $ __nedges ctriangulation + vertices <- peekArray nvertices (__vertices ctriangulation) + triangles <- peekArray ntriangles (__triangles ctriangulation) + edges <- peekArray nedges (__edges ctriangulation) + vertices' <- mapM cVertexToVertex vertices + triangles' <- mapM cTriangleToTriangle triangles + edges' <- mapM cEdgeToEdge edges + return $ Triangulation { _vertices = IM.fromAscList (zip [0 .. nvertices-1] vertices') + , _triangles = triangles' + , _edges = edges' } + +cCTriangulationToConstrainedTriangulation :: CCTriangulation -> IO ConstrainedTriangulation +cCTriangulationToConstrainedTriangulation cctriangulation = do + let nvertices = fromIntegral $ __nvertices' cctriangulation + ntriangles = fromIntegral $ __ntriangles' cctriangulation + nedges = fromIntegral $ __nedges' cctriangulation + nfedges = fromIntegral $ __nfixededges' cctriangulation + vertices <- peekArray nvertices (__vertices' cctriangulation) + triangles <- peekArray ntriangles (__triangles' cctriangulation) + edges <- peekArray nedges (__edges' cctriangulation) + fedges <- peekArray nfedges (__fixededges' cctriangulation) + vertices' <- mapM cVertexToVertex vertices + triangles' <- mapM cTriangleToTriangle triangles + edges' <- mapM cEdgeToEdge edges + fedges' <- mapM cEdgeToEdge fedges + let triangulation = Triangulation { + _vertices = IM.fromAscList (zip [0 .. nvertices-1] vertices') + , _triangles = triangles' + , _edges = edges' + } + return ConstrainedTriangulation { + _triangulation = triangulation + , _fixedEdges = fedges' } + +foreign import ccall unsafe "delaunay" c_delaunay + :: Ptr CVertex -> CSize -> IO (Ptr CTriangulation) + +foreign import ccall unsafe "cdelaunay" c_cdelaunay + :: Ptr CVertex -> CSize -> Ptr CEdge -> CSize -> IO (Ptr CCTriangulation)
+ src/Geometry/HCDT/Triangulation.hsc view
@@ -0,0 +1,224 @@+{-# LANGUAGE ForeignFunctionInterface #-} +{-# LANGUAGE CPP #-} +module Geometry.HCDT.Triangulation +( cTriangulationToTriangulation +, cCTriangulationToConstrainedTriangulation +, vertexToCVertex +, edgeToCEdge +, c_delaunay +, c_cdelaunay +, CTriangulation (..) +, CVertex (..) +, CCTriangulation (..) +, CEdge (..) ) + where +import Geometry.HCDT.Types +import Foreign +import Foreign.C.Types +import qualified Data.IntMap.Strict as IM + +#include "hcdt.hpp" + +data CVertex = CVertex { + __x :: CDouble + , __y :: CDouble +} + +instance Storable CVertex where + sizeOf __ = #{size VertexT} + alignment __ = #{alignment VertexT} + peek ptr = do + x' <- #{peek VertexT, x} ptr + y' <- #{peek VertexT, y} ptr + return CVertex { __x = x', __y = y' } + poke ptr (CVertex r1 r2) + = do + #{poke VertexT, x} ptr r1 + #{poke VertexT, y} ptr r2 + +cVertexToVertex :: CVertex -> IO Vertex +cVertexToVertex cvertex = do + let x = realToFrac $ __x cvertex + let y = realToFrac $ __y cvertex + return $ Vertex x y + +vertexToCVertex :: Vertex -> IO CVertex +vertexToCVertex (Vertex x y) = do + return $ CVertex { __x = realToFrac x, __y = realToFrac y } + +data CEdge = CEdge { + __i :: CUInt + , __j :: CUInt +} + +instance Storable CEdge where + sizeOf __ = #{size EdgeT} + alignment __ = #{alignment EdgeT} + peek ptr = do + i' <- #{peek EdgeT, i} ptr + j' <- #{peek EdgeT, j} ptr + return CEdge { __i = i' + , __j = j' } + poke ptr (CEdge r1 r2) + = do + #{poke EdgeT, i} ptr r1 + #{poke EdgeT, j} ptr r2 + +cEdgeToEdge :: CEdge -> IO Edge +cEdgeToEdge cedge = do + let i = fromIntegral $ __i cedge + let j = fromIntegral $ __j cedge + return $ Edge i j + +edgeToCEdge :: Edge -> IO CEdge +edgeToCEdge (Edge i j) = do + return $ CEdge { __i = fromIntegral i, __j = fromIntegral j } + +data CTriangle = CTriangle { + __i1 :: CUInt + , __i2 :: CUInt + , __i3 :: CUInt +} + +instance Storable CTriangle where + sizeOf __ = #{size TriangleT} + alignment __ = #{alignment TriangleT} + peek ptr = do + i1' <- #{peek TriangleT, i1} ptr + i2' <- #{peek TriangleT, i2} ptr + i3' <- #{peek TriangleT, i3} ptr + return CTriangle { __i1 = i1' + , __i2 = i2' + , __i3 = i3' } + poke ptr (CTriangle r1 r2 r3) + = do + #{poke TriangleT, i1} ptr r1 + #{poke TriangleT, i2} ptr r2 + #{poke TriangleT, i3} ptr r3 + +cTriangleToTriangle :: CTriangle -> IO Triangle +cTriangleToTriangle ctriangle = do + let i1 = fromIntegral $ __i1 ctriangle + let i2 = fromIntegral $ __i2 ctriangle + let i3 = fromIntegral $ __i3 ctriangle + return $ Triangle i1 i2 i3 + +data CTriangulation = CTriangulation { + __vertices :: Ptr CVertex + , __nvertices :: CSize + , __triangles :: Ptr CTriangle + , __ntriangles :: CSize + , __edges :: Ptr CEdge + , __nedges :: CSize +} + +instance Storable CTriangulation where + sizeOf __ = #{size TriangulationT} + alignment __ = #{alignment TriangulationT} + peek ptr = do + vs <- #{peek TriangulationT, vertices} ptr + nvs <- #{peek TriangulationT, nvertices} ptr + ts <- #{peek TriangulationT, triangles} ptr + nts <- #{peek TriangulationT, ntriangles} ptr + es <- #{peek TriangulationT, edges} ptr + nes <- #{peek TriangulationT, nedges} ptr + return CTriangulation { __vertices = vs + , __nvertices = nvs + , __triangles = ts + , __ntriangles = nts + , __edges = es + , __nedges = nes } + poke ptr (CTriangulation r1 r2 r3 r4 r5 r6) + = do + #{poke TriangulationT, vertices} ptr r1 + #{poke TriangulationT, nvertices} ptr r2 + #{poke TriangulationT, triangles} ptr r3 + #{poke TriangulationT, ntriangles} ptr r4 + #{poke TriangulationT, edges} ptr r5 + #{poke TriangulationT, nedges} ptr r6 + +data CCTriangulation = CCTriangulation { + __vertices' :: Ptr CVertex + , __nvertices' :: CSize + , __triangles' :: Ptr CTriangle + , __ntriangles' :: CSize + , __edges' :: Ptr CEdge + , __nedges' :: CSize + , __fixededges' :: Ptr CEdge + , __nfixededges' :: CSize +} + +instance Storable CCTriangulation where + sizeOf __ = #{size CTriangulationT} + alignment __ = #{alignment CTriangulationT} + peek ptr = do + vs <- #{peek CTriangulationT, vertices} ptr + nvs <- #{peek CTriangulationT, nvertices} ptr + ts <- #{peek CTriangulationT, triangles} ptr + nts <- #{peek CTriangulationT, ntriangles} ptr + es <- #{peek CTriangulationT, edges} ptr + nes <- #{peek CTriangulationT, nedges} ptr + fes <- #{peek CTriangulationT, fixededges} ptr + nfes <- #{peek CTriangulationT, nfixededges} ptr + return CCTriangulation { __vertices' = vs + , __nvertices' = nvs + , __triangles' = ts + , __ntriangles' = nts + , __edges' = es + , __nedges' = nes + , __fixededges' = fes + , __nfixededges' = nfes } + poke ptr (CCTriangulation r1 r2 r3 r4 r5 r6 r7 r8) + = do + #{poke CTriangulationT, vertices} ptr r1 + #{poke CTriangulationT, nvertices} ptr r2 + #{poke CTriangulationT, triangles} ptr r3 + #{poke CTriangulationT, ntriangles} ptr r4 + #{poke CTriangulationT, edges} ptr r5 + #{poke CTriangulationT, nedges} ptr r6 + #{poke CTriangulationT, fixededges} ptr r7 + #{poke CTriangulationT, nfixededges} ptr r8 + +cTriangulationToTriangulation :: CTriangulation -> IO Triangulation +cTriangulationToTriangulation ctriangulation = do + let nvertices = fromIntegral $ __nvertices ctriangulation + ntriangles = fromIntegral $ __ntriangles ctriangulation + nedges = fromIntegral $ __nedges ctriangulation + vertices <- peekArray nvertices (__vertices ctriangulation) + triangles <- peekArray ntriangles (__triangles ctriangulation) + edges <- peekArray nedges (__edges ctriangulation) + vertices' <- mapM cVertexToVertex vertices + triangles' <- mapM cTriangleToTriangle triangles + edges' <- mapM cEdgeToEdge edges + return $ Triangulation { _vertices = IM.fromAscList (zip [0 .. nvertices-1] vertices') + , _triangles = triangles' + , _edges = edges' } + +cCTriangulationToConstrainedTriangulation :: CCTriangulation -> IO ConstrainedTriangulation +cCTriangulationToConstrainedTriangulation cctriangulation = do + let nvertices = fromIntegral $ __nvertices' cctriangulation + ntriangles = fromIntegral $ __ntriangles' cctriangulation + nedges = fromIntegral $ __nedges' cctriangulation + nfedges = fromIntegral $ __nfixededges' cctriangulation + vertices <- peekArray nvertices (__vertices' cctriangulation) + triangles <- peekArray ntriangles (__triangles' cctriangulation) + edges <- peekArray nedges (__edges' cctriangulation) + fedges <- peekArray nfedges (__fixededges' cctriangulation) + vertices' <- mapM cVertexToVertex vertices + triangles' <- mapM cTriangleToTriangle triangles + edges' <- mapM cEdgeToEdge edges + fedges' <- mapM cEdgeToEdge fedges + let triangulation = Triangulation { + _vertices = IM.fromAscList (zip [0 .. nvertices-1] vertices') + , _triangles = triangles' + , _edges = edges' + } + return ConstrainedTriangulation { + _triangulation = triangulation + , _fixedEdges = fedges' } + +foreign import ccall unsafe "delaunay" c_delaunay + :: Ptr CVertex -> CSize -> IO (Ptr CTriangulation) + +foreign import ccall unsafe "cdelaunay" c_cdelaunay + :: Ptr CVertex -> CSize -> Ptr CEdge -> CSize -> IO (Ptr CCTriangulation)
src/Geometry/HCDT/Types.hs view
@@ -1,28 +1,35 @@-module Geometry.HCDT.Types- where-import Data.IntMap.Strict (IntMap)-import Data.List (sort)--data Triangle = Triangle Int Int Int - deriving (Show, Read)-instance Eq Triangle where- Triangle i j k == Triangle i' j' k' = sort [i, j, k] == sort [i', j', k']--data Edge = Edge Int Int- deriving (Show, Read)-instance Eq Edge where- Edge i j == Edge i' j' = (i == i' && j == j') || (i == j' && j == i')--data Vertex = Vertex Double Double- deriving (Show, Eq)--data Triangulation = Triangulation {- _vertices :: IntMap Vertex- , _triangles :: [Triangle]- , _edges :: [Edge]-} deriving Show--data ConstrainedTriangulation = ConstrainedTriangulation {- _triangulation :: Triangulation- , _fixedEdges :: [Edge]+{-# LANGUAGE InstanceSigs #-} + +module Geometry.HCDT.Types + where +import Data.Hashable (Hashable, hashWithSalt) +import Data.IntMap.Strict (IntMap) +import Data.List (sort) + +data Triangle = Triangle Int Int Int + deriving (Show, Read) +instance Eq Triangle where + (==) :: Triangle -> Triangle -> Bool + Triangle i j k == Triangle i' j' k' = sort [i, j, k] == sort [i', j', k'] + +data Edge = Edge Int Int + deriving (Show, Read) +instance Eq Edge where + (==) :: Edge -> Edge -> Bool + Edge i j == Edge i' j' = (i == i' && j == j') || (i == j' && j == i') +instance Hashable Edge where + hashWithSalt s (Edge m1 m2) = hashWithSalt s (min m1 m2) `hashWithSalt` (max m1 m2) + +data Vertex = Vertex Double Double + deriving (Show, Eq) + +data Triangulation = Triangulation { + _vertices :: IntMap Vertex + , _triangles :: [Triangle] + , _edges :: [Edge] +} deriving Show + +data ConstrainedTriangulation = ConstrainedTriangulation { + _triangulation :: Triangulation + , _fixedEdges :: [Edge] } deriving Show
src/Geometry/HCDT/Utils.hs view
@@ -1,47 +1,49 @@-module Geometry.HCDT.Utils- ( borderEdges,- partitionEdges- )-where- -import Geometry.HCDT.Types -import Data.Sequence as DS (Seq, fromList, (><), filter, drop) -import Data.Maybe (isNothing, fromJust)-import Data.Foldable (find)-import Data.Foldable.WithIndex (ifind)--triangleEdges :: Triangle -> Seq Edge-triangleEdges (Triangle i j k) = fromList [Edge i j, Edge j k, Edge i k]---- isUnique :: Eq a => Seq a -> a -> Bool--- isUnique xs x = length (DS.filter (== x) xs) == 1--allEdges :: Triangulation -> Seq Edge-allEdges triangulation = - let triplets = map triangleEdges (_triangles triangulation) in- foldl1 (><) triplets---- borderEdges :: Triangulation -> Seq Edge--- borderEdges triangulation = DS.filter (isUnique edges) edges--- where--- edges = allEdges triangulation--isUnique :: Eq a => Seq a -> a -> Bool-isUnique xs x = isNothing y - where- (i, _) = fromJust $ ifind (\_ x' -> x' == x) xs- y = find (== x) (DS.drop i xs)---- | Exterior edges of a Delaunay triangulation.-borderEdges :: Triangulation -> Seq Edge-borderEdges triangulation = DS.filter (isUnique edges) edges- where- edges = allEdges triangulation---- | Exterior and interior edges of a Delaunay triangulation.-partitionEdges :: Triangulation -> (Seq Edge, Seq Edge)-partitionEdges triangulation = (exEdges, inEdges)- where- edges = allEdges triangulation- exEdges = DS.filter (isUnique edges) edges- inEdges = DS.filter (not . isUnique edges) edges+module Geometry.HCDT.Utils + ( borderEdges, + partitionEdges + ) +where + +import Geometry.HCDT.Types + ( Triangulation(_triangles), Edge(..), Triangle(..) ) +import Data.Sequence as DS (Seq, fromList, (><), filter, deleteAt) +import Data.Maybe (isNothing, fromJust) +import Data.Foldable (find) +import Data.Foldable.WithIndex (ifind) +import Witherable (hashNub) + +triangleEdges :: Triangle -> Seq Edge +triangleEdges (Triangle i j k) = fromList [Edge i j, Edge j k, Edge i k] + +-- isUnique :: Eq a => Seq a -> a -> Bool +-- isUnique xs x = length (DS.filter (== x) xs) == 1 + +allEdges :: Triangulation -> Seq Edge +allEdges triangulation = + let triplets = map triangleEdges (_triangles triangulation) in + foldl1 (><) triplets + +-- borderEdges :: Triangulation -> Seq Edge +-- borderEdges triangulation = DS.filter (isUnique edges) edges +-- where +-- edges = allEdges triangulation + +isUnique :: Eq a => Seq a -> a -> Bool +isUnique xs x = isNothing y + where + (i, _) = fromJust $ ifind (\_ x' -> x' == x) xs + y = find (== x) (DS.deleteAt i xs) + +-- | Exterior edges of a Delaunay triangulation. +borderEdges :: Triangulation -> Seq Edge +borderEdges triangulation = DS.filter (isUnique edges) edges + where + edges = allEdges triangulation + +-- | Exterior and interior edges of a Delaunay triangulation. +partitionEdges :: Triangulation -> (Seq Edge, Seq Edge) +partitionEdges triangulation = (exEdges, inEdges) + where + edges = allEdges triangulation + exEdges = DS.filter (isUnique edges) edges + inEdges = hashNub $ DS.filter (not . isUnique edges) edges